summaryrefslogtreecommitdiffstats
path: root/scripts/rmi-vuln-classloader.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/rmi-vuln-classloader.nse')
-rw-r--r--scripts/rmi-vuln-classloader.nse115
1 files changed, 115 insertions, 0 deletions
diff --git a/scripts/rmi-vuln-classloader.nse b/scripts/rmi-vuln-classloader.nse
new file mode 100644
index 0000000..968eea2
--- /dev/null
+++ b/scripts/rmi-vuln-classloader.nse
@@ -0,0 +1,115 @@
+local rmi = require "rmi"
+local shortport = require "shortport"
+local string = require "string"
+local stdnse = require "stdnse"
+local vulns = require "vulns"
+
+description = [[
+Tests whether Java rmiregistry allows class loading. The default
+configuration of rmiregistry allows loading classes from remote URLs,
+which can lead to remote code execution. The vendor (Oracle/Sun)
+classifies this as a design feature.
+
+
+Based on original Metasploit module by mihi.
+
+References:
+* https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/multi/misc/java_rmi_server.rb
+]];
+
+---
+-- @usage
+-- nmap --script=rmi-vuln-classloader -p 1099 <target>
+--
+-- @output
+-- PORT STATE SERVICE
+-- 1099/tcp open rmiregistry
+-- | rmi-vuln-classloader:
+-- | VULNERABLE:
+-- | RMI registry default configuration remote code execution vulnerability
+-- | State: VULNERABLE
+-- | Description:
+-- | Default configuration of RMI registry allows loading classes from remote URLs which can lead to remote code executeion.
+-- |
+-- | References:
+-- |_ https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/multi/misc/java_rmi_server.rb
+
+author = "Aleksandar Nikolic";
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html";
+categories = {
+ "intrusive",
+ "vuln"
+};
+
+
+portrule = shortport.port_or_service({1098, 1099, 1090, 8901, 8902, 8903}, {"java-rmi", "rmiregistry"})
+
+action = function (host, port)
+ local registry = rmi.Registry:new(host, port);
+ registry:_handshake();
+ local rmiArgs = rmi.Arguments:new();
+ local argsRaw = "75" .. --TC_ARRAY
+ "72" .. -- TC_CLASSDESC
+ "0018" .. -- string len
+ "5B4C6A6176612E726D692E7365727665722E4F626A49443B" .. -- class name "[Ljava.rmi.server.ObjID;"
+ "871300B8D02C647E" .. -- serial id
+ "02" .. -- FLAGS (serializable)
+ "0000" .. -- FIELD COUNT
+ "70787000000000" .. --TC_NULL TC_BLOCKEND TC_NULL
+ "77080000000000000000" .. -- TC_BLOCKDATA
+ "73" .. -- TC_OBJECT
+ "72" .. -- TC_CLASSDESC
+ "0005" .. -- string len
+ "64756D6D79" .. -- class name "dummy"
+ "A16544BA26F9C2F4" .. -- serial id
+ "02" .. -- FLAGS (serializable)
+ "0000" .. -- FIELD COUNT
+ "74" .. -- TC_STRING
+ "0010" .. -- string len
+ "66696C653A2E2F64756D6D792E6A6172" .. -- annotation "file:./dummy.jar"
+ "78" .. -- TC_ENDBLOCKDATA
+ "70" .. -- TC_NULL
+ "7701000A"; -- TC_BLOCKDATA
+ local rmi_vuln = {
+ title = "RMI registry default configuration remote code execution vulnerability",
+
+ description = [[
+Default configuration of RMI registry allows loading classes from remote URLs which can lead to remote code execution.
+]],
+ references = {
+ 'https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/multi/misc/java_rmi_server.rb',
+ },
+ exploit_results = {},
+ };
+
+ local report = vulns.Report:new(SCRIPT_NAME, host, port);
+ rmi_vuln.state = vulns.STATE.NOT_VULN;
+
+ rmiArgs:addRaw(stdnse.fromhex( argsRaw));
+
+ -- reference: java/rmi/dgc/DGCImpl_Stub.java and java/rmi/dgc/DGCImpl_Skel.java
+ -- we are calling DGC's (its objectId is 2) method with opnum 0
+ -- DCG's hashcode is f6b6898d8bf28643 hex or -669196253586618813 dec
+ local status, j_array = registry.out:writeMethodCall(registry.out, 2, "f6b6898d8bf28643", 0, rmiArgs);
+ local status, retByte = registry.out.dis:readByte();
+ if not status then
+ return false, "No return data received from server";
+ end
+
+ if 0x51 ~= retByte then
+ -- 0x51 : Returndata
+ return false, "No return data received from server";
+ end
+ -- Need to make sure we get a good chunk of data. It's going to be a java
+ -- stack trace. But if we don't get enough, I guess we can check with
+ -- whatever we get.
+ registry.out.dis:canRead(256)
+ local data = registry.out.dis.bReader.readBuffer;
+
+ if string.find(data, "RMI class loader disabled") == nil then
+ rmi_vuln.state = vulns.STATE.VULN;
+ return report:make_output(rmi_vuln);
+ end
+
+ return report:make_output(rmi_vuln);
+end;