
Goot是GO的靜態分析框架。 Goot是易於學習的,易於使用且高度可擴展的,使您可以輕鬆地在其上進行新的分析。
目前,Goot提供以下主要分析組件(以及更多分析):
intall goot bed
go get -u github.com/cokeBeer/goot
在要分析的項目中寫下代碼,例如cmd/taint/main.go
package main
import "github.com/cokeBeer/goot/pkg/example/dataflow/taint"
func main () {
// if this file is cmd/taint/main.go
// and you want to analyse package pkg
// the path should be "../../pkg"
// or "../../pkg..." for all packages under pkg
runner := taint . NewRunner ( "relative/path/to/package" )
// for this project, is "github.com/cokeBeer/goot"
runner . ModuleName = "module-name"
runner . PassThroughDstPath = "passthrough.json"
runner . TaintGraphDstPath = "taintgraph.json"
runner . Run ()
}運行代碼,您將在同一目錄中獲得一個passthrough.json ,其中包含項目中所有功能的污點通行信息
您可以看到鍵fmt.Sprintf擁有一個值對象
{
"fmt.Sprintf" : {
"Recv" : null ,
"Results" : [
[ 0 , 1 ]
],
"Params" : [
[ 0 , 1 ],
[ 1 ]
]
}
}這意味著三件事
另外,您將在同一目錄中獲得taintgraph.json
您可以看到JSON文件包含從一個調用參數到另一個調用參數的污點邊緣
{
"(*github.com/example/runnner.Runner).RunCmd#0#(*os/exec.Cmd).StdoutPipe#0" : {
"From" : " (*github.com/example/runnner.Runner).RunCmd " ,
"FromIndex" : 0 ,
"To" : " (*os/exec.Cmd).StdoutPipe " ,
"ToIndex" : 0 ,
"ToIsMethod" : false ,
"ToIsSink" : true ,
"ToIsSignature" : false ,
"ToIsStatic" : true
}
}這意味著從RunCmd的位置0 (在這種情況下,參數是接收者runner.Runner本身)到StdoutPipe的位置0 (在這種情況下,參數也為exec.Cmd iteself)
為了更好地查看污染邊緣,您可以通過設置這些參數將它們加載到Neo4J(有關更多詳細的選項,請參見Runner的選項)
func main () {
runner := taint . NewRunner ( "../../internal..." )
runner . ModuleName = "gitlab.com/gitlab-org/gitlab-workhorse"
// parameters about neo4j
runner . PersistToNeo4j = true
runner . Neo4jURI = "bolt://localhost:7687"
runner . Neo4jUsername = "neo4j"
runner . Neo4jPassword = "password"
err := runner . Run ()
if err != nil {
log . Fatal ( err )
}
}當分析結束時,您可以在NEO4J數據庫中找到節點和污點邊緣
例如,我們在gitlab.com/gitlab-org/[email protected]上進行污染分析,其中具有RCE脆弱性CVE-2021-22225
在下面使用查詢查找污點路徑
MATCH (source:Source),(sink:Sink {name:"os/exec.CommandContext"}),p=(source)-[*7]->(sink) RETURN p
我們可以得到這樣的圖:(紅色節點是接收器,棕色節點是內部功能,而綠色節點是源))
它揭示了從源到sink os/exec.CommandContext兩條污點路徑,與CVE-2021-22225相同
要將Goot用作框架,請首先創建兩個實現pkg/toolkits/scalar.FlowAnalysis接口的結構
// FlowAnalysis represents a flow analysis
type FlowAnalysis interface {
GetGraph () * graph. UnitGraph
IsForward () bool
Computations () int
FlowThrougth ( inMap * map [ any ] any , unit ssa. Instruction , outMap * map [ any ] any )
NewInitalFlow () * map [ any ] any
EntryInitalFlow () * map [ any ] any
Copy ( srcMap * map [ any ] any , dstMap * map [ any ] any )
MergeInto ( Unit ssa. Instruction , inout * map [ any ] any , in * map [ any ] any )
End ( universe [] * entry. Entry )
}和pkg/golang/switcher.Switcher接口
// Switcher represents a ssa instruction switcher
type Switcher interface {
CaseAlloc ( inst * ssa. Alloc )
CasePhi ( inst * ssa. Phi )
CaseCall ( inst * ssa. Call )
CaseBinOp ( inst * ssa. BinOp )
CaseUnOp ( inst * ssa. UnOp )
...
CaseGo ( inst * ssa. Go )
CaseDefer ( inst * ssa. Defer )
CaseSend ( inst * ssa. Send )
CaseStore ( inst * ssa. Store )
CaseMapUpdate ( inst * ssa. MapUpdate )
CaseDebugRef ( inst * ssa. DebugRef )
}不用擔心這些API。實現它們的一種簡單方法是使用pkg/toolkits/scalar.BaseFlowAnalysis中使用撰寫。
// ConstantPropagationAnalysis represents a constant propagtion analysis
type ConstantPropagationAnalysis struct {
scalar. BaseFlowAnalysis
constantPropagationSwitcher * ConstantPropagationSwitcher
}和pkg/golang/switcher.BaseSwitcher
// ConstantPropagationSwitcher represents a constant propagtion switcher
type ConstantPropagationSwitcher struct {
switcher. BaseSwitcher
constanctPropagationAnalysis * ConstantPropagationAnalysis
inMap * map [ any ] any
outMap * map [ any ] any
}這些可以使您專注於在特定分析中真正需要仔細設計的核心方法
您可以學習有關如何使用Goot作為框架的更多信息,以及如何從我為您準備的如何使用以及如何運行的微小示例中運行分析,這表明了constant propagation analysis
*map[any]any AS Flow和ssa.Instruction Instruction作為單位,因此請注意類型的斷言