
La gluée est un cadre d'analyse statique pour GO. La gluée est facile à apprendre, facile à utiliser et très extensible, vous permettant de développer facilement de nouvelles analyses en plus.
Actuellement, Goot fournit les principaux composants d'analyse suivants (et d'autres analyses sont en route):
INTALL GOOT par
go get -u github.com/cokeBeer/goot
Écrivez le code ci-dessous dans le projet à analyser, par exemple 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 ()
} Exécutez le code, et vous obtiendrez un passthrough.json dans le même répertoire, qui contient des informations Taint Passthrough de toutes les fonctions de votre projet
Vous pouvez voir Key fmt.Sprintf détient un objet de valeur
{
"fmt.Sprintf" : {
"Recv" : null ,
"Results" : [
[ 0 , 1 ]
],
"Params" : [
[ 0 , 1 ],
[ 1 ]
]
}
}Cela signifie trois choses
De plus, vous obtiendrez un taintgraph.json dans le même répertoire
Vous pouvez voir que le fichier JSON contient des bords de souillure d'un paramètre d'appel à un autre paramètre d'appel
{
"(*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
}
} Cela signifie qu'il y a un bord impassible de la position 0 de RunCmd (dans ce cas, le paramètre est le runner.Runner de récepteur.Runner lui-même) pour positionner 0 de StdoutPipe (dans ce cas, le paramètre est Ther Recevier exec.Cmd itelf, aussi) aussi)
Pour mieux afficher les bords de souillure, vous pouvez les charger sur NEO4J en définissant ces paramètres (pour des options plus détaillées, voir les options de coureur)
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 )
}
} Lorsque l'analyse est finie, vous pouvez trouver des nœuds et des bords de souillure dans votre base de données NEO4J
Par exemple, nous exécutons une analyse de souillure sur gitlab.com/gitlab-org/[email protected], qui a une vulnérabilité RCE CVE-2021-22225
Utilisation de la requête ci-dessous pour trouver des chemins de souche
MATCH (source:Source),(sink:Sink {name:"os/exec.CommandContext"}),p=(source)-[*7]->(sink) RETURN p
Nous pouvons obtenir un graphique comme celui-ci: (les nœuds rouges sont un puits, les nœuds bruns sont des fonctions intra et les nœuds verts sont source)
Qui révèle deux chemins de souillure de la source au sink os/exec.CommandContext , le même que CVE-2021-22225
Pour utiliser GOOT comme cadre, créez d'abord deux structures implémentant l'interface 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 )
} et pkg/golang/switcher.Switcher interface séparément
// 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 )
} Ne vous inquiétez pas pour ces API. Un moyen facile de les implémenter est d'utiliser Compose comme pkg/toolkits/scalar.BaseFlowAnalysis
// ConstantPropagationAnalysis represents a constant propagtion analysis
type ConstantPropagationAnalysis struct {
scalar. BaseFlowAnalysis
constantPropagationSwitcher * ConstantPropagationSwitcher
} et 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
} Ceux-ci peuvent vous faire vous concentrer sur les méthodes de base dont vous avez vraiment besoin pour concevoir soigneusement dans des analyses spécifiques
Vous pouvez apprendre plus d'informations sur la façon d'utiliser la gluée comme cadre et comment exécuter une analyse à partir d'un petit exemple que j'ai préparé pour vous dans la façon d'utiliser et comment exécuter qui démontre une constant propagation analysis
*map[any]any flux et ssa.Instruction comme unité, alors faites attention à l'affirmation de type