日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

codeql php,使用codeql 挖掘 ofcms

發布時間:2023/12/10 php 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 codeql php,使用codeql 挖掘 ofcms 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

網上關于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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。