信息收集
当拿到一个java项目之后,先进行一个简单的信息收集,看看网上有没有相关的文章来介绍这个项目什么的,并且如果说在我审计的这个版本之前还有多个版本,那么我会先收集一下该项目的历史漏洞和cve,对这些洞进行一个基础的了解,因为有的情况下后续版本对于这些漏洞打的补丁并不彻底全面,还是可以进行一个绕过处理的,然后按着前面已经爆出来的洞的思路来进行审计有时候也会意外之喜
然后了解项目的分层架构(如Controller层、Service层、DAO层)和采用的MVC框架(Spring MVC, Struts2等)
在进行项目环境搭建了之后(往往来说这步会遇到挺多问题的),我会先看pom.xml文件,主要关注项目使用的第三方库、中间件及其版本,重点排查是否有已知公开漏洞的组件,看看使用的依赖,是否是可以进行反序列攻击的相关依赖,比如cc、fastjson、shiro等
再查看web.xml、框架配置文件等,了解URL映射、过滤器(Filter)链、权限控制配置等全局设置,查看这些主要是为了后面可能存在的越权漏洞
第二步
对于我自己来说,我一般是采取黑白结合的方式来对项目进行审计
从入口点到漏洞
开始我会对整个项目进行一个简单的黑盒测试,主要目的是了解该项目总的各种功能点,看看哪些是比较可能存在漏洞的,需要重点关注的,然后对这些功能点相关的代码进行测试审计
比如说遇到一个文件上传的功能点,开始debug,跟踪输入的数据在代码中的完整传递路径,分析数据经过了哪些处理(如过滤、校验、拼接),直到最终被使用,在这个过程中对所经过的代码片段进行一个审计,看是否有使用一些危险函数,对于文件名及存储路径是否做了完整的过滤检测,那些校验的过程是否可以绕过,加密的过程是否可以进行破解的等等
这里再重点提一下反序列化相关的漏洞,比如说看到cookie里面有一个rememberme字段,那么肯定就是有shiro依赖,这时候跟一下数据流,看是否是对称加密,密钥是不是硬编码,我们是否可以自己来伪造加密过程,是否有限制了一些危险函数,我们是否可以绕过,比如禁掉了Runtime,那么我们可以通过直接使用更底层的一个类ProcessImpl来进行绕过
相关的还有很多,这里就不一一列举了
从代码层到入口点
这里就是直接开始白盒审计,要是在时间充足,代码量不多的情况下可以选择把所有代码都看一遍,这样就啥都不会漏掉了
但是这里我一般使用的是通过关键词来进行定位代码片段,寻找漏洞
对于sql注入来讲,只要是与数据库存在交互的地方,应用程序对用户的输入没有进行有效的过滤,都有可能存在SQL注入漏洞
比如说发现所用的数据库是mybatis,那么可以搜索关键词${来进行定位,因为对于mybatis来说有预编译的话,那么会使用#{}来对我们输入的数据进行包括,在这情况下就不需要考虑sql注入漏洞
1 | Select * from news where title like ‘%${title}%’ |
但是在sql语句的一些地方是不能够使用预编译语句来进行处理,比如order by、group by这些关键字,可以再配合+这个符号来进行一个定位
1 | Select * from news where title =‘java’ order by ${time} asc |
定位到了我们可以控制的sql语句后并没有结束,这时候我们需要跟着该sql语句往上走,走到入口点,检查在这个过程中有没有对我们输入的数据进行一个检测,是否有对一些sql注入常用的函数进行一个过滤,即使有的话也可以尝试一手能不能进行一个绕过
对于ssrf漏洞来说,代码中提供了从其他服务器应用获取数据的功能但没有对目标地址做过滤与限制的话就有可能存在该漏洞,简单来说就是只要是能够对外发起网络请求的地方,就有可能会出现SSRF漏洞。可以重点查找以下函数
1 | HttpClient.execute |
对于XXE漏洞,我们可以全局搜索接口关键词
原生
1 | javax.xml.parsers.DocumentBuilderFactory 关键词:DocumentBuilderFactory.newInstance(); |
第三方
1 | Dom4j 关键词:new SAXReader(); |
对于xxe漏洞的防范可以说是最简单的,就是直接禁止外部实体注入就可以了
相关的还有很多,这里就不一一列举了
最后一步
最后一步也是我认为的最重要的一部分,那就是业务逻辑漏洞,如用户注册/登录/密码找回、权限管理(越权访问)、支付流程、数据归属等,这种类型的漏洞一般来说比较难通过工具扫描发现,需要我们自己对相关的流程、代码有比较深的认识
比如说支付漏洞,之前我有遇到一道ctf题目的时候,那道题需要大量的虚拟货币来获取到一个vip权限,而获取虚拟货币的途径是签到、做任务或者是氪金,因为那道题目有提供源码给我们下载,所以我就对着源码进行审计,发现在其充值功能处并没有做的很安全,项目的充值流程是你输入所需要的金额充值完毕后,后端会自动跳转到一个路由进行充值是否成功的确认,但是该功能点的确认参数只有账号id、登陆凭证、充值数额以及一个充值是否成功的布尔值,这些参数都是通过json进行传输,我们可以对其进行任意更改,于是我们就达到了不需要真正的充值但是也能获取到大量虚拟货币的效果
在业务逻辑中,我觉得真正的重头戏是权限管理(越权访问),这种漏洞造成的危害相对来说是比较大的,我下面会比较详细地讲一讲
在java中,通常是通过filter和interceptor来进行一个权限的控制
我们首先需要做的就是查看过滤器是否是对根路径及以下所有子路径都进行了一个权限管理,如果是仅对管理员的功能点进行一个权限的限制的话,那么就可以开始审计看普通用户之间是否存在水平越权,管理员的所有功能点是否都做了权限校验,因为有的开发者会比较粗心,对管理员的大部分功能点都进行了校验,但是漏掉了少数几个,在这种情况下我们就可以直接进行垂直越权操作
接着我们审计过滤器具体逻辑,看是否存在路径穿越导致未授权访问敏感信息、执行功能的漏洞,具体是怎么操作的呢,我们重点要关注request.getRequestURL()和equest.getRequestURI()方法,因为在tomcat中URL解析是支持嵌入./、../、;xx/等特殊字符的,而这两个函数解析提取的URL内容是包含我们嵌入的特殊字符的,因此当使用不当时会存在安全问题如绕过认证
当然如果只是简单的通过黑名单来禁止这几个特殊字符的话还可以通过url编码来进行绕过
但是在上面这步具体实施之前,还需要先做一步,那就是查看一下springboot的版本,这是因为在 Spring Boot 2.5.0 开始,官方引入了一个安全性修复:
禁止对静态资源路径进行
..(目录穿越)解析。也就是说,如果你访问类似:
1
http://localhost:8080/../../application.properties
在 Spring Boot 2.4.x 及之前,可能会被 Tomcat/WebFlux 的静态资源解析器处理(具体行为依赖配置,有些情况下能“逃逸”到资源目录外)。
从 Spring Boot 2.5.0 起,这个被直接拦截,返回400或404
最后呢我们要查代码中的登陆凭证的加密逻辑和检测逻辑,看看检测逻辑是否可以绕过,加密过程是否可控等等,比如说一个项目采用的是JWT加密认证方式,那么我们可以检查其采取的加密方法,是对称加密还是非对称加密,然后再一步步跟进加密代码逻辑,看加密所采用的密钥是否是硬编码,我们是否可以自己伪造出来,从而拿到我们想要的任何登录凭证,实现越权
补充
上述内容就是我平常进行代码审计的时候所采取的思路和一些小技巧,目前的话我感觉纯手审还是太累了一点,打算试着联合codeql来简化一些审计的步骤,比如提前写好检测mysql漏洞的ql脚本,然后把项目编译成ql数据库拿去跑一下,或者是利用网上现成的fortify工具来缩短审计的时间,目前这两种方法还在理论阶段,尚未进行测试,欢迎各位师傅来和我沟通交流,指导一下我