前言

网上关于codeql的文章并不多,国内现在对codeql的研究相对比较少,可能是因为codeql暂时没有中文文档,资料也相对较少,需要比较好的英语功底,但是我认为在随着代码量越来越多,传统的自动化漏洞挖掘工具的瓶颈无法突破的情况下,codeql相当于是一种折中的办法,通过codeql的辅助,来减少漏洞挖掘人员的工作,更加关注漏洞的发现和利用过程

之所以选ofcms,是因为有p0desta师傅之前的审计经验,而且使用codeql审计cms尚属第一次,所以选用了ofcms审计

ql构造

在ql中,漏洞挖掘是根据污点追踪进行的,所以我们需要知道我们的挖掘的cms的source点在哪里,sink点在哪里,相对来说,source点比较固定,一般就是http的请求参数,请求头这一类的

但是sink比较难以确定,由于现在的web应用经常使用框架,有些文件读取,html输出其实是背后的框架在做,所以这就导致了我们的sink定义不可能是一成不变的,要对整个web应用有一个大致的了解,才能定义对应的sink

source点的ql

source点很清楚,对于一个web应用来说,http请求参数,http请求头,我们关注ofcms中对请求参数的获取方式:

ofcms使用了jfinal这个框架,而ofcms继承了jfinal的controller来获取参数,在整个ofcms中大体有三种类型来获取请求参数:

BaseController

Controller(Jfinal提供)

ApiBase

所以我们的source都是根据这几个类展开的,在观察这几个类之后很容易发现,所有的获取http参数的方法都是getXXX()这样的命名方式,所以我们可以这样定义source的ql语法:

class OfCmsSource extends MethodAccess{

OfCmsSource(){

(this.getMethod().getDeclaringType*().hasQualifiedName("com.ofsoft.cms.admin.controller", "BaseController") and

(this.getMethod().getName().substring(0, 3) = "get"))

or

(this.getMethod().getDeclaringType*().hasQualifiedName("com.jfinal.core", "Controller") and

(this.getMethod().getName().substring(0, 3) = "get"))

or

(this.getMethod().getDeclaringType*().hasQualifiedName("javax.servlet.http", "HttpServletRequest") and (this.getMethod().getName().substring(0, 3) = "get"))

or

(this.getMethod().getDeclaringType*().hasQualifiedName("com.ofsoft.cms.api", "ApiBase") and

(this.getMethod().getName().substring(0, 3) = "get"))

}

}

到这一步,我们的source就算定义完了,接下来就是定义对应的sink了

sink点的ql

相对于source的固定,sink就很不固定了,常见的web漏洞一般来说都可以作为sink,而且因为框架的不同,同一种漏洞在不同框架下的ql都是不一样的,所以我们需要略微分析一下整个web应用在做文件读取,模版渲染等操作的时候一般都用的是什么方法

模版渲染的问题

Jfinal中对模版渲染有一系列的render方法:

可以看到,所有都是render开头,所以我们对方法名的判断很简单,截取前面6个字符,判断是否为render,随便找一个项目使用render的地方,可以发现render其实是在com.jfinal.core.Controller里面定义的方法,所以现在我们唯一确定了模版渲染的方法,所以我们的sink也就呼之欲出了,也就是这些render方法的参数,所以构造ql:

class RenderMethod extends MethodAccess{

RenderMethod(){

(this.getMethod().getDeclaringType*().hasQualifiedName("com.jfinal.core", "Controller") and

this.getMethod().getName().substring(0, 6) = "render") or (this.getMethod().getDeclaringType*().hasQualifiedName("com.ofsoft.cms.core.plugin.freemarker", "TempleteUtile") and this.getMethod().hasName("process"))

}

}

在上面的ql中我添加了TempleteUtile这个类,因为这个类的process第一个参数可控的话也会造成模版的问题,所以我们可以随时去到ql中添加我们认为可能出现问题的模版渲染方法

文件类的问题

在ofcms中,文件的创建一般都是new File()这种形式创建的,所以我们的sink点应该为new File的参数为我们的sink点,所以构造ql:

class FileContruct extends ClassInstanceExpr{

FileContruct(){

this.getConstructor().getDeclaringType*().hasQualifiedName("java.io", "File")

}

}

污点追踪

codeql提供了几种数据流的查询:

local data flow

local taint data flow

global data flow

global taint data flow

local data flow基本是用在一个方法中的,比如想要知道一个方法的入参是否可以进入到某一个方法,就可以用local data flow

global data flow是用在整个项目的,也是我们做污点追踪用的最多的

简单解释一下taint和非taint有什么区别:taint的dataflow会在数据流分析的基础上加上污点分析,比如

String a = "evil";

String b = a + a;

在使用taint的dataflow中,b也会被标记为被污染的变量

构造configure

class OfCmsTaint extends TaintTracking::Configuration{

OfCmsTaint(){

this = "OfCmsTaint"

}

override predicate isSource(DataFlow::Node source){

source.asExpr() instanceof OfCmsSource

}

override predicate isSink(DataFlow::Node sink){

exists(

FileContruct rawOutput |

sink.asExpr() = rawOutput.getAnArgument()

)

}

}

当我们需要去做污点分析的时候,我们需要继承TaintTracking::Configuration这个类,来重写两个方法isSource和isSink,在这里,dataflow中的Node节点和我们直接使用的节点是不一样的,我们需要使用asExpr或者asParamter来将其转换为语法节点

这里可以看到,我们的source为我们之前定义的http参数的输入地方,sink为我们之前定义的new File的这种实例化

结果分析

codeql只能给出从source到sink的一条路径,但是这条路径中的一些过滤和条件是无法被判断的,这也就需要一部分的人工成本,让我们来运行一下我们刚刚写的ql:

import ofcms

from DataFlow::Node source, DataFlow::Node sink, OfCmsTaint config

where config.hasFlow(source, sink)

select source, sink

最后的查询结果:

可以看到找到了11个可能存在问题的地方,我们来依次看一看是否有问题:

ReprotAction

第一个在ReprotAction这个类的expReport方法中:

可以很明显看到,在获取j参数之后,对jrxmlFileName没有任何的校验,导致我们可以穿越到其他目录,但是文件后缀名必须为jrxml,而且在JasperCompileManager的compileReport函数中,对xml文档没有限制实体,导致可以造成XXE漏洞,这里很尴尬的利用点是:

需要一个文件上传

后缀名必须为jrxml

TemplateController

在TemplateController这个类的getTemplates方法中:

在这里对获取的参数没有任何的校验,导致可以跨越目录列文件并且修改文件,但是在后面的实现中,我们只能修改和查看特定的文件

假设我们在tmp目录下有着a.html和a.xml文件,我们可以跨越到tmp目录下读取并修改这两个文件

TemplateController

还有一个地方就是save函数,这个函数在p0desta师傅的博客中也挖掘出了任意文件上传漏洞:

很明显的一任意文件上传,文件名,路径,文件内容全部可控,直接getshell

剩下的一个并不能造成影响,就不多说了

后记

在render的sink定义中,如果运行可以发现很多地方的前台的一个小问题,也就是我们可以指定模版文件,ofcms使用了freemarker模版引擎,如果可以包含到我们自定义的模版文件,即可导致RCE,但是并没有发现有一个文件上传的点可以上传文件到模版目录下(除了上面的一个任意文件上传),所以不太好前台RCE

顺手测了下发现前台评论地方有存储XSS,但是和codeql无关就不多说了

整个ql:

ofcms.qll

import java

import semmle.code.java.dataflow.TaintTracking

class OfCmsSource extends MethodAccess{

OfCmsSource(){

(this.getMethod().getDeclaringType*().hasQualifiedName("com.ofsoft.cms.admin.controller", "BaseController") and

(this.getMethod().getName().substring(0, 3) = "get"))

or

(this.getMethod().getDeclaringType*().hasQualifiedName("com.jfinal.core", "Controller") and

(this.getMethod().getName().substring(0, 3) = "get"))

or

(this.getMethod().getDeclaringType*().hasQualifiedName("javax.servlet.http", "HttpServletRequest") and (this.getMethod().getName().substring(0, 3) = "get"))

or

(this.getMethod().getDeclaringType*().hasQualifiedName("com.ofsoft.cms.api", "ApiBase") and

(this.getMethod().getName().substring(0, 3) = "get"))

}

}

class RenderMethod extends MethodAccess{

RenderMethod(){

(this.getMethod().getDeclaringType*().hasQualifiedName("com.jfinal.core", "Controller") and

this.getMethod().getName().substring(0, 6) = "render") or (this.getMethod().getDeclaringType*().hasQualifiedName("com.ofsoft.cms.core.plugin.freemarker", "TempleteUtile") and this.getMethod().hasName("process"))

}

}

class SqlMethod extends MethodAccess{

SqlMethod(){

this.getMethod().getDeclaringType*().hasQualifiedName("com.jfinal.plugin.activerecord", "Db")

}

}

class FileContruct extends ClassInstanceExpr{

FileContruct(){

this.getConstructor().getDeclaringType*().hasQualifiedName("java.io", "File")

}

}

class ServletOutput extends MethodAccess{

ServletOutput(){

this.getMethod().getDeclaringType*().hasQualifiedName("java.io", "PrintWriter")

}

}

class OfCmsTaint extends TaintTracking::Configuration{

OfCmsTaint(){

this = "OfCmsTaint"

}

override predicate isSource(DataFlow::Node source){

source.asExpr() instanceof OfCmsSource

}

override predicate isSink(DataFlow::Node sink){

exists(

FileContruct rawOutput |

sink.asExpr() = rawOutput.getAnArgument()

)

}

}

test.ql

import ofcms

from DataFlow::Node source, DataFlow::Node sink, OfCmsTaint config

where config.hasFlow(source, sink)

select source, sink

不足

感觉一个很大的问题是sink的定义,因为框架的变换以及一些开发者自己的工具类,以及一些漏洞可能根本不存在,导致sink的定义有时候挖不出来漏洞

像p0desta师傅测的CSRF漏洞,暂时想不到有什么好的办法来定义sink,人工可能很好去看出来,但是不好用codeql语言定义这种漏洞

太菜了,有个点的任意文件读取写不出来ql,2333

师傅们教教我

感觉在定义的时候要尽量找共性,但是也不能找太深

参考文章:

codeql php,使用codeql 挖掘 ofcms相关推荐

  1. 安全态势感知系统java_代码分析平台CodeQL学习手记(十三) - 嘶吼 RoarTalk – 回归最本质的信息安全,互联网安全新媒体,4hou.com...

    在前面的文章中,我们为读者详细介绍了如何通过CodeQL平台的Visual Studio Code插件,即CodeQL for VS Code在本地编写和运行查询,并直接在工作区中展示查询结果,以及如 ...

  2. CodeQL笔记之基本语法(一)

    文章目录 前言 CodeQL基本语法 1.语法结构 2.类库 3.谓词 4.基本数据类型及其内建函数 前言 学习利用CodeQL进行Java代码审计.包含一些概念和例子. CodeQL基本语法 Cod ...

  3. CodeQL 代码审计平台学习笔记

    CodeQL是一款代码审计分析平台,它将Python.Go.JavaScript等语言解析生成语法树并存储到数据库中,之后通过QL语法进行代码审查与筛选.你可以按照自己的想法写代码审计检测脚本,甚至进 ...

  4. log4j2的核弹漏洞是如何被发现的?

    开篇 上一篇文章log4j2的codeql规则我看了codeql官方的规则,然后发现了一个2020年的规则,从而推出很可能是codeql挖到的(现在哪个大佬还一个个代码看idea挖洞啊,那都是廉价安服 ...

  5. BlackHat USA 2021 洞察(三):议题技术解读

    1.CHIERI ISA 安全分析 议题名:Security Analysis of CHERI ISA CHERI(Capability Hardware Enhanced RISC Instruc ...

  6. 菜鸟的源代码审计之路

    一.前言 源代码审计,顾名思义就是检查源代码中是否存在安全隐患,使用自动化工具以及人工的方式对源代码进行分析检查,发现源代码的这些缺陷引起的漏洞,并提供修复措施和建议. 在开始之前,我们先了解一下MV ...

  7. 【安全漏洞】利用CodeQL分析并挖掘Log4j漏洞

    前言 分析漏洞的本质是为了能让我们从中学习漏洞挖掘者的思路以及挖掘到新的漏洞,而CodeQL就是一款可以将我们对漏洞的理解快速转化为可实现的规则并挖掘漏洞的利器.根据网上的传言Log4j2的RCE漏洞 ...

  8. 【CodeQL从入门到精通系列】01-CodeQL简介

    什么是CodeQL?CodeQL是哪个公司维护和发行的?CodeQL相比其他代码检查工具有哪些亮点?CodeQL的发展历程是怎样的?读完本文你将收获所有答案.如还有其他相关疑问,欢迎留言讨论. 1. ...

  9. CodeQL基础语法

    基础QL语法 CodeQL的查询语法有点像SQL,如果你学过基本的SQL语句,基本模式应该不会陌生. 结构 import java // 导入使用的库from int i /* ... 变量声明... ...

最新文章

  1. web安全之点击劫持攻击(clickjack)
  2. java对象转xml jackson_五分钟Jackson入门(三) JSON数据类XML转换(附项目源码)
  3. 学习MFC首先要知道的--程序执行顺序
  4. Django2.+ path配置
  5. python剑指offer替换空格_迷人的算法-剑指offer面试题5:替换空格
  6. 对Java注解(Annotation)初步的认识
  7. Redis入门,Redis的安装
  8. @程序员,这门编程语言不输 C/C++!
  9. Hbuilder X 开发APP指南
  10. 使用VS Code插件Code Runner一键运行ANSYS命令流
  11. XMLSpy使用流程
  12. 交换机Trunk详解
  13. 支付宝支付与提现转账问题小结
  14. 还在为JS闭包烦恼? FF带你一篇文章玩转闭包,某化腾听了都说好!!
  15. 流媒体与实时计算,Netflix公司Druid应用实践
  16. mmorpg游戏服务器技能系统设计,对MMORPG职业技能设计的一些思考和方法总结
  17. 客户端开发 Windows驱动开发(1)SDK WDK DDK WDM的关系
  18. 各种软件如何双开,三开,N开,包括微信,qq等。
  19. Oracle项目管理系统之预算变更
  20. Codeforces Round #826 (Div. 3)(A~D)

热门文章

  1. JAVA实现美团电影价格抓取(附代码)
  2. idea的plugins无法使用marketplace plugins are not loaded
  3. Mysql:替换某个字段中的部分字符串——replace函数
  4. new String(123) 创建了几个对象?
  5. yarn 安装 sass
  6. format 参数说明
  7. SQLServer UPDATE INNER JOIN、DELETE INNER JOIN
  8. mysql 字符串类型 小数_在Mysql中,小数数据类型是指由字符串来表示的数字。(  )...
  9. 计算机考试打字对齐,2010年职称计算机考试:对齐方式
  10. javagei图片设置热点_【物理考点】重磅押题: 2020高考物理必考热点+命题预测+例题解析, 快快快收藏!...