Thursday 18 October 2012

Java Instrumentation Agent

We previously blogged about the power of JavaSnoop when performing application security assessments. This tool is especially useful when auditing Java clients that are obfuscated and the usual testing methods (de-compiling, editing the code and then re-compiling) are not an option. JavaSnoop allows you to intercept the Java method calls at runtime and alter the parameters and return value. In situations when you are unsure what methods you actually need to intercept JavaSnoop provides a "canary mode" which tracks a value through the work flow of the application showing all methods that were called that took this value as a parameter. Imagine a FIX trading application that is highly obfuscated and you are unsure which method places the trade in the queue. JavaSnoop canary mode can track the stock name for example, highlighting methods that need further inspection. In some situations methods you need to intercept either do not parameters, or the parameter may be in a form that you do not know about as an attacker - the stock name string may be embedded deep within an object or converted to some other form. In this edge case it is important for the security tester to see all methods called when a button is pressed which they can then inspect or pass through to JavaSnoop to do the heavy intercepting which it is so good at.

To solve this problem we have created a Java Runtime Agent that injects into the JVM of the target process and performs bytecode modification of the class to do a System.out.println each time a method is called. In a number of tests performed recently this has provided a fast and efficient way of targeting useful functionality which we then intercept using JavaSnoop to obtain the desired results.

Java agents can either be launched from the command line using the "javaagent" option:

java -javaagent:agent.jar Application.jar

Or injected at runtime using the Attach API (see previous blog post).

We have created a generic JVM injector that lists all available Java processes and allows the user to inject the agent at runtime.

Fixulate Injector

This generic injector can be reused for other example projects, it is the actual agent that does the bytecode modification and that can be specified from within the GUI.

The agent itself is made up of 3 classes, the AgentMain which contains the premain method as specified within the manifest file, an AgentForm which provides a GUI after being injected into the target process, and finally the transformation class which uses the ASM library to perform the bytecode modification to each method that is called.

After attaching (using the Injector GUI above) the AgentMain GUI is presented to the user - at this point yo are running within the target process and can kill the Injector GUI as it is no longer needed. Although the agent has been injected, the transformation is not yet active and needs to be started using the "Activate" button. We also provide a list of classes that can be edited within the GUI that are to be blacklisted by the transformation agent, this is to reduce noise and allow the tester to focus on classes that are likely to be of interest. For example we do not want to transform javax.swing classes and be flooded with notification of their method calls.

Fixulate Runtime Agent
Once activated the transformation modifies the classes and inserts a print statement that notifies the tester when a method is called. Using this information you can quickly determine what classes and methods are responsible for functionality performed as a result fo GUI actions and then use JavaSnoop to continue your testing.

Console output showing blacklisted classes and then obfuscated method calls

We will be uploading the Fixulate Injector and Runtime Agent to the resources section very soon.





Monday 24 September 2012

Java Attach & Instrumentation API for Security Testing

A high percentage of applications we test are based around the Java technology stack - this includes core Java clients launched from the dekstop, applets, Java ME for mobile devices, J2EE web applications (spring, struts, java faces), regular JSP and all the backend components that make these technologies work (application servers, containers, databases etc).

When testing web applications or client side components that use the HTTP protocol it is trivial to intercept the traffic with a web proxy and modify / replay the requests. This type of testing is second nature to many application security consultants as it makes testing the security boundary between the client and the server easy, its efficient and the tools that support this style of testing are plentiful - Burp Proxy is a great example of a suite of tools to support this type of security testing.

Although the testing methodology remains similar for the different technologies (for example we look at authentication, authorisation, session management, encryption, input validation) the tools and techniques to perform the assessment may differ. When testing a compiled Java client we often need to dip into the code to better understand what is going on under the hood. We may find that it does not use HTTP to communicate with the server which means tools like Burp, Paros, Webscarab are no longer what we need.

When developing a Java application the source code is compiled into Java bytecode which is executed by the Java virtual machine. The bytecode contains extensive metadata which makes it possible to decompile the application and retrieve the original source code. During the assessment this is where the consultant may make modifications and changes to the application to achieve a desired result. For example, modifying the code to remove encryption, changing hardcoded parameters (URLs for endpoints, IP addresses, usernames, passwords, etc) and bypassing local security checks. In a number of cases it is not always possible to completely decompile the application, if heavy obfuscation is used during compile time, anonymous inner classes or maybe within the time frame of the assessment it is not feasible to "re-engineer" the Java application.

When it is not trivial to intercept the network traffic and you can not modify the source code of the Java application what is the next play? Thankfully Aspect Security have done some great work in the area of Java instrumentation - the ability to hook into Java classes and inject "agents" during runtime. Their utility "JavaSnoop" allows you to instantly intercept method calls, class loading, inject custom Java code, modify parameters passed to methods and alter the return values - exactly what a security consultant will want to do during an assessment of a Java application.

So if you are reading this post because you searched for "hacking java applications" on google, then do not waste any more time, head over to Aspect Security, download JavaSnoop and get on with it - it should be all you need to get well under way. I am now going to quickly take a look at the two Java API's that make this possible -  Attach and Instrumentation, and also a quick tutorial on how you can develop your own agents to perform custom tasks at runtime.

The Attach API allows you to attach to a Java Virtual Machine by specifying its process ID. The API provides functions to list all current running VM's and makes it trivial to attach to these. Once attached you use Java instrumentation to "inject" a Java agent into the JVM. Think of this process as hooking or intercepting method calls. What happens at this injection point is entirely up to you (or the agents developer), the instrumentation API provides a way to alter Java classes as they load.

Sample code for listing running Java VM's:

 List<VirtualMachineDescriptor> vms = VirtualMachine.list();  
 for (VirtualMachineDescriptor vmd : vms) {  
       System.out.println("[+] " + vmd.id() + " " + vmd.displayName());  
     }  

Once the JVM of the process has been identified the following code can be used to attach:

 vm = VirtualMachine.attach(virtualmachine);  
 vm.loadAgent("./RuntimeAgent.jar");  
 System.out.println("[+] Injected agent into process " + virtualmachine);  
 vm.detach();  

RuntimeAgent.jar is the path to the instrumentation agent. This is where the actual modification of the Java clases occurs, the Attach API simply allows the injection into the JVM. It is important to note that it is also possible to perform instrumentation without attaching at runtime. Using the "javaagent' parameter when launching an application from the command line you can specific an agent to be injected and perform instrumentation:

java -javaagent:instrumentation/BootTimeAgent.jar -jar TargetApplication.jar

So now that we have two methods of injecting our agent into the JVM, lets take a look at how the agent is created and the subtle differences between a boot time agent and a run time agent.

Boot Time agents launched using the "javaagent" parameter has a "premain" method which initiates the class transformation:

 package com.fixulate.instrument;  
 import java.lang.instrument.Instrumentation;  
 public class SimpleMain {  
   public static void premain(String agentArguments, Instrumentation instrumentation) {       
     try {  
       System.out.println("Running class transformer\n");  
       instrumentation.addTransformer(new SimpleTransformer());        
     } catch (Exception e) {  
       System.out.println("Exception: " + e);  
     }  
   }       
 }  

SimpleTransformer is the class that makes the alterations to the compiled Java class. It takes and returns a byte array - this is where you modify the class based on your desired outcome and attack logic.
 package com.fixulate.instrument;  
 //Core Java imports  
 import java.lang.instrument.ClassFileTransformer;  
 import java.lang.instrument.IllegalClassFormatException;  
 import java.security.ProtectionDomain;  
 import java.text.DateFormat;  
 import java.text.SimpleDateFormat;  
 import java.util.Date;  
 // ASM bytecode modification import  
 import org.objectweb.asm.*;  
 public class SimpleTransformer implements ClassFileTransformer {  
   DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm");  
   public SimpleTransformer() {  
     super();  
   }  
   public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) throws IllegalClassFormatException {  
     if (className.startsWith("java/") || className.startsWith("sun/") || className.startsWith("javax/") || className.startsWith("apple/") || className.startsWith("com/sun") || className.startsWith("com/apple")) {  
       System.out.println("Ignoring class: " + className);  
       return bytes;  
     }   
     else {  
       try {  
         System.out.println("[+] " + dateFormat.format(new Date()) + " - Loading class: " + className);  
         byte[] result = bytes;  
           // Create a reader for the existing bytes.       
           ClassReader reader = new ClassReader(bytes);  
           // Create a writer  
           ClassWriter writer = new ClassWriter(true);  
           // Create our class adapter, pointing to the class writer  
           // and then tell the reader to notify our visitor of all   
           // bytecode instructions  
           reader.accept(new PrintStatementClassAdapter(writer, className), true);  
           // get the result from the writer.  
           result = writer.toByteArray();  
           return result;            
       } catch (Exception e) {  
         System.out.println("Exception: " + e);  
         return bytes;  
       }  
     }  
   }  
   private class PrintStatementClassAdapter extends ClassAdapter {  
     private String className;  
     PrintStatementClassAdapter(ClassVisitor visitor, String theClass) {  
       super(visitor);  
       className = theClass;    
     }  
     @Override   
     public MethodVisitor visitMethod(int arg0, String name, String descriptor, String signature, String[] exceptions) {  
       try {  
         return new PrintStatementMethodAdapter(super.visitMethod(arg0, name, descriptor, signature, exceptions), className, name, descriptor);     
       } catch (Exception e) {  
         System.out.print("Exception: " + e);  
         return null;  
       }  
     }  
   }  
     private class PrintStatementMethodAdapter extends MethodAdapter {  
       private String methodName;  
       private String methodDescriptor;  
       private String className;  
       PrintStatementMethodAdapter(MethodVisitor visitor, String theClass, String name, String descriptor) {  
         super(visitor);  
         methodName = name;  
         methodDescriptor = descriptor;  
         className = theClass;  
       }  
       @Override   
       public void visitCode() {  
         try {  
         super.visitCode();                 
         // load the system.out field into the stack  
         super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");  
         // load the constant string we want to print into the stack  
         // this string is created by the values we get from ASM  
         super.visitLdcInsn("[+] " + dateFormat.format(new Date()) + " - Method called: " + className + "." + methodName + "\t Type: " + methodDescriptor);  
         // trigger the method instruction for 'println'  
         super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");      
         } catch (Exception e) {  
         System.out.println("Exception: " + e);    
         }                 
       } //end of visitCode()  
     } //end of inner class  
 }  
The technical details of how the bytecode modification works under the hood will be covered in a future blog post, in the mean time take a look at this and this post :-)

The Boot time agent must have a Premain-Class attribute in the Manifest file as shown below:


Manifest-Version: 1.0
Premain-Class: com.fixulate.instrument.SimpleMain
Boot-Class-Path: lib/asm-2.0.jar lib/asm-attrs-2.0.jar lib/asm-commons-2.0.jar

When using a runtime agent the premain method in the SimpleMain class should be replaced with "agentmain" and the Manifest file should contain "Agent-Class", "Boot-Class-Path" and "Can-Redefine-Classes" attribute, shown below:


Agent-Class: com.fixulate.instrument.SimpleMain
Boot-Class-Path: lib/asm-2.0.jar lib/asm-attrs-2.0.jar lib/asm-commons-2.0.jar
Can-Redefine-Classes: true
We will be shortly uploading some resources and java agents we use when conducting security assessments of Java applications. Although JavaSnoop from Aspect Security provides an excellent suite of tools there will always be the corner and edge case scenarios when you need to cut your own code to get a job done.



 For further reading on the Attach API and Java Instrumentation see the following links:




Thursday 13 September 2012

Fixulate

Fixulate is a technical blog taking a look at the FIX (Financial Information Exchange) Protocol from a security perspective. While there is a wealth of information published on how to implement FIX in various configurations that enhance speed, availability, latency and throughput, the security work in this field is a little thin on the ground at best.

To get started I have compiled a list of recommended reading from what is already out there on the web, if you would like to have something of yours published here please send me an email:


The information that does detail security issues (mainly from the Fix Protocol Ltd website) generally covers the use of end-to-end encryption (PGP, SSL) to ensure confidentially and integrity of the FIX data in transit. While this is indeed good security practice we are however increasingly seeing companies dropping the use of encryption in favour of speed and ultra low latency. Now that the FIX engine endpoints are more exposed than ever it is important for organisations to know about vulnerabilities that may exist in their most critical IT systems.

Some topics that we will be covering very soon include:

  • Leased Lines - "Its leased so removing encryption must be OK"
  • Discovering FIX endpoints - Hide and seek with opportunistic hackers
  • Fuzzing FIX - Discovering vulnerabilities in a FIX engine and how to include security testing as part of your development life-cycle
  • Traditional vulnerabilities in FIX software (Buffer overflows and Format Strings)
  • "Web" vulnerabilities in FIX software (SQL injection and XSS)

If there is a specific concern or area of FIX security you would like to know more about please add a comment to let us know; we will try our best.