赞
踩
Java 代码审计工作纯靠手工一行一行代码去审视的话,着实太“费眼”,容易产生因安全人员的“疲惫”和“松懈”导致的遗漏,难以保障有效的安全检查覆盖率。个人觉得理智的做法应该是结合有效的 SAST 工具,对一些简单、基础、容易识别的代码漏洞缺陷使用工具进行自动化覆盖识别,然后对一些高风险接口或难以通过工具扫描规则识别的漏洞进行人工介入审计。
SAST 即静态应用程序安全测试(Static Application Security Testing),也称为静态分析,它通过直接查看应用程序的源代码发现各种安全漏洞,以避免企业损失。SAST 工具和扫描程序基本都是在应用程序代码完全编译之前使用,因此也可以将它们称为“白盒”工具。更多相关概念可参见:《详解安全测试工具:SAST、DAST、IAST、SCA的异同》、《一文洞悉DAST、SAST、IAST ——Web应用安全测试技术对比浅谈》。
本文来学习、总结下业界常见的 Java 语言代码审计工具的使用,同时通过检验工具的漏洞扫描结果学习下常见漏洞的代码特征,毕竟只有多看看漏洞所在的缺陷代码,才能避免实战中与漏洞“碰面”了却“互不相识”。业界已有的成熟商业化工具有 Fortify、Checkmarx(以色列),但均价格昂贵……下文将收集使用的是业界开源或免费的 Java 代码审计工具。
【附】 Fortify(破解版) 简单的使用与体验可参见我前面的文章《JAVA代码审计之WebGoat靶场SQL注入》。
前面的博文《CodeQL代码静态污点分析引擎排查漏洞模式》已经学习过 CodeQL 的基本语法和使用,下文使用开源 Java SpringBoot 靶场进一步学习 CodeQL 在 Java 白盒代码审计过程中的运用: https://github.com/WebGoat/WebGoat。
基础安装操作就不说了,下载 WebGoat 靶场源码后直接生成相应的 AST 结构数据库:
D:\Security\WebTools\CodeQL\codeql-win64\codeql.exe database create D:\Security\WebTools\CodeQL\Database\WebGoat --language="java" --source-root=D:\Code\Java\WebGoat-2023.8\
记得先本地安装配置好 Maven 环境:maven的下载与安装教程(超详细)与靶场依赖的 Java JDK21,成功创建数据库如下:
最后到 VSCode 的 CodeQL 插件中导入上述创建完成的 AST 结构数据库,CodeQL 分析 WebGoat 的靶场源码的环境就算准备完毕了:
CodeQL 规则库预置、提供了许多常见的安全漏洞的扫描规则,可以直接拿来扫描项目源码,以 Java 项目的扫描规则(https://codeql.github.com/codeql-query-help/java/)为例,其中部分开源扫描规则如下:
CodeQL 借助 CodeQL Cli 进行批量 ql 脚本自动扫描的方法(具体请参见 官方指导文档):
codeql database analyze your_codeql_db_path codeql_file_path --format=csv --output=result.csv
// 举个例子:
codeql database analyze D:\Security\CodeQL\Database\WebGoat\ D:\Security\CodeQL\codeql-main\java\ql\src\Security\CWE\CWE-089\ --format=csv --output=result.csv
扫描结果如下(相比于 VSCode 来说并不直观):
CodeQL 的其他相关资源:
深度使用 CodeQL 开展工作的情况下,一定是需要根据官方指导文档自行编写 ql 规则的,遇到问题多看官方指导文档(最快最准)。
先以前面分析过的 WebGoat 靶场 XXE 漏洞代码为例《JAVA代码审计之深入XXE漏洞挖掘与防御》,在 CodeQL 规则库项目的 codeql-main/java/ql/src/Security
路径下存放着大量官方提供的 ql 漏洞扫描规则脚本,就以官方提供的 XXE 漏洞检测脚本为例:
点击运行按钮直接运行扫描脚本,成功识别出三处 XXE 漏洞代码利用链,以第一关存在直接回显的 XXE 漏洞路径 “xxe/simple” 为例,Source 点识别如下:
污点变量的数据传递过程:
最终 sink 点则识别如下:
可以看到 CodeQL 官方开源漏洞扫描脚本准确识别了靶场的 XXE 漏洞的完整路径(其它两个漏洞路径此处不展示)。
【附】查看 CodeQL 历史执行的漏洞扫描结果可以在 CodeQL 插件的面板中 QUERY HISTORY 处找到:
前面另外的文章《JAVA代码审计之WebGoat靶场SQL注入》也分析过 WebGoat 靶场的 SQL 注入,来看看 CodeQL 的扫描脚本 CWE-089 的扫描效果,识别出来 26 个:
第一关 /SqlInjection/attack6a
识别出来的完整链路:
第二关布尔盲注识别出来的完整链路:
第三关 order by 注入识别出来的完整链路:
路径穿越漏洞很好理解,当代码接收外部传递的参数数据作为文件路径并进一步读写该文件路径所在文件的时候,如果没对用户传递的参数进行校验或过滤,攻击者可以传递携带诸如../../
路径的恶意参数来实现跨路径访问数据。
CodeQL 给了示例缺陷代码:
扫描 WebGoat 靶场,识别出存在风险的链路如下:
来看看其中一个例子:
接收了外部传递的 fullName,在没校验合法性的情况下直接进行文件的拷贝操作:
但是进一步发现了一个误报如下:
日志打印敏感数据的检测规则 CWE-532:
WebGoat 靶场的扫描结果仅有一个,可以看到打印了 password 的 base64 编码值:
密码硬编码检测规则 CWE-798:
扫描结果如下:
最后再看看密码硬编码的其中另外一个子规则与扫描结果:
总的来说,CodeQL 属于全球化的代码审计工具,拥有大量优秀的实践案例和开源规则,同时支持用户扩展检测规则。但是需要注意的是 CodeQL 的 SDK 完全开源,里面包含大部分现成的漏洞规则,也可以利用其编写自定义规则,但是其解析引擎(解析引擎用来解析我们编写的规则)并不开源,只能在官网下载二进制文件直接使用。
CodeQLpy 是一款基于 CodeQL 实现的半自动化代码审计工具,目前仅支持 java 语言。实现从源码反编译,数据库生成,脆弱性发现的全过程,可以辅助代码审计人员快速定位源码可能存在的漏洞。该工具与 CodeQL 的核心差别是支持了 jar、War 包的扫描,并默认执行了 Java 项目常见数百种漏洞的扫描 ql 规则,完整的靶场案例实践演示参见:StudyCodeQLpy/javasec_demo/README.md。
默认情况下,CodeQL 通过项目源代码编译过程生成 AST 数据库,所以 CodeQL 无法分析已编译的代码(如 Jar、War、APK 等)。但是在需求驱动的情况下,业界也出现了相关积极的探索,可参见:
下面来看看如何通过 CodeQLpy 直接根据 Jar 包自动建立 CodeQL 的数据库。
按照官方提供的指导文档进行环境准备:
本人照着修改配置如下:
使用步骤如下:
// 生成数据库初始化
python main.py -t /Users/xxx/Downloads/OAapp/ -c
// 创建数据库,注意执行完第一条命令,程序会自动提示接下来创建数据库的完整正确命令的
codeql database create out/database/OAapp --language=java --command="run.cmd" --overwrite
// 开始漏洞扫描
python main.py -d /Users/xxx/CodeQLpy/out/database/OAapp/
运行完成之后最终会返回结果文件,结果文件是 csv 文件,保存目录在 out/result/
目录之下。
本来想使用 WebGoat 的 Jar 包试试效果如何,发现该工具在创建 WebGoat jar 包所对应的 AST 数据库时识别到一些非 java 代码导致无法成功创建数据库:
更换为使用另一个存在漏洞的 jar 包(code-inspector-demo-0.2-beta.jar):
创建数据库成功:
最后进行漏洞扫描,此工具会自动根据其 /plugins/java
与 /plugins/java_ext
等子路径下的近百个 ql 脚本进行逐一漏洞扫描(有点耗时,耐心等待……):
运行到 UnsafeReflection.ql
似乎有 Bug,跑了几十分钟也没有进展,可以暂停删除该 ql 后继续执行:
最终扫描结果如下:
简单看下识别出来的反序列化漏洞,先看 source 点:
sink 点很露骨,直接反序列化用户传递的数据:
其他漏洞具体分析参见下文 code-inspector 章节。
【More】 补充说明一下,上述 CodeQLpy 通过 Jar 包直接创建的数据库,也可以直接导入 VsCode 的 CodeQL 插件进行分析的:
虽然上面 WebGoat Jar 直接建库失败,但我们可以使用前面 CodeQL+WebGoat 源码直接构造出来的 AST 数据库也进行扫描试验下:
python main.py -d D:\Security\CodeQL\Database\WebGoat\
这下一口气直接识别出来 180 个漏洞……
总体来说,CodeQLpy 帮我们自动批量跑 Java 漏洞的 ql 扫描规则,但是污点分析的链路展示并不如 CodeQL 那么完整,只有 source 和 sink,需要人工进一步跟进排查。
一款由 4ra1n 开发的开源 Java 代码审计工具:https://github.com/4ra1n/code-inspector。
此工具的输入源是 Jar/War 包,作者提供了一个存在漏洞的 demo Jar 包:
扫描结果保存在本地的 html 文件:
从 demo 扫描出来的结果中挑选 SSRF 漏洞进一步分析:
使用 jadx-gui 反编译并跟进 code-inspector-demo-0.2-beta.jar
的污点分析链路:
顺便看下 demo 被检测出来的其它几个 SSRF 漏洞样例,作者总结为 HttpUrlConnection 请求、Apache HttpClient 请求、Socket 建立新连接、OKHttp 请求四大类 SSRF 漏洞:
先看看作者在工具中的默认 RCE 漏洞检测规则分类:
ok,然后看看 demo 的检测结果,存在 6 个 RCE 漏洞:
同样反编译 jar 包,跟进代码看看:
其他几个 RCE 也写得更露骨……直接上代码吧:
public class RCEServiceImpl implements RCEService {
@Override // code.inspector.demo.service.RCEService
public String rce1(String data) {
try {
Runtime.getRuntime().exec(data);
return "ok";
} catch (Exception e) {
e.printStackTrace();
return "ok";
}
}
@Override // code.inspector.demo.service.RCEService
public String rce2(String data) {
try {
new ProcessBuilder(data).start();
return "ok";
} catch (Exception e) {
e.printStackTrace();
return "ok";
}
}
@Override // code.inspector.demo.service.RCEService
public String rce3(String data) {
try {
GroovyShell shell = new GroovyShell();
shell.evaluate(data);
return "ok";
} catch (Exception e) {
e.printStackTrace();
return "ok";
}
}
@Override // code.inspector.demo.service.RCEService
public String rce4(Obj obj) {
try {
Runtime.getRuntime().exec(obj.getCmd());
return "ok";
} catch (Exception e) {
e.printStackTrace();
return "ok";
}
}
@Override // code.inspector.demo.service.RCEService
public String rce5(String data) {
try {
new InitialContext().lookup(data);
return "ok";
} catch (Exception ex) {
ex.printStackTrace();
return "ok";
}
}
@Override // code.inspector.demo.service.RCEService
public String rce6(String data) {
try {
new InitialContext();
Runtime runtime = Runtime.getRuntime();
if (runtime.equals("obj")) {
System.out.println(runtime);
}
String finalData = "ping " + data;
RCEUtil.exec(finalData);
return "ok";
} catch (Exception ex) {
ex.printStackTrace();
return "ok";
}
}
}
同样先看下作者对这类漏洞的分类和检测规则:
demo 检测结果如下:
跟进分析下源代码:
检测规则:
demo 检测结果:
跟进分析源码:
从 demo 的扫描结果来看看似该工具的污点分析能力十分强大,但是当使用 WebGoat 靶场作为输入源,则一个漏洞都没识别出来……
原因是作者的扫描规则都是写死的,比如上面的 SSRF 漏洞的检测特征:
工具对应的检测逻辑关键代码:
但好处是作者提供了自定义扩展扫描规则的指导文档:https://github.com/4ra1n/code-inspector/blob/master/doc/NEW.md,可以结合指导文档进行二次开发,完善检测规则,此处不展开。
IDEA 作为 Java 项目主流的开发工具被广泛使用,不少组织或个人便开发了相关的 IDEA Plugins 插件来辅助检查 Java 代码中的安全风险。
这是 IDEA 默认会加载的一款安全插件,用于检测 Gradle、Maven 等引入的依赖包存在的已知 CVE 漏洞风险:
以 WebGoat 项目为例,打开 pom.xml 或者 Problems 功能选项卡,可以看到相关依赖包存在的已知 CVE 漏洞风险:
这款插件基于 Checkmarx 和美国国家漏洞库的数据源提供了项目依赖组件的风险识别,可以说是相当实用且关键的安全检查了。
当然了这里面不是完全准确的,并不是说引入的组件版本存在 CVE 漏洞就一定能被利用,还需要结合漏洞利用条件和代码环境的实际情况判断是否存在实际安全风险。举个例子:Shiro 组件默认密钥漏洞,当 Package Checks 插件基于版本号识别出当前引入了符合漏洞所在的版本的后,还需要确认用户是不是手动修改掉了默认密钥,才能最终判断系统是否受此漏洞影响。但是,从降低安全风险的角度出发,这些不安全的组件版本都应该被积极升级替换。
如官方项目 spotbugs 所述:SpotBugs 是 FindBugs 的继承者,它是一种静态分析工具,用于查找 Java 代码中的错误。
直接安装后在项目右键即可看到相应的功能按钮:
但是本人安装 IDEA Plugins 默认的 SpotBugs 1.2.7 最新版本后发现执行扫描报错(根据官方 Github 项目的 Issue 讨论情况看,该问题是升级后与 IDEA 不兼容且当前尚未被官方解决),故手动到 IDEA 插件市场下载 SpotBugs 1.2.6 版本 并手动导入 ZIP 包到 IDEA 安装后解决报错。
以扫描 WebGoat 的 SQL 注入相关源码文件夹为例,SpotBugs 成功识别出相关 sql 注入风险点:
接着以 XXE 漏洞源码文件路径为例,发现并未识别出 XXE 漏洞,只是给出了一些风险提示(此代码将对外部可变对象的引用存储到该对象的内部表示中,如果实例由不受信任的代码访问,并且对可变对象进行未经检查的更改会危及安全性或其他重要属性):
同时 SSRF 漏洞源码也同样没被检测出风险:
顺便说下该插件还支持以 html 格式导出扫描结果:
但是漏报率堪忧啊……
总的来说,SpotBugs 插件使用起来极其方便,但是对于安全漏洞的识别率有待提高(该插件完全开源,可自行二次开发优化),当前存在较多漏报,暂时只适合做辅助工具。
Momo 安全团队开源的一款工具:GitHub - momosecurity/momo-code-sec-inspector-java: IDEA静态代码安全审计及漏洞一键修复插件。
添加插件后右键选择想要扫描的项目或文件夹即可,以 SQL 注入源码文件路径为例:
官方文档描述的支持检测的漏洞类型:
扫描整个 WebGoat 项目的结果,比 SpotBugs 识别度高了不少,但是依旧很多漏洞没被识别出来(比如 XXE 漏洞):
下面则是阿里巴巴的 Java 编码规范检查插件(More:https://github.com/alibaba/p3c):
安装后右键选择“编码规范检查”:
最后简单总结一下本文实践涉及到的 Java 代码审计工具或 SATS 工具的优缺点:
更多 SATS 工具和 Java 安全编码的资源与参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。