当前位置: 首页 > news >正文

Codeql复现CVE-2018-11776学习笔记

基本使用

1、首先下载struts2漏洞版本源码:
https://codeload.github.com/apache/struts/zip/refs/tags/STRUTS_2_3_20
2、构建codeql数据库(构建失败文末有解决办法):

codeql database create ~/CodeQL/databases/struts2-2.3.20-database  --language="java"  --command="mvn clean install --file pom.xml" --source-root=~/CodeQL/struts-STRUTS_2_3_20/xwork-core

QL代码编写

source的建模

以S2-032 ( CVE-2016-3081 )、S2-033 ( CVE-2016-3687 ) 和S2-037 ( CVE-2016-4438 )为例,这三个漏洞都是与调用ognlUtil.getValue有关,比如下列代码:

String methodName = proxy.getMethod();    //<--- untrusted source, but where from?
LOG.debug("Executing action method = {}", methodName);
String timerKey = "invokeAction: " + proxy.getActionName();
try {UtilTimerStack.push(timerKey);Object methodResult;try {methodResult = ognlUtil.getValue(methodName + "()", getStack().getContext(), action); //<--- RCE

上面的代码中使用了proxy.getMethod()方法来获取不受信任的数据源,最终导致了调用ognlUtil.getValue造成了RCE,但除此之外还有各种方法,例如getActionName()和getNamespace(),所以可以编写如下QL代码,对这些不受信任的源进行建模:

class ActionProxyGetMethod extends Method {ActionProxyGetMethod() {getDeclaringType().getASupertype*().hasQualifiedName("com.opensymphony.xwork2", "ActionProxy") and(hasName("getMethod") orhasName("getNamespace") orhasName("getActionName"))}
}predicate isActionProxySource(DataFlow::Node source) {source.asExpr().(MethodAccess).getMethod() instanceof ActionProxyGetMethod
}

因为proxy是com.opensymphony.xwork2.ActionProxy类型,所以getDeclaringType().getASupertype*().hasQualifiedName(“com.opensymphony.xwork2”, “ActionProxy”),其中*号代表递归

sink的建模

S2-032、S2-033和S2-037使用了 OgnlUtil::getValue(),然而在漏洞 S2-045(CVE-2017-5638)中,使用了 TextParseUtil::translateVariables(),所以推测一下OgnlUtil::compileAndExecute() 和 OgnlUtil::compileAndExecuteMethod() 也可能存在漏洞,所以对传入compileAndExecute、和compileAndExecute的sink进行建模:

predicate isOgnlSink(DataFlow::Node sink) {exists(MethodAccess ma | ma.getMethod().hasName("compileAndExecute") or ma.getMethod().hasName("compileAndExecuteMethod") | ma.getMethod().getDeclaringType().getName().matches("OgnlUtil") and sink.asExpr() = ma.getArgument(0))
}

MethodAccess表示方法访问,即当调用xxx.compileAndExecute或者xxx.compileAndExecuteMethod
ma.getMethod().getDeclaringType().getName().matches(“OgnlUtil”)表示该方法所在的类为OgnlUtil。

进行污点追踪

之前已经定义好了sink和source,所以直接将这两个套进去,这里重写了一个isAdditionalFlowStep,用来衔接一些没匹配到的参数。

class OgnlTaintTrackingCfg extends DataFlow::Configuration {OgnlTaintTrackingCfg() {this = "mapping"}override predicate isSource(DataFlow::Node source) {isActionProxySource(source)}override predicate isSink(DataFlow::Node sink) {isOgnlSink(sink)}override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {TaintTracking::localTaintStep(node1, node2) orexists(Field f, RefType t | node1.asExpr() = f.getAnAssignedValue() and node2.asExpr() = f.getAnAccess() andnode1.asExpr().getEnclosingCallable().getDeclaringType() = t andnode2.asExpr().getEnclosingCallable().getDeclaringType() = t)}
}from OgnlTaintTrackingCfg cfg, DataFlow::Node source, DataFlow::Node sink
where cfg.hasFlow(source, sink)
select source, sink

isAdditionalFlowStep用于衔接一些没匹配上的数据流,简单来说可能有如下情况,在调用bar()时没有提前调用foo(),导致数据流并不认为foo到bar是连通的所以默认DataFlow::Configuration是不包括这个流步骤的,因为也不能确定bar()中对this.field的访问总是被污染的,但是在漏洞挖掘中,包含这种形式的调用是非常有意义的。

public void foo(String taint) {this.field = taint;
}public void bar() {String x = this.field; //x is tainted because field is assigned to tainted value in `foo`
}

对isAdditionalFlowStep进行逐步分析:
TaintTracking::localTaintStep(node1, node2) 测试node1到node2的连通性。
Field 表示字段,RefType表示除了原始类型(int、float等)、null、数组的任何类型
f.getAnAssignedValue()表示获取被赋值给该字段的表达式。例如有一个字段int a,其中a = 10+20,那么表达式为10+20
f.getAnAccess()表示该字段的访问。例如int y = x;//访问字段x
node1.asExpr() = f.getAnAssignedValue() and node2.asExpr() = f.getAnAccess()这段连起来:表示有一个获取被赋值给字段的表达式node1,node2表达式对该字段进行了访问。
node1.asExpr().getEnclosingCallable().getDeclaringType() = t and node2.asExpr().getEnclosingCallable().getDeclaringType() = t表示node1和node2都是同一个类

初版代码

import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTrackingclass ActionProxyGetMethod extends Method{ActionProxyGetMethod(){getDeclaringType().getASupertype*().hasQualifiedName("com.opensymphony.xwork2", "ActionProxy") and(hasName("getMethod") orhasName("getActionName") orhasName("getNamespace"))}
}predicate isActionProxySource(DataFlow::Node source) {source.asExpr().(MethodAccess).getMethod() instanceof ActionProxyGetMethod}predicate isOgnlSink(DataFlow::Node sink) {exists(MethodAccess ma | ma.getMethod().hasName("compileAndExecute") or ma.getMethod().hasName("compileAndExecuteMethod") | ma.getMethod().getDeclaringType().getName().matches("OgnlUtil") and sink.asExpr() = ma.getArgument(0))
}class OgnlTaintTrackingCfg extends DataFlow::Configuration {OgnlTaintTrackingCfg() {this = "mapping"}override predicate isSource(DataFlow::Node source) {isActionProxySource(source)}override predicate isSink(DataFlow::Node sink) {isOgnlSink(sink)}override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {TaintTracking::localTaintStep(node1, node2) orexists(Field f, RefType t | node1.asExpr() = f.getAnAssignedValue() and node2.asExpr() = f.getAnAccess() andnode1.asExpr().getEnclosingCallable().getDeclaringType() = t andnode2.asExpr().getEnclosingCallable().getDeclaringType() = t)}}from OgnlTaintTrackingCfg cfg, DataFlow::Node source, DataFlow::Node sinkwhere cfg.hasFlow(source, sink)select source, sink

改进isBarrier

查看数据流步骤:
在这里插入图片描述

在数据流经过几步之后,调用getActionName()返回的值流入到了对pkg.getActionConfigs()返回的对象上调用get()的一个参数中:

String chainedTo = actionName + nameSeparator + resultCode; //actionName comes from `getActionName` somewhere
ActionConfig chainedToConfig = pkg.getActionConfigs().get(chainedTo); //chainedTo contains `actionName` and ended up in the `get` method.

点进入ValueStackShadowMap::get()函数内部,代码类似如下:

public Object get(Object key) {Object value = super.get(key);  //<--- key gets tainted?if ((value == null) && key instanceof String) {value = valueStack.findValue((String) key);  //<--- findValue ended up evaluating `key`}return value;
}

通过查看数据流步骤发现,因为pkg.getActionConfigs()返回一个Map,而ValueStackShadowMap实现了Map接口,理论上,pkg.getActionConfigs()返回的值可能是ValueStackShadowMap的一个实例。因此,CodeQL DataFlow库显示了从变量chainedTo到ValueStackShadowMap类中get()实现的这一潜在流动。

但实际上,ValueStackShadowMap类属于jasperreports插件,且该类的实例只在几个地方创建,其中没有任何一个是通过pkg.getActionConfigs()返回的。所以ValueStackShadowMap::get()可能是个误报,不太可能被触发。

定义一个isBarrier,如果污点数据流入ValueStackShadowMap的get()或containsKey()方法,则不继续跟踪它。

override predicate isBarrier(DataFlow::Node node) {exists(Method m | (m.hasName("get") or m.hasName("containsKey")) andm.getDeclaringType().hasName("ValueStackShadowMap") andnode.getEnclosingCallable() = m)
}

最终代码:
https://github.com/Semmle/SecurityQueries/blob/master/semmle-security-java/queries/struts/cve_2018_11776/initial.ql

坑点

1、构建数据库失败
~/CodeQL/struts-STRUTS_2_3_20/xwork-core/target/surefire-reports

-------------------------------------------------------------------------------
Test set: TestSuite
-------------------------------------------------------------------------------
Tests run: 741, Failures: 5, Errors: 0, Skipped: 0, Time elapsed: 13.404 sec <<< FAILURE!
testSetPropertiesDate(com.opensymphony.xwork2.ognl.OgnlUtilTest)  Time elapsed: 0.011 sec  <<< FAILURE!
junit.framework.AssertionFailedError: expected:<Fri Feb 12 00:00:00 CST 1982> but was:<null>at junit.framework.Assert.fail(Assert.java:47)at junit.framework.Assert.failNotEquals(Assert.java:283)at junit.framework.Assert.assertEquals(Assert.java:64)at junit.framework.Assert.assertEquals(Assert.java:71)at com.opensymphony.xwork2.ognl.OgnlUtilTest.testSetPropertiesDate(OgnlUtilTest.java:351)testRangeValidation(com.opensymphony.xwork2.validator.DateRangeValidatorTest)  Time elapsed: 0.028 sec  <<< FAILURE!
junit.framework.AssertionFailedError: Expected date range validation error message.at junit.framework.Assert.fail(Assert.java:47)at junit.framework.Assert.assertTrue(Assert.java:20)at junit.framework.Assert.assertNotNull(Assert.java:214)at com.opensymphony.xwork2.validator.DateRangeValidatorTest.testRangeValidation(DateRangeValidatorTest.java:60)testVisitorChildConversionValidation(com.opensymphony.xwork2.validator.VisitorFieldValidatorTest)  Time elapsed: 0.015 sec  <<< FAILURE!
junit.framework.AssertionFailedError: expected:<6> but was:<4>at junit.framework.Assert.fail(Assert.java:47)at junit.framework.Assert.failNotEquals(Assert.java:283)at junit.framework.Assert.assertEquals(Assert.java:64)at junit.framework.Assert.assertEquals(Assert.java:195)at junit.framework.Assert.assertEquals(Assert.java:201)at com.opensymphony.xwork2.validator.VisitorFieldValidatorTest.testVisitorChildConversionValidation(VisitorFieldValidatorTest.java:189)testVisitorChildValidation(com.opensymphony.xwork2.validator.VisitorFieldValidatorTest)  Time elapsed: 0.015 sec  <<< FAILURE!
junit.framework.AssertionFailedError: expected:<5> but was:<3>at junit.framework.Assert.fail(Assert.java:47)at junit.framework.Assert.failNotEquals(Assert.java:283)at junit.framework.Assert.assertEquals(Assert.java:64)at junit.framework.Assert.assertEquals(Assert.java:195)at junit.framework.Assert.assertEquals(Assert.java:201)at com.opensymphony.xwork2.validator.VisitorFieldValidatorTest.testVisitorChildValidation(VisitorFieldValidatorTest.java:167)testContextIsPropagated(com.opensymphony.xwork2.validator.VisitorFieldValidatorTest)  Time elapsed: 0.008 sec  <<< FAILURE!
junit.framework.AssertionFailedError: expected:<3> but was:<2>at junit.framework.Assert.fail(Assert.java:47)at junit.framework.Assert.failNotEquals(Assert.java:283)at junit.framework.Assert.assertEquals(Assert.java:64)at junit.framework.Assert.assertEquals(Assert.java:195)at junit.framework.Assert.assertEquals(Assert.java:201)at com.opensymphony.xwork2.validator.VisitorFieldValidatorTest.testContextIsPropagated(VisitorFieldValidatorTest.java:153)

报错的都是一些测试方法,解决方法:删除测试目录,即:
~/CodeQL/struts-STRUTS_2_3_20/xwork-core/src/test

参考链接

https://www.freebuf.com/articles/web/283795.html (入门推荐)
https://securitylab.github.com/research/apache-struts-CVE-2018-11776/

http://www.lryc.cn/news/317959.html

相关文章:

  • CVE-2024-27199 JetBrains TeamCity 身份验证绕过漏洞2
  • ms office学习记录12:Excel学习记录㈥
  • 基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的条形码二维码检测系统(深度学习+UI界面+训练数据集+Python代码)
  • npm yarn 一起使用报错
  • 基于springboot实现驾校信息管理系统项目【项目源码+论文说明】计算机毕业设计
  • DXP软件界面显示“No Hard Devices”【简单的操作问题】加【软件下载】
  • 通过Spring Boot 实现页面配置生成动态接口?
  • 【数据结构与算法】:插入排序与希尔排序
  • 前端性能优化——javascript
  • Docker容器化技术(使用Docker搭建论坛)
  • C# ListView 控件使用
  • 【string一些函数用法的补充】
  • 【Go】令牌桶限流算法
  • go的slice学习
  • 软件设计师17--磁盘管理
  • 学点Java打小工——Day2Day3一点作业
  • 【话题】2024年AI辅助研发趋势,有那些应用领域
  • 蓝桥杯——数组切分
  • 【机器学习】进阶学习:详细解析Sklearn中的MinMaxScaler---原理、应用、源码与注意事项
  • 数据库是什么?数据库连接、管理与分析工具推荐
  • 【C#算法实现】可见的山峰对数量
  • Selenium 隐藏浏览器指纹特征的几种方式
  • k8s发布nacos-server,nodeport配置注意事项
  • 伪分布式Spark集群搭建
  • Android 监听卫星导航系统状态及卫星测量数据变化
  • 鸿蒙培训开发:就业市场的新热点~
  • 【C++】string的底层剖析以及模拟实现
  • Unity的PICO项目基础环境搭建笔记(调试与构建应用篇)
  • 电脑远程桌面选项变成灰色没办法勾选怎么办?
  • 2024.3.14