
Gootは、GOの静的分析フレームワークです。 Gootは学習しやすく、使いやすく、非常に拡張可能であるため、その上に新しい分析を簡単に開発できます。
現在、Gootは次の主要な分析コンポーネントを提供しています(そして、より多くの分析が進行中です):
Intall Goot by
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 ]
]
}
}これは3つのことを意味します
また、同じディレクトリに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
}
}これはexec.Cmd RunCmd StdoutPipe位置0からの汚染エッジがあることを意味します(この場合、パラメーターは0 runner.Runner自体です)。
汚染エッジをより適切に表示するには、これらのパラメーターを設定してNeo4Jにロードできます(詳細なオプションについては、ランナーのオプションを参照してください)
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]で汚染分析を実行します。
以下のクエリを使用して、汚染パスを見つけます
MATCH (source:Source),(sink:Sink {name:"os/exec.CommandContext"}),p=(source)-[*7]->(sink) RETURN p
次のようなグラフを取得できます:(赤いノードはシンク、茶色のノードは関数内で、緑色のノードはソースです)
これは、ソースからos/exec.CommandContextへの2つの汚染パスを明らかにします。これはCVE-2021-22225と同じです
Gootをフレームワークとして使用するには、最初にpkg/toolkits/scalar.FlowAnalysisを実装する2つの構造体を作成します。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のようなComposeを使用することです
// 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を使用し、 ssa.Instructionユニットとして使用するので、タイプのアサーションに注意してください