官方公众号企业安全新浪微博
FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。
FreeBuf+小程序
前言
告别脚本小子系列是本公众号新开的一个集代码审计、安全研究和漏洞复现的专题,意在帮助大家更深入的理解漏洞原理和掌握漏洞挖掘的思路和技巧。首期系列课程主要分享关于java安全相关内容,一方面主要记录自己的java安全学习历程,另一方面帮助对java安全感兴趣的小伙伴指明学习线路。我们会从最基础的java安全名词概念、环境搭建、调试方法等开始讲起。课程中提到的任何漏洞均为已公开漏洞,禁止把文中提到的方法用于攻击目的,由此产生的法律问题与本公众号无关。
本系列课程可能包含多篇文章,初期构想的课程目录如下,具体可能依据实际情况有所变动。如果你对下面的某些内容感兴趣,可以点击关注。
目录
1. Java本地调试和远程调试技巧
2. Java反编译技巧
3. Java安全基础概念之反射与ClassLoader
4. 从冰蝎来看目前webshell的实现原理
5. Java反序列化基础
6. CommonCollections利用链分析介绍上
7. CommonCollections利用链分析介绍下
8. JNDI注入原理与fastjson漏洞实践
9. Weblogic反序列化漏洞分析
10. Java命令回显技术研究
11. Java内存马技术研究
12. RASP技术研究
13. 基于CodeQL的自动化代码审计技术研究上
14. 基于CodeQL的自动化代码审计技术研究下
……
Java编写的项目一般较复杂,而且通常会引用大量第三方jar包。如果直接看代码逻辑会是一件很痛苦的事情,学会调试是开始java安全的必备技能。
本地调试
1)下断点你可以在你任意感兴趣的位置下断点。如果你不清楚程序会运行到哪个位置,可以下多个断点。支持条件断点,右键点击下断点的位置,可以弹出条件断点,条件断点一般用于在for循环语句中增加断点条件,是一种常用的断点技巧。2)断点的调试方式idea提供了多种断点调试的按键,一般情况下我们会用到的调试方法如下:F7:步入,如果当前行有方法调用,会进入方法内部,否则继续下一行执行。不能进入官方类库的方法。F8:步过,一行一行执行代码,如果当前行有方法调用,不会进行方法内部。Alt + Shift + F7:强制步入,能进去任何方法,和F7的区别是能步入官方类库的方法。Shift + F8:步出,从步入的方法内推出到方法外面,此时方法已经执行完毕。
3)运行即时表达式即时表达式是java调试中的重要工具,能帮助我们查看当前环境中变量值,查看线程信息,判断程序中的对比条件。4)查看当前断点信息对于大型的项目,很多时候我们会下很多断点,但是自己都会忘记在哪个文件还打了断点的,这时候通过断点管理的功能就可以很方便的对断点进行管理。
另外这个位置还提供了异常断点的功能,异常断点是断点调试中的重要调试技巧之一。如果我们不确定程序的运行逻辑,但是知道程序一定会暴异常,这时候就可以通过异常断点来查看程序的运行逻辑。通过搜索异常名称就可以在对应异常位置下断点了,如下图所示:5)查看当前的栈调用信息栈调用是非常重要的调试信息,通常栈调用过程就是程序运行时的逻辑顺序,对java漏洞调试非常重要。我们以log4j2漏洞来看栈调用对漏洞分析的重要性,我们知道log4j2最终会执行jndi注入,所以我们直接在java原生执行jndi远程方法调用的位置下断点,就可以看到整个漏洞的利用过程。如下图所示,漏洞分析和复现就只需要依次查看栈调用中的每一个步骤了,是不是很简单呢?6)当前环境下的变量情况这是一个很有用的信息。但是我个人不喜欢查看这个信息,因为我觉得用3)即时表达式里面的值看起来更清晰和灵活。
远程调试
多数情况下,我们进行代码审计或者漏洞复现,都是把靶机环境装在虚拟机中,然后通过远程调试的方式来对系统进行利用。要让服务器支持远程调试,必须在启动的时候增加KVM参数,如下所示。
-Xdebug -Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=0.0.0.0:5555
关于上面调试语句的解释如下:Xdebug:启动调试,需要与-Xrunjdwp一起配合实现完整的调试模式。Xrunjdwp:代表本次远程调试的参数设置。transport:指定远程调试的协议,一般使用的是dt_socket,其他还有dt_shmem等。suspend: 代表是否在调试客户端建立起来之后才运行KVM,一般选择n。这样调试程序不会影响主程序的运行。server: 代表是否支持在server模式的VM中运行调试模式。address:代表远程调试监听的端口host:port。这里的host字段支持省略的写法,但是我们不建议省略host字段。刚开始进行远程调试时如果发现客户端连不上服务端远程调试的端口,就要检查服务端端口是否监听在127.0.0.1这样的本机地址,如果监听在本机地址,是不允许远程连接的。
上面说了那么多,但是还有一个重要的问题没有解决,就是增加的KVM参数究竟写在哪?不同服务器远程调试的参数写的位置不一样,我们简单举例几种远程调试的方式。1)如果是打包独立运行的SpringBoot的jar包,那么可以直接在命令行中增加远程调试的参数。
java -jar -Xdebug -Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=0.0.0.0:5555 Test.jar
2)如果是Tomcat服务器,则可以直接修改bin/catalina.bat文件。在文件最前面增加远程调试的参数,如下所示。上面开启了服务端远程调试的端口之后,下一步就需要客户端连接远程服务器进行调试。为了保证远程调试的准确性,需要客户端拥有和服务端完全一样的源代码(这很重要,一定要完全一样),所以最好直接把服务端整个源码拷贝一份到客户端idea中进行调试。
使用idea本地打开拷贝的服务端源码,并且把所有的jar包加入library。然后新增一个configuration,选择Remote JVM Debug,填写开启的远程调试服务器ip和端口。然后点击debug按钮,可以看到下面的成功连接到远程服务器的信息,代表远程连接建立成功。后续就可以像本地调试一样对远程项目进行调试了。
调试案例
为了更加清晰直观的理解远程调试的过程,我们以远程调试泛微OA为例来演示整个远程调试的过程。首先在虚拟机中搭建泛微OA的程序,然后我在网上搜了一下关于泛微OA远程调试的文章,毫无意外的没有找到。
但是转念一想,泛微OA实际上是使用的Resin服务器来解析java代码,那么只需要找到Resin远程调试的办法就可以了。
网上关于Resin远程调试的文章很多,但是基本都不适用。大多数文章都说的直接改conf/resin.conf文件,但是泛微安装的resin根本就没有这个文件。
最后自己翻看了泛微里面resin的各个配置文件,找到了conf/resin.properties文件。在截图所在行追加下面的内容。
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=2012
重启resin服务,重启服务失效就重启机器。在idea里面配置远程连接,新增一个configuration, 选择Remote JVM Debug。配置远程泛微OA的IP和调试端口。然后就可以快乐的下断点来调试了。