Core Java

Patching Java at runtime

This article will slightly highlight how to fix issues with third party libs that

  • can’t be circumvented
  • are difficult to exclude/bypass/replaced
  • simply provide no bugfix

In such cases solving the issue remains a challengig task.

As a motivation for this scenario consider the attacks on “hash indexed” data structures, such as java.util.Hashtable and java.util.HashMap (for those who are not familar with this kinds of attacks I would recommend to view the following talk of the 28C3: Denial-of-Service attacks on web applications made easy).

To make a long story short the core issue is the usage of a non-cryptographic hash functions (where finding collisions is easy). The root cause is hidden in the java.lang.String.hashCode() function. The obvious approach would be to patch the java.lang.String class which is difficult for two reasons:

  1. it contains native code
  2. it belongs to the Java core classes which are delivered with the Java installation and thus out of our control

The first point would force us to patch with architecture and OS specific libs which we should circumvent whenever possible. The second point is true but it is a little more flexible as we will see in the following.

Ok, so let’s reconsider: Patching native is dirty and we are not eager to go this way – we have to do some work for others (in this case patch SDK libs) who are not willing to fix their code.

An attempt:
The classes java.util.Hashtable and java.util.HashMap are concerned by the hashing issue and don’t use any native code. Patching these classes is much easier as it is sufficient to provide one compiled class for all architectures and OSs.
We could use one of the provided solutions for the bug and adjust (or replace) the original classes with fixed versions. The difficulty is to patch the VM without touching the core libs – I guess users would be very disappointed if they have to change parts of their JVM installation or, even worse, our application does this automatically during installation. Further on introducing new, custom Classloaders could be difficult in some cases.

What we need is a solution to patch our single application on the fly – replace the buggy classes and don’t touch anything else. If we do this transparently other software parts don’t even recognize any changes (in best case) and remain interfacing the classes without any modifications.

This could easily be done by abusing the Java Instrumentation API. To quote the JavaDoc:

“Provides services that allow Java programming language agents to instrument programs running on the JVM.”

And that is exactly what we need!

Proof of concept
At first we need a sample application to demonstrate the concept:

public class StringChanger {
  public static void main(String[] args) {
    System.out.println(A.shout());
  }

}

public class A {
  public static String shout() {
    return "A";
  }
}

When this class is run it simply outputs: A
After applying our “patch” we would like to have the following output: Apatched

The “patched code looks like this:

public class A {
  public static String shout() {
    return "Apatched";
  }
}

Further on we need an “Agent” which governs the used classes and patches the right ones:

final public class PatchingAgent implements ClassFileTransformer {

  private static byte[] PATCHED_BYTES;
  private static final String PATH_TO_FILE = "Apatched.class";
  private static final String CLASS_TO_PATCH = "stringchanger/A";

  public PatchingAgent() throws FileNotFoundException, IOException {
    if (PATCHED_BYTES == null) {
      PATCHED_BYTES = readPatchedCode(PATH_TO_FILE);
    }
  }

  public static void premain(String agentArgument,
    final Instrumentation instrumentation) { 
    System.out.println("Initializing hot patcher...");
    PatchingAgent agent = null;

    try {
     agent = new PatchingAgent();
    } catch(Exception e) {
      System.out.println("terrible things happened....");
    }

    instrumentation.addTransformer(agent);
  }

  @Override
  public byte[] transform(final ClassLoader loader, String className,
    final Class classBeingRedefined, final ProtectionDomain protectionDomain,
    final byte[] classfileBuffer) throws IllegalClassFormatException {
    byte[] result = null;

    if (className.equals(CLASS_TO_PATCH)) {
     System.out.println("Patching... " + className);
     result = PATCHED_BYTES;
    }

    return result;
  }

  private byte[] readPatchedCode(final String path)
    throws FileNotFoundException, IOException {
    ...
  }
}

Don’t worry – I’m not going to bother you with implementaion details, since this is only PoC code, far from being nice, clever, fast and neat. Except from the fact that I’m catching Exception just because I’m too lazy at this point I’m not filtering inputs, building deep-copies (defensive programming as a buzzword) this really shouldn’t be taken as production code.

public PatchingAgent()
Initializes the agent (in this case fetching the bytes of a patched A.class file. The patched class was compiled and is stored somewhere where we can access it.

public static void premain(…)
This method is called after the JVM has initialized and prepares the agent.

public byte[] transform(…)
Whenever a class is defined (for example by ClassLoader.defineClass(…)) this function will get invoked and may transform the handled class byte[] (classfileBuffer). As can be seen we will do this for our class A in the stringchanger package. You are not limited how you are going to transform the class (as long as it remains a valid Java class ) – for example you could utilize byte code modification frameworks… – to keep things simple we assume that we replace the old byte[] with the one of the patched class (by simply buffering the complete patched A.class file into a byte[]).

That’s all for the coding part of the patcher… As a final thing we have to build a jar of the agent with a special manifest.mf file which tells the JVM how the agent can be invoked.

Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build
Premain-Class: stringchanger.PatchingAgent

After building this jar we could try out our PoC application. at first we will call it without the necessary JVM arguments to invoke the agent:

run:
A
BUILD SUCCESSFUL (total time: 0 seconds)

It behaves as expect and prints the output as defined by the unpatched class.

And now we will try it with the magic JVM arguments to invoke the agent –javaagent:StringChanger.jar:

run:
Initializing hot patcher…
Reading patched file.
Patching… stringchanger/A
Apatched
BUILD SUCCESSFUL (total time: 0 seconds)

Voilà, the code was successfully patched on-the-fly!

As we can see it is possible to hot-patch a JVM dynamically without touching the delivered code. What has to be done is the development of a patching agent and a patched class. At this moment I’m not aware of performance measuring data. Thus I’m very unsure how practical this solution is for production systems and in how far it influences application performance.

To make it clear, this is not an elegant solution – at least it is very dirty! The best way would be to patch the root cause, but as long as there is no vendor fix developers can prevent their software by hot-patching without rewriting every single line where the vulnerable classes are used.

Finally I would kindly ask for comments, improvements or simply better solutions. Many thanks to Juraj Somorovsky who joint-works on this issue with me.

Reference: Patching Java at runtime from our JCG partner Christopher Meyer at the Java security and related topics.

Christopher Meyer

Chris works as a researcher and is eagerly looking for bugs in SSL/TLS, the Java platform and various applications. In addition, he is primarily interested in secure coding and exploiting coding mistakes.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
J.A García
J.A García
10 years ago

Looks interesting. I’m going to try this to crack a very nasty java exploit that has been breaking my balls lately

Back to top button