Doop學習 part 1
doop倉庫是一個gradle專案, ./doop
其實就是一個bash去呼叫gradle命令
#!/usr/bin/env bash ARGS="" while [ "$1" != "" ]; do case "$1" in # Quote arguments with spaces. *\ * ) ARGS="${ARGS} '$1'" ;; *) ARGS="${ARGS} $1" ;; esac shift done # Export number of terminal columns for help display. if command -v 'tput' &> /dev/null then export COLUMNS=`tput cols` fi eval "./gradlew :run -Pargs=\"$ARGS\""
還提供了一個 ./doopOffline
,就是離線模式的doop。通常在每次呼叫Doop時,底層構建系統都會檢查所有依賴庫的更新版本。有時可能需要在離線模式下呼叫doop。為此目的有一個替代指令碼。
#!/bin/bash eval './gradlew :run -Pargs="'$@'" --offline'
其實就是gradle加了一個offline引數
Doop 執行流程大致可以分為幾步:
- 使用soot生成jimple檔案
- 使用
--generate-jimple
引數可以輸出jimple檔案,在output/$(uuid)/database/jimple
資料夾下 - 將jimple檔案轉換為datalog引擎的輸入事實(.facts)
- 使用souffle引擎執行選定的分析,將關係輸出為
.csv
,即分析結果
以長城杯b4bycoffee為例,解壓springboot專案,將class檔案打包為jar包
unzip b4bycoffee.jar cd BOOT-INF/classes jar -cvf classes.jar *
然後執行
./doop -a context-insensitive --information-flow spring --fact-gen-cores 16 --souffle-jobs 16 -i /tmp/BOOT-INF/lib/classes.jar --stats none
解釋一下各個引數
-a context-insensitive --information-flow spring --fact-gen-cores 16 --souffle-jobs 16 -i /tmp/BOOT-INF/lib/classes.jar --stats none
如果是第一次執行會比較慢,因為他會去 http://centauri.di.uoa.gr:8081/
拉一些jar包,等著就行了,第二次就快了。
構建完之後可見整個構建過程分為幾部分
out/uuid/database
分析輸出的結果如圖
ubuntu@ubuntu:~/doop$ cat last-analysis/MockObject.csv javax.servlet.http.HttpServletRequest::MockObject javax.servlet.http.HttpServletRequest javax.servlet.http.HttpServletResponse::MockObject javax.servlet.http.HttpServletResponse com.example.b4bycoffee.controller.indexController::MockObject com.example.b4bycoffee.controller.indexController com.example.b4bycoffee.controller.coffeeController::MockObject com.example.b4bycoffee.controller.coffeeController com.example.b4bycoffee.model.CoffeeRequest::MockObject com.example.b4bycoffee.model.CoffeeRequest
可見spring的controller會被自動進行汙點分析。
除去 --information-flow
指定為spring以外還支援一些其他的比如webapps javaee專案,這裡不再演示了。
新增自己的規則
doop的規則是基礎規則,只給你了腳手架,針對我們的實際應用我們不得不寫一些自定義規則,比如我們想要呼叫圖,那麼可以將如下規則儲存為my.dl
.decl CG(?caller:Method, ?callee:Method) CG(?caller, ?callee) :- mainAnalysis.AnyCallGraphEdge(?invocation, ?callee), Instruction_Method(?invocation, ?caller). .output CG
然後加上引數 --extra-logic my.dl
重新構建,檢視last-analysis下的CG.csv即可。
另一種自定義規則的方式
加引數 --extra-logic
仍然會很慢,會更新依賴包、重新編譯、生成facts等固定步驟,有沒有更快的方式?
在doop分析的時候可見是用gcc編譯成二進位制檔案來分析
這個二進位制檔案是souffle編譯完的可執行檔案。
可以直接執行這個可執行檔案會按照之前的規則重新輸出結果,規則檔案是 gen_xxx.dl
我們寫的my.dl被追加到最後面去執行了。
那麼我們想要改自定義的規則可以直接編輯這個 gen_xx.dl
,在最後面追加即可。然後用souffle去執行,畢竟facts事實檔案都有了。
souffle -F database/ gen_1755044251944027223.dl -j32
這裡提到bytecodedl是把doop的幾部分拆出來做了。用soot-facts-generator生成facts,編寫規則之後直接用souffle進行查詢。
而doop的好處就是內建的規則比bytecodedl多。
缺點很明顯就是慢,每次查詢都需要下依賴並重新生成facts。
官方也提到了可以生成facts之後用souffle執行自定義規則,我直接複製過來。
在檔案 temp.dl 中放入程式碼:
#!java .decl Var_DeclaringMethod(v: symbol, m: symbol) .input Var_DeclaringMethod(IO="file", filename="Var-DeclaringMethod.facts", delimiter="\t") .decl VarPointsTo(c1: symbol, h: symbol, c2: symbol, v: symbol) .input VarPointsTo(IO="file", filename="VarPointsTo.csv", delimiter="\t") .decl Temp(v: symbol, h: symbol) Temp(v, h) :- VarPointsTo(_, h, _, v), Var_DeclaringMethod(v, "<Example: void test(int)>"). .output Temp
複製 Var-DeclaringMethod.facts,使它們與輸出關係 VarPointsTo 位於同一目錄中(替換$id為您的分析 ID):
#!bash $ cp out/$id/facts/Var-DeclaringMethod.facts out/$id/database/
執行查詢並檢視其結果:
#!bash $ souffle -F out/$id/database/ temp.dl $ cat Temp.csv <Example: void test(int)>/@this <Example: void main(java.lang.String[])>/new Example/0 <Example: void test(int)>/l0#_0 <Example: void main(java.lang.String[])>/new Example/0 <Example: void test(int)>/l3#_32 <Example: void test(int)>/new Cat/1 <Example: void test(int)>/l4#_33 <Example: void test(int)>/new Cat/2 <Example: void test(int)>/$stack5 <Example: void test(int)>/new Dog/0 <Example: void test(int)>/$stack6 <Example: void test(int)>/new Cat/1 <Example: void test(int)>/$stack7 <Example: void test(int)>/new Cat/2 <Example: void test(int)>/$stack8 <Example: void test(int)>/new Cat/0 <Example: void test(int)>/l2#_26 <Example: void test(int)>/new Cat/0 <Example: void test(int)>/l2_$$A_1#_28 <Example: void test(int)>/new Dog/0 <Example: void test(int)>/l2_$$A_2#_29 <Example: void test(int)>/new Cat/0 <Example: void test(int)>/l2_$$A_2#_29 <Example: void test(int)>/new Dog/0
這種方式需要你寫import facts的規則,比較麻煩。bytecodedl就是這樣。
可能會碰到的報錯
soot生成facts事實的時候可能會報oom異常,這是因為記憶體給小了,修改build.gradle中
def factGenXmx='32G' def factGenStack='16G'
給大一點就行了。
文末
其實doop最精華的應該是他的dl規則,我再看明白一點再寫文章把。
說是最牛逼的指標分析框架,但實際學習的時候文件不全、資料太少、莫名其妙的報錯等各種原因導致學習門檻太高了。
對實際挖洞而言,具體怎麼用得再學一學再寫,個人傾向於像bytecodedl那樣拆出來改一改,加上危險函式sink規則,配合doop原有的spring、webapp的source,輸出一條準確的汙點分析過後的路徑圖是最好的。
學無止境啊,共勉吧。
- fastjson 1.2.80 漏洞分析
- Doop學習 part 1
- ByteCodeDL 學習
- CVE-2022-36923 ManageEngine OpManager getUserAPIKey Authentication Bypass
- 從濫用HTTP hop by hop請求頭看CVE-2022-1388
- 解決哥斯拉記憶體馬pagecontext的問題
- CVE 2022 22947 SpringCloud GateWay SPEL RCE Echo Response
- CVE-2021-44521 Apache Cassandra 載入UDF RCE
- dotnet 反序列化的另外幾個gadget
- CVE-2021-45456 Apache Kylin 命令注入
- 使用C#開發IIS模組後門
- 使用serverless實現動態新增水印
- XXE到域控復現(基於資源的約束委派)
- Kerberos協議之基於資源的約束委派
- Kerberos協議之約束委派
- Kerberos協議之TGS_REQ & TGS_REP
- Kerberos協議之AS_REQ & AS_REP
- Windows網路認證NTLM&Net-NTLM Hash
- fastjson 1.2.68 bypass autotype
- Ysoserial JDK7u21