Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说Java自动化审计(上篇),希望能够帮助你!!!。
本文是 i 春秋论坛作家「Wker」表哥分享的技术文章,文章旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。
CodeQL是一个免费开源的代码语义分析引擎(GitHub购买之后的开源项目),其利用QL语言对代码、执行流程等进行“查询”,以此实现对代码的安全性白盒审计,进行漏洞挖掘。
靶场
在网上找到的一个简单的靶场,spring的项目,使用的jdk1.8。
CodeQl安装
到目前为止,CodeQl已经更新到v2.7.3版本。
1、下载CodeQl;
2、放入环境变量:export PATH=/Home/CodeQL/codeql:$PATH、source /etc/profile,方便我们后期使用vscode插件;
3、下载codeql sdk;
4、下载codeql vsc插件:
只需要几步下载就可以完成CodeQl环境的部署。
生成CodeQl数据库
CodeQl并不提供语法树解析,只是提供对外查询的接口,所以我们需要给他生成database帮助其进行查询(这里检测的是我在网上clone下的靶场)。
codeql database create databasePath --language="java" --command="mvn clean install --file pom.xml" --source-root=sourcePath
databasePath:数据库路径(要保存到那)
sourcePath:源文件代码存放路径
执行完之后就会生成对应的数据库,这里需要注意mvn的路径问题。
测试语句
1、首先用vscode打开codeql sdk,在ql/java/ql/src下创建一个test.ql来编写测试脚本。
2、在codeql选项卡中打开之前创建的数据库。
3、在test.ql中编写select "hello word",右击选择执行ql,选择测试的数据库。
如果执行完毕之后出现如下界面,则说明环境搭建完毕。
到此为止就可以在test.ql下编写检测脚本了。
CodeQl语法
和sql语句很类似,可以通过AST视图查看到当前数据库中的内容:
这里简单介绍基本的使用方法:
from [datatype] var
where condition(var = something)
select var
对应下面的:
from int i
where i = 1
select i
第一行指定的变量,第二行条件判断,第三行输出内容。
类库可以通过查看AST中的内容进行确定。
名称 |
解释 |
Method |
方法类,Method method表示获取当前项目中所有的方法。 |
MethodAccess |
方法调用类,MethodAccess call表示获取当前项目当中的所有方法调用。 |
Parameter |
参数类,Parameter表示获取当前项目当中所有的参数。 |
这只是举例说明,例如下面这段代码:
import java
from Method method
select method
可以查询出当前项目的所有方法。
下面这段是存在过滤的代码:
import java
from Method method
where method.hasName("getStudent")
select method.getName(), method.getDeclaringType()
查找名称为getStudent的方法和对应的类。
并且codeql提供了一种谓词的方法帮助来分割复杂的逻辑代码:
import java
predicate isStudent(Method method) {exists(|method.hasName("getStudent"))}
from Method method
where isStudent(method)
select method.getName(), method.getDeclaringType()
详细的语法内容可以参考:
https://codeql.github.com/docs/codeql-language-guides/basic-query-for-java-code/
污点分析
污点分析可以抽象成一个三元组〈sources, sinks, sanitizers〉的形式。其中,source即污点源,代表直接引入不受信任的数据或者机密数据到系统中;sink即污点汇聚点,代表直接产生安全敏感操作(违反数据完整性)或者泄露隐私数据到外界(违反数据保密性);sanitizer即无害处理,代表通过数据加密或者移除危害操作等手段使数据传播不再对软件系统的信息安全产生危害。
污点分析就是分析程序中由污点源引入的数据是否能够不经无害处理,而直接传播到污点汇聚点,如果不能说明系统是信息流安全的;否则,说明系统产生了隐私数据泄露或危险数据操作等安全问题。
简单的理解是,source参数输入的位置,sink危险函数执行的位置,sanitizers过滤函数。
通过定义上述三点内容可以定位出一条参数传递链,当然sanitizers可以不存在。
设置source
通过override predicate isSource(DataFlow::Node src) {}设置source,这是大多常用的source入口,包括spring的也在其中。
设置sink
override predicate isSink(DataFlow::Node sink) {
exists(Method method, MethodAccess call |
method.hasName("query")
and
call.getMethod() = method and
sink.a**pr() = call.getArgument(0)
)
}
上述代码是一个谓语,查询方法名为query的方法。
当然,目前检测的是SQL注入,所以当jdbc执行query方法,则为进入了sink。
Flow数据流
from VulConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink, "source"
通过config.hasFlowPath(source, sink)设置source和sink,这样codeql就可以帮助自动检测漏洞,检索调用链。
测试
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.QueryInjection
import DataFlow::PathGraph
class VulConfig extends TaintTracking::Configuration {
VulConfig() { this = "SqlInjectionConfig" }
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(Method method, MethodAccess call |
method.hasName("query")
and
call.getMethod() = method and
sink.a**pr() = call.getArgument(0)
)
}
}
from VulConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink, "source"
上述是拷贝的他人写好的,代码也是上面所讲,运行之后就可以看到所有进入query方法的链路了。
可以点击对应的source和sink查看位置,可以看到codeql已经帮我们整理出了5条调用链。
一步步跟踪第一条调用链,可以得出完整的调用链路:
indexLogic.getStudent(username);
indexDb.getStudent(username);
String sql = "select * from students where username like '%" + username + "%'";
jdbcTemplate.query(sql, ROW_MAPPER);
至此,简单的SQL注入就能查询得到。
以上为今天分享的内容,小伙伴们看懂了吗?
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。