在完成对Java Web代码审计的环境搭建工作后,就可以开始对Java Web应用进行代码审计了。代码审计最基础的技术手段除了静态审计,还有动态调试。在Java代码审计工作中,动态调试技术是不可或缺的。相对静态审计技术来说,动态调试技术需要运行应用程序,并以设置断点的形式让程序执行到指定的位置,然后通过一步步调试来分析程序的逻辑、参数的传递、方法函数的调用等,从而寻找可能存在的漏洞。在实际的Java Web应用程序的代码审计工作中,动态调试一般可以分为两种场景:一种是在有Java Web应用程序源码的情况下,在本地使用IDEA等工具导入项目源码并运行进行动态调试,也称为本地动态调试;另一种则是在没有Java Web应用程序源码,只具备Java Web应用程序的运行环境(如Docker或应用程序jar包等)的情况下,可通过在本地构建应用程序依赖项并连接应用程序运行环境进行调试,这种调试称为远程动态调试。
本地动态调试是比较容易操作的,因为在有项目源码,以及清晰完整的项目架构的情况下,只需要使用IDEA等工具导入项目包并设置断点,即可进行本地动态调试。
图1-24所示为一个基于SSM(Spring、SpringMVC、MyBatis)框架的简单Java Web项目,在Controller层调用删除账户方法的代码行(第39行)设置一个断点。
图1-24 简单Java Web项目
在IDEA中单击“调试”按钮开启动态调试,打开浏览器可以看到如图1-25所示的功能。
图1-25 Java Web项目功能示意
单击图1-25中的任意一个“删除”按钮,打开IDEA,可以看到程序在断点处(第39行)停下,并输出参数值以及方法调用栈等信息,如图1-26所示。
图1-26 程序运行到断点处
单击Step Into按钮(见图1-27中的箭头处),进行动态调试,跟进到了Service层的AccountServiceImpl接口实现类的deleteAccountById方法中,如图1-27所示。
图1-27 AccountServiceImpl.deleteAccountById方法
若继续跟进调试,则会调用Dao层的相关方法,在此不展开。动态调试相比静态审计的好处就是可以看到程序在运行之后,客户端执行的操作及参数在后端各层级的代码中是如何传递、调用的,这非常有利于代码审计者厘清程序逻辑。
远程动态调试是对应于本地动态调试的一种调试技术,并非传统意义上的对远端应用进行调试。远程动态调试是在没有应用程序完整源码的情况下使用的调试手段。下面分别介绍常见的Docker环境下的远程动态调试和jar包文件远程动态调试。
在漏洞研究与挖掘过程中,Docker出现的频率非常高,Docker可以帮助安全研究人员快速搭建应用程序运行环境,从而大大提高漏洞研究的效率。在本节中,将基于Docker搭建一个存在CVE-2017-10271 WebLogic XMLDecoder反序列化漏洞的WebLogic应用程序环境,以介绍使用IDEA对Docker环境进行远程动态调试的方法。
Vulhub是一个基于Docker和Docker Compose的漏洞环境集合,进入对应目录并执行一条语句即可启动一个全新的漏洞环境,让漏洞复现变得更加简单,让安全研究者专注于研究漏洞的原理。Vulhub中具备WebLogic各个历史漏洞所对应的环境,可以快速搭建起存在CVE-2017-10271漏洞的历史环境。首先将Vulhub克隆到本地的kali测试主机上,如图1-28所示。克隆命令如下。
git clone https://github.com/vulhub/vulhub.git
图1-28 克隆Vulhub环境
通过cd vulhub/weblogic/CVE-2017-10271/命令进入WebLogic CVE-2017-10271漏洞环境的目录中,并通过vim docker-compose.yml命令修改docker-compose.yml配置文件,开启配置项ports中的“8453:8453”远程调试端口,如图1-29所示。IDEA需要连接该调试端口才能进行远程动态调试。
图1-29 修改docker-compose.yml配置文件开启调试端口
修改上述配置后,通过运行docker-compose up -d命令即可下载并运行相应的Docker镜像环境,如图1-30所示。
图1-30 运行相应的Docker镜像环境
通过上述过程,已经成功搭建并启动了存在CVE-2017-10271漏洞的WebLogic应用程序环境,并开启了Docker环境远程调试端口。但是,对于WebLogic而言,如果想要对其进行动态调试,还需要开启WebLogic自身的调试模式。通过docker ps查询容器ID后可以使用下面的命令进入相应的容器Shell环境中。
docker exec -it c4347c88fe68 /bin/bash
如图1-31所示,成功获得了有WebLogic CVE-2017-10271漏洞环境的Shell环境。
图1-31 成功获得Shell
获得Shell后,即可通过编辑WebLogic应用程序的配置文件来开启调试模式,通过下面的命令编辑WebLogic配置文件。
vi /root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh
并在配置文件中加入如下两行代码。
debugFlag="true" export debugFlag
图1-32所示为修改后的WebLogic配置文件内容。
图1-32 修改后的WebLogic配置文件
保存修改后的WebLogic配置文件,通过exit命令退出容器Shell环境回到宿主机中。如图1-33所示,通过docker restart cve-2017-10271-weblogic-1命令重启相应的漏洞环境,使配置生效。
图1-33 重启漏洞环境
通过上述过程即可搭建起支持远程动态调试的WebLogic CVE-2017-10271漏洞环境,接下来需要通过下面的两条命令复制并打包Docker环境中的WebLogic应用程序目录文件。
docker cp cve-2017-10271-weblogic-1:/root ./weblogic_jars tar czf weblogic_jars.tar.gz weblogic_jars/
如图1-34所示,将所需文件复制到宿主机中并打包。
图1-34 复制所需文件
将压缩包传输到安装了IDEA的调试主机上并解压,使用IDEA打开压缩包中待调试的Oracle\ Middleware\wlserver_10.3目录,如图1-35所示。
图1-35 使用IDEA打开待调试的目录
将Oracle\Middleware\目录下所有子目录中的jar包文件复制到同一个文件夹中,在Windows系统下可以直接在Oracle\Middleware\目录搜索.jar进行快速复制(如图1-36所示);或者在Linux/macOS下使用“find ./ -name *.jar -exec cp {} ./test/ \;”命令进行快速复制。
图1-36 复制jar包文件到同一个文件夹中
如图1-37所示,打开IDEA,选择File→Project Structure→Libraries选项,单击图1-37中框起来的“+”按钮,将上述存放了所有jar包的文件夹导入项目依赖中。
图1-37 导入项目依赖jar包
同时选择File→Project Structure→Project选项,将项目Java环境版本设置为源码包中自带的JDK 1.6环境,如图1-38所示。
图1-38 设置项目Java环境版本
接下来进行远程动态调试的连接配置,在IDEA中,单击Run→Edit Configurations按钮,添加一个“Remote JVM Debug”调试器。在图1-39所示的界面输入Docker漏洞环境的IP地址以及配置的调试端口,并配置项目路径。
图1-39 IDEA远程动态调试配置
保存上述配置,单击Debug按钮运行项目,若IDEA的Console窗口出现如图1-40所示的日志提示,则证明远程动态调试配置无误。
图1-40 日志提示
此时使用浏览器访问http://ip:7001,在上述配置无误的情况下则会出现如图1-41所示的WebLogic默认404页面。
图1-41 浏览器访问WebLogic页面
依据CVE-2017-10271 WebLogic XMLDecoder反序列化漏洞的触发特征,使用IDEA在/wlserver_ 10.3/server/lib/weblogic.jar!/weblogic/wsee/jaxws/WLSServletAdapter.class文件的126行设置断点,然后使用浏览器或Burp Suite请求链接http://ip:7001/wls-wsat/CoordinatorPortType。如图1-42所示,应用程序运行到上述断点位置后停止运行,并输出了当前位置的参数传递、方法调用栈等信息。
接下来就可以按照CVE-2017-10271漏洞的形成原因一步步进行调试,该漏洞后续的调试分析过程这里不再展开介绍。
图1-42 设置断点进行远程动态调试
除了Docker环境远程动态调试,在Java代码审计中还有一种场景比较常见,那就是获得了应用程序的可执行jar文件,需要对应用程序进行调试。这里以功能十分强大的Webshell管理工具——哥斯拉为例,展开对jar包文件远程动态调试的介绍。
首先下载哥斯拉工具的可执行jar文件,下载页面如图1-43所示。
图1-43 哥斯拉工具的下载页面
使用IDEA创建一个Java项目,然后在项目下新建一个文件夹,并将可执行jar文件复制到该文件夹中,如图1-44所示。该文件夹用于存放jar包文件。
图1-44 创建项目并复制jar文件
在放置了jar包的文件夹上右击,在弹出的快捷菜单中选择Add as Library选项,将该文件夹设置为项目依赖文件夹,单击OK按钮,如图1-45所示。
图1-45 将文件夹设置为项目依赖文件夹
上述设置完毕后,jar包即可展开,图1-46为展开的jar文件目录。
图1-46 展开的jar文件目录
接下来配置远程调试器,单击Add Configuration按钮,在弹出的对话框中单击“+”按钮,添加一个远程调试器“Remote JVM Debug”,如图1-47所示。
图1-47 添加一个远程调试器
配置远程调试器,使用默认设置(也就是Host设置为localhost,Port设置为5005),然后复制自动生成的启动参数(也就是“-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005”),如图1-48所示。
图1-48 配置远程调试器
将上述得到的启动参数进行拼接,得到如下启动哥斯拉jar文件的命令,使用cmd运行该命令启动哥斯拉工具,如图1-49所示。
java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005 godzilla.jar
图1-49 启动哥斯拉工具
如图1-50所示,设置断点(第38行),开启调试,即可对哥斯拉jar文件进行动态调试。
图1-50 对哥斯拉jar文件进行动态调试