1.1. 介绍(解决漏报)

上面一篇文章没有讲到如果存在漏报的情况怎么处理,出现漏报大概率是因为存在一些codeql不能识别的方法或者类等,从而导致整个数据流断掉了

我们的解决方法也很简单,只需要将数据流断掉地方的两个节点强行接在一起就行了,也就是通过isAdditionalTaintStep()方法,用一张大师傅的生动图

image

isAdditionalTaintStep方法也是TaintTracking::Configuration类中提供的方法,简而言之:如果node1可控,我们强行将node2和node1连接上,那么node2也是可控的。

image-20220407160931472

此次shiro反序列化中,就存在CodeQL不能把cookiecookie.getvalue()连起来,因此我们可以通过isAdditionalTaintStep()方法告诉污点追踪把这两个节点连起来。

1.2. 准备源码

https://github.com/apache/shiro/tree/shiro-root-1.2.4

下载即可

1.3. 创建数据库

先本地试试能不能编译jar成功,大概率是有问题的:

mvn package -DskipTests

image-20220407170638709

如果和我问题一样,可以参考:https://blog.csdn.net/qq_38376348/article/details/108962790

如果上面那个存在其他异常问题,建议给上面的jdk8全都换成jdk7的,最终我成功的版本:

image-20220407171416238


本地可以编译成功了,这个时候再用codeql来生成数据库(codeql分析必须要求本地能打包成功)

codeql database create shiro_codeql_db --language=java --command="mvn package -DskipTests" --source-root=./shiro-shiro-root-1.2.4/

1.4. 编写QL规则

导入到VS Code里面,就可以编写QL规则来分析了。

1.4.1. SDK自带source和sink

先直接用自带的QL规则

import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.UnsafeDeserializationQuery
import DataFlow::PathGraph

class TestShiro extends TaintTracking::Configuration {
    TestShiro() { this = "TestShiro" }

    override predicate isSource(DataFlow::Node source) {
        source instanceof RemoteFlowSource
    }

    override predicate isSink(DataFlow::Node sink) {
        sink instanceof UnsafeDeserializationSink
    }
}

from DataFlow::PathNode source, DataFlow::PathNode sink, TestShiro ts
where ts.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "[email protected] flows to here and unsafe deserialization.",
  source.getNode(), "User-provided value"

image-20220407191952767

可以看到codeql确实牛,但是这毕竟是别人写好的规则,我们尝试自己来写一下,以便新洞出来后第一时间写好最新的规则去批量。

1.4.2. 自写:定位source

输入点就是cookie,那我们只需要找到所有的获取cookie值的地方即可;根据分析是调用了getCookie()这个方法,所以我们筛选出来这个方法的点即可。

可以先单独写一下规则,看看能不能定位到source

import java
import semmle.code.java.dataflow.FlowSources

from MethodAccess m, DataFlow::Node source
where m.getMethod().getName() = "getCookie" and source.asExpr() = m
select m, source.asExpr()

image-20220407194441183

能定位到再稍加修改重写到isSource()函数中即可

    override predicate isSource(DataFlow::Node source) {
        exists(MethodAccess m| m.getMethod().getName() = "getCookie" and source.asExpr() = m)
    }

1.4.3. 自写:定位sink

和上面一样,还是先测试能不能给执行点定位到

import java
import semmle.code.java.dataflow.FlowSources

from MethodAccess m, DataFlow::Node sink
where m.getMethod().getName() = "readObject" and sink.asExpr() = m
select m, sink.asExpr()

image-20220407195409093

定位到了再改

    override predicate isSink(DataFlow::Node sink) {
        exists(MethodAccess m |
            m.getMethod().getName() = "readObject" and sink.asExpr() = m)
    }

1.4.4. 自写:flow(连接首尾)

就是给source和sink连接起来,最终的代码

import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.UnsafeDeserializationQuery
import DataFlow::PathGraph

class TestShiro extends TaintTracking::Configuration {
    TestShiro() { this = "TestShiro" }

    override predicate isSource(DataFlow::Node source) {
        exists(MethodAccess m| 
            m.getMethod().getName() = "getCookie" and source.asExpr() = m)
    }

    override predicate isSink(DataFlow::Node sink) {
        exists(MethodAccess m |
            m.getMethod().getName() = "readObject" and sink.asExpr() = m)
    }
}

from DataFlow::PathNode source, DataFlow::PathNode sink, TestShiro ts
where ts.hasFlowPath(source, sink)
select source, sink

头尾看着都没问题,但是并没有结果,所以肯定是流中间某个地方出问题了

1.4.5. 自写:分析解决问题

根据分析的文章,不断的修改sink的点,看看到哪一步断的

发现从getCookiereadValue中间都是断的

image-20220407200457442

猜测原因是CodeQL不认为this.cookiecookie.getValue()是连续的,所以这两个节点之间是断的,那么也就认为无法形成一条完整的流。

要解决这个问题,我们就需要用到最开始说到的isAdditionalTaintStep()方法,将两个节点强行连接起来,也就是让this.cookiecookie.getValue()连接起来

所以编写代码如下:

import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.UnsafeDeserializationQuery
import DataFlow::PathGraph

/**
 * 根据分析的连贯性,定位第一个节点和第二个节点
 */
predicate isCookie(Expr expSrc, Expr expDest) {
    exists(MethodAccess ma | 
        expSrc.getType().toString() = "Cookie" // 第一个节点类型是Cookie
        and expDest = ma
        and ma.getMethod().getName() = "getValue"   // 第二个节点的函数名
        and ma.getMethod().getDeclaringType().toString() = "Cookie" // 第二个节点函数的返回类型
        )
}

class TestShiro extends TaintTracking::Configuration {
    TestShiro() { this = "TestShiro" }

    override predicate isSource(DataFlow::Node source) {
        exists(MethodAccess m| 
            m.getMethod().getName() = "getCookie" and source.asExpr() = m)
    }

    override predicate isSink(DataFlow::Node sink) {
        exists(MethodAccess m |
            m.getMethod().getName() = "readObject" and sink.asExpr() = m)
    }

    override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
        isCookie(node1.asExpr(), node2.asExpr())
    }
}

from DataFlow::PathNode source, DataFlow::PathNode sink, TestShiro ts
where ts.hasFlowPath(source, sink)
select source, sink

结果:

image-20220407221929956

1.5. 参考

Copyright © d4m1ts 2022 all right reserved,powered by Gitbook该文章修订时间: 2022-04-08 14:23:08

results matching ""

    No results matching ""