时隔n天,重蹈覆辙
解决Lombok问题
在springboot中用过这个Lombok的类库。
主要功能就是 @Data 和我们写一堆 的getter \setter方法一致
package com.l4yn3.microserviceseclab.data;
import lombok.Data;
@Data
public class Student {
private int id;
private String username;
private int sex;
private int age;
}
但是由于lombok的实现机制,导致Code QL无法获取到Lombok自动生成的代码,所以就导致使用了Lombok的代码即使存在漏洞,也无法识别出来
# get a copy of lombok.jar
wget https://projectlombok.org/downloads/lombok.jar -O "lombok.jar"
# run "delombok" on the source files and write the generated files to a folder named "delombok"
java -jar "lombok.jar" delombok -n --onlyChanged . -d "delombok"
# remove "generated by" comments
find "delombok" -name '*.java' -exec sed '/Generated by delombok/d' -i '{}' ';'
# remove any left-over import statements
find "delombok" -name '*.java' -exec sed '/import lombok/d' -i '{}' ';'
# copy delombok'd files over the original ones
cp -r "delombok/." "./"
# remove the "delombok" folder
rm -rf "delombok"
上面的代码,实现的功能是:去掉代码里的lombok注解,并还原setter和getter方法的java代码,从而使CodeQL的Flow流能够顺利走下去, 从而检索到安全漏洞。
其实不太理解这些,如果用到直接套用
本来想了想 getter/setter能有什么漏洞,但是又一想,假设路由出的参数直接setname了,然后query查询 getname不就是洞嘛
持续工程化
因为我们的micro-service-seclab项目,是按照标准生成的微服务结构,那么我们可以使用这个ql规则去跑其他的项目,来自动化检测其它项目,从而做到自动化检测,提高安全检测效率。
创建数据库
codeql database create ~/CodeQL/databases/micro-service-seclab --language="java" --command="mvn clean install --file pom.xml -Dmaven.test.skip=true" --source-root="~/Code/micro-service-seclab/"
通过语句可以执行我们写好的QL文件,然后将结果输出到指定csv文件。
codeql database analyze /CodeQL/databases/micro-service-seclab /CodeQL/ql/java/ql/examples/demo --format=csv --output=/CodeQL/Result/micro-service-seclab.csv --rerun
本地测试
codeql database analyze /home/jycxk/Desktop/phpstudy/micro_service_seclab/micro_service_seclab_database /home/jycxk/Desktop/CCC/codeql-main/java/ql/examples/snippets/constructor_call.ql --format=csv --output=/home/jycxk/Desktop/JYCCC.csv --rerun
真的成功了我丢
但是生成的呃呃只有源,后面的数字没看懂啥意思(第一列是source的位置别的母鸡)
发现了一个贼好用的显示方法
alerts这个路径就是一个真实的调用链子!!!
但是这样大体的漏洞形式就有了,可以细化慢慢分析以后。
CodeQL进阶
终于来到了进阶了,我配?(bushi)
上面主要完成的就是一个简单的sql注入自动化审计工作
用instanceof替代复杂查询语句问题
在上面的污点分析中就用到了,instanceof是用来优化代码结构非常好的语法糖。
我们都知道,我们可以使用exists( | )这种子查询的方式定义source和sink,但是如果source/sink特别复杂(比如我们为了规则通用,可能要适配springboot,Thrift RPC, Servlet等source),如果我们把这些都在一个子查询内完成,比如condition 1 or condition 2 or condition 3,这样一直下去,太复杂了。 |
instanceof给我们提供了一种机制,我们只需要定义一个abstract class,比如这个案例当中的:
/** A data flow source of remote user input. */
abstract class RemoteFlowSource extends DataFlow::Node {
/** Gets a string that describes the type of this remote flow source. */
abstract string getSourceType();
}
然后在isSource方法里进行instanceof,判断src是 RemoteFlowSource类型就可以了
override predicate is Source(DataFlow::Node src){
src instanceof RemoteFlowSource
}
CodeQL和Java不太一样,只要我们的子类继承了这个RemoteFlowSource类,那么所有子类就会被调用,它所代表的source也会被加载。
我们在 RemoteFlowSource定义下面会看到非常多子类,就是这个道理,它们的结果都会被用and串联加载。
这里就可以理解为,RemoteFlowSource这个类的子类,差不多就是所有的输入传参那种,也就是source,所以我们的Source要instance这个类,不然不就特别复杂啥 get post 传参等等
递归问题
CodeQL里面的递归调用语法是:在谓词方法的后面跟*或者+,来表示调用0次以上和1次以上,0次会打印自己
在Java语言里,我们可以使用class嵌套class,多个内嵌class的时候,我们需要知道最外层的class是什么怎么办?
public class StudentService {
class innerOne {
public innerOne(){}
class innerTwo {
public innerTwo(){}
public String Nihao() {
return "Nihao";
}
}
public String Hi(){
return "hello";
}
}
}
我们如果要根据InnerTwo类定位到最外层的StudentService类,怎么实现?
按照非递归的写法,我们可以这样做:
import java
from Class classes
where classes.getName().toString="innerTwo"
select classes.getEnclosingType().getEnclosingType() //getEnclosingtype获取作用域
我们通过连续2次调用getEnclosingType方法是能够拿到最外层的StudentService的
但是实际我们并不清楚一共有多少层嵌套
我们在调用方法后面加*(从本身开始调用)或者+(从上一级开始调用),来解决此问题
from Class classes
where classes.getName().toString()="innerTwo"
select classes.getEnclosingType+()
非常清晰明了,其实作用域就是此时的类在哪个类的作用域里面,也就是在哪个类的内置类里
classe当前类
classes.getEnclosingType() 当前作用域
classes.getEnclosingType() 上一层
强制类型转换问题
在Codeql的规则集中,我们会看到很多类型转换的代码
在java中是强转类型,但是在CodeQL当中的强制类型转换,相当于一个filter过滤器,只把满足条件的数据留下(比如这个就会只留下RefType类型的数据,不符合的都去掉)
测试
列举出所以的参数,和参数类型
import java
from Parameter param
select param,param.getType()
然后使用强制类型转换(RefType就是去掉int 等基础类型之后的数据)
import java
from Parameter param
select param,param.getType().(RefType)
我们也可以指定过滤保留所有数值类型
import java
from Parameter param
select param, param.getType().(IntegralType)
疑惑
在想能不能直接用codeql语法对一个java文件,直接进行查询
这里尝试用了
codeql database create --language=java java-database --command="javac StudentService.java"
但是在ql查询中会爆出错误(数据库是生成功了,但是查询出错了试了几个基本的也还是不行)
想法又又又来了,试一下能否创建一个maven项目然后创建数据库这种
codeql database create X:\codeqldatabase --language="java" --command="mvn clean install --file pom.xml" --source-root=K:\javafile\Enjoy --overwrite
发现也会出现同样的问题
如果借用` micro_service_seclab的项目工程,然后在里面添加我们自己的方法试试可以嘛
(就是套个外壳)
codeql database create X:\codeqldatabase\newnew --language="java" --command="mvn clean install --file pom.xml" --source-root=X:\codeqldatabase\micro_service_seclab --overwrite
发现真的可以,这里我首先在项目(microserviceseclab中建立了自己的java文件)
然后构造ql语句进行查询的时候发现(是可以查询到的)
此时想法又又又来了
我项目中导入的jar包,能否查询到呢?
把我整懵了,类名能调用到,但是方法调不到
加油继续探索!!!
CodeQL从入门到放弃 - FreeBuf网络安全行业门户
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。