
Goot es un marco de análisis estático para GO. Goot es fácil de aprender, fácil de usar y altamente extensible, lo que le permite desarrollar fácilmente nuevos análisis además de él.
Actualmente, Goot proporciona los siguientes componentes de análisis principales (y hay más análisis en camino):
Intall Goot por
go get -u github.com/cokeBeer/goot
Escriba el código a continuación en el proyecto a analizar, por ejemplo, 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 ()
} Ejecute el código y obtendrá un passthrough.json en el mismo directorio, que contiene información de transferencia de todas las funciones en su proyecto
Puede ver la clave fmt.Sprintf contiene un objeto de valor
{
"fmt.Sprintf" : {
"Recv" : null ,
"Results" : [
[ 0 , 1 ]
],
"Params" : [
[ 0 , 1 ],
[ 1 ]
]
}
}Esto significa tres cosas
Además, obtendrá un taintgraph.json en el mismo directorio
Puede ver que el archivo JSON contiene bordes de muescas de un parámetro de llamada a otro parámetro de llamada
{
"(*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
}
} Esto significa que hay un borde StdoutPipe exec.Cmd desde la posición 0 0 RunCmd (en este caso, el parámetro es el runner.Runner receptor.
Para ver mejor los bordes de contaminación, puede cargarlos a Neo4j estableciendo estos parámetros (para opciones más detalladas, consulte Opciones de corredor)
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 )
}
} Cuando el análisis finalice, puede encontrar nodos y bordes de mancha en su base de datos NEO4J
Por ejemplo, ejecutamos un análisis Taint en gitlab.com/gitlab-org/[email protected], que tiene una vulnerabilidad RCE CVE-2021-22225
Uso de la consulta a continuación para encontrar rutas de mancha
MATCH (source:Source),(sink:Sink {name:"os/exec.CommandContext"}),p=(source)-[*7]->(sink) RETURN p
Podemos obtener un gráfico como este: (los nodos rojos son fregaderos, los nodos marrones son intra funciones y los nodos verdes son fuente)
Que revela dos rutas de mancha desde la fuente hasta la hundimiento de os/exec.CommandContext , lo mismo que CVE-2021-22225
Para usar Goot como marco, primero cree dos estructuras que implementan pkg/toolkits/scalar.FlowAnalysis Interface
// 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 )
} y pkg/golang/switcher.Switcher Interface por separado
// 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 )
} No se preocupe por estas API. Una manera fácil de implementarlos es usar componer como pkg/toolkits/scalar.BaseFlowAnalysis
// ConstantPropagationAnalysis represents a constant propagtion analysis
type ConstantPropagationAnalysis struct {
scalar. BaseFlowAnalysis
constantPropagationSwitcher * ConstantPropagationSwitcher
} y 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
} Estos pueden hacer que se concentre en los métodos centrales que realmente necesita diseñar cuidadosamente en análisis específicos.
Puede obtener más información sobre cómo usar Goot como marco y cómo ejecutar un análisis de un pequeño ejemplo que preparé para usted en cómo usar y cómo ejecutar, lo que demuestra un constant propagation analysis
*map[any]any de flujo y ssa.Instruction como unidad, así que tenga cuidado de la afirmación de tipo