
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作为单位,因此请注意类型的断言