Skip to content

Commit

Permalink
Better naming for some variables, switched to my own concurrent backe…
Browse files Browse the repository at this point in the history
…nd for storing edges rather than gsync.MapOf (it's a sorted slice using binary lookup)
  • Loading branch information
lkarlslund committed Oct 30, 2022
1 parent c475bc9 commit a7cb3ba
Show file tree
Hide file tree
Showing 17 changed files with 665 additions and 289 deletions.
15 changes: 6 additions & 9 deletions modules/analyze/export-graph.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package analyze

import (
"bytes"
"fmt"
"os"
"sort"
Expand All @@ -23,11 +22,11 @@ func ExportGraphViz(pg engine.Graph, filename string) error {
case engine.ObjectTypeComputer:
formatting = ""
}
fmt.Fprintf(df, " \"%v\" [label=\"%v\";%v];\n", object.GUID(), object.OneAttr(activedirectory.Name), formatting)
fmt.Fprintf(df, " \"%v\" [label=\"%v\";%v];\n", object.ID(), object.OneAttr(activedirectory.Name), formatting)
}
fmt.Fprintln(df, "")
for _, connection := range pg.Connections {
fmt.Fprintf(df, " \"%v\" -> \"%v\" [label=\"%v\"];\n", connection.Source.GUID(), connection.Target.GUID(), connection.JoinedString())
fmt.Fprintf(df, " \"%v\" -> \"%v\" [label=\"%v\"];\n", connection.Source.ID(), connection.Target.ID(), connection.JoinedString())
}
fmt.Fprintln(df, "}")

Expand Down Expand Up @@ -72,16 +71,14 @@ func GenerateCytoscapeJS(pg engine.Graph, alldetails bool) (CytoGraph, error) {

// Sort the nodes to get consistency
sort.Slice(pg.Nodes, func(i, j int) bool {
return bytes.Compare(pg.Nodes[i].Object.GUID().Bytes(), pg.Nodes[j].Object.GUID().Bytes()) == -1
return pg.Nodes[i].Object.ID() < pg.Nodes[j].Object.ID()
})

// Sort the connections to get consistency
sort.Slice(pg.Connections, func(i, j int) bool {
return bytes.Compare(
pg.Connections[i].Source.GUID().Bytes(),
pg.Connections[i].Source.GUID().Bytes()) == -1 ||
bytes.Compare(pg.Connections[i].Target.GUID().Bytes(),
pg.Connections[i].Target.GUID().Bytes()) == -1
return pg.Connections[i].Source.ID() < pg.Connections[j].Source.ID() ||
(pg.Connections[i].Source.ID() == pg.Connections[j].Source.ID() &&
pg.Connections[i].Target.ID() < pg.Connections[j].Target.ID())
})

g.Elements = make(CytoElements, len(pg.Nodes)+len(pg.Connections))
Expand Down
16 changes: 8 additions & 8 deletions modules/analyze/webservicefuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,17 +275,17 @@ func analysisfuncs(ws *webservice) {
if strings.HasPrefix(potentialfilter, "pwn_") {
prefix := potentialfilter[4 : len(potentialfilter)-2]
suffix := potentialfilter[len(potentialfilter)-2:]
method := engine.E(prefix)
if method == engine.NonExistingEdgeType {
edge := engine.E(prefix)
if edge == engine.NonExistingEdgeType {
continue
}
switch suffix {
case "_f":
edges_f = edges_f.Set(method)
edges_f = edges_f.Set(edge)
case "_m":
egdes_m = egdes_m.Set(method)
egdes_m = egdes_m.Set(edge)
case "_l":
edges_l = edges_l.Set(method)
edges_l = edges_l.Set(edge)
}
} else if strings.HasPrefix(potentialfilter, "type_") {
prefix := potentialfilter[5 : len(potentialfilter)-2]
Expand Down Expand Up @@ -861,13 +861,13 @@ func analysisfuncs(ws *webservice) {
result.Statistics[engine.ObjectType(objecttype).String()] += count
}

var pwnlinks int
var edgeCount int
ws.Objs.Iterate(func(object *engine.Object) bool {
pwnlinks += object.Edges(engine.Out).Len()
edgeCount += object.Edges(engine.Out).Len()
return true
})
result.Statistics["Total"] = ws.Objs.Len()
result.Statistics["PwnConnections"] = pwnlinks
result.Statistics["PwnConnections"] = edgeCount

data, _ := json.MarshalIndent(result, "", " ")
w.Write(data)
Expand Down
2 changes: 1 addition & 1 deletion modules/engine/analyzeobjects.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func AnalyzeObjects(opts AnalyzeObjectsOptions) (pg Graph) {
// Iterate over ever outgoing pwn
// This is not efficient, but we sort the pwnlist first
object.Edges(ec).Range(func(target *Object, eb EdgeBitmap) bool {
// If this is not a chosen method, skip it
// If this is not a chosen edge, skip it
detectededges := eb.Intersect(detectedges)

if detectededges.IsBlank() {
Expand Down
2 changes: 1 addition & 1 deletion modules/engine/analyzepaths.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func AnalyzePaths(start, end *Object, obs *Objects, lookforedges EdgeBitmap, min

source.Edges(Out).Range(func(target *Object, edges EdgeBitmap) bool {
if _, found := visited[target]; !found {
// If this is not a chosen method, skip it
// If this is not a chosen edge, skip it
detectededges := edges.Intersect(lookforedges)

if detectededges.IsBlank() {
Expand Down
68 changes: 45 additions & 23 deletions modules/engine/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ type attributeinfo struct {
onget AttributeGetFunc
name string
tags []string
atype AttributeType
mergeSuccesses atomic.Uint64 // number of successfull merges where this attribute was the deciding factor
single bool // If true, this attribute can not have multiple values
unique bool // Doing a Find on this attribute will return multiple results
merge bool // If true, objects can be merged on this attribute
hidden bool // If true this does not show up in the list of attributes
atype AttributeType
single bool // If true, this attribute can not have multiple values
unique bool // Doing a Find on this attribute will return multiple results
merge bool // If true, objects can be merged on this attribute
hidden bool // If true this does not show up in the list of attributes
}

type AttributeType uint8
Expand Down Expand Up @@ -62,8 +62,8 @@ var (
LDAPDisplayName = NewAttribute("lDAPDisplayName").Single()
Description = NewAttribute("description")
SAMAccountName = NewAttribute("sAMAccountName").Single()
ObjectSid = NewAttribute("objectSid").Single() // Strange yes, but in the final results there are multiple objects with the same SID
ObjectGUID = NewAttribute("objectGUID").Single().Unique()
ObjectSid = NewAttribute("objectSid").Single() // Single, but not unique! Strange yes, but in the final results there are multiple objects with the same SID
ObjectGUID = NewAttribute("objectGUID").Single().Merge().Unique()
NTSecurityDescriptor = NewAttribute("nTSecurityDescriptor").Single()
SchemaIDGUID = NewAttribute("schemaIDGUID")
RightsGUID = NewAttribute("rightsGUID")
Expand Down Expand Up @@ -160,38 +160,58 @@ func (a Attribute) String() string {
if a == NonExistingAttribute {
return "N/A"
}
return attributenums[a].name
attributemutex.RLock()
result := attributenums[a].name
attributemutex.RUnlock()
return result
}

func (a Attribute) Type(t AttributeType) Attribute {
attributemutex.Lock()
attributenums[a].atype = t
attributemutex.Unlock()
return a
}

func (a Attribute) Single() Attribute {
attributemutex.Lock()
attributenums[a].single = true
attributemutex.Unlock()
return a
}

func (a Attribute) IsSingle() bool {
return attributenums[a].single
attributemutex.RLock()
result := attributenums[a].single
attributemutex.RUnlock()
return result
}

func (a Attribute) Unique() Attribute {
attributemutex.Lock()
attributenums[a].unique = true
attributemutex.Unlock()
return a
}

func (a Attribute) IsNonUnique() bool {
return !attributenums[a].unique
attributemutex.RLock()
result := !attributenums[a].unique
attributemutex.RUnlock()
return result
}

func (a Attribute) IsUnique() bool {
return attributenums[a].unique
attributemutex.RLock()
result := attributenums[a].unique
attributemutex.RUnlock()
return result
}

func (a Attribute) Hidden() Attribute {
attributemutex.Lock()
attributenums[a].hidden = true
attributemutex.Unlock()
return a
}

Expand All @@ -209,37 +229,39 @@ func StandardMerge(attr Attribute, a, b *Object) (*Object, error) {
}

func (a Attribute) Merge() Attribute {
ai := attributenums[a]
ai.merge = true
attributenums[a] = ai
attributemutex.Lock()
attributenums[a].merge = true
attributemutex.Unlock()
return a
}

func AddMergeApprover(name string, mf mergefunc) {
attributemutex.Lock()
mergeapprovers = append(mergeapprovers, mergeapproverinfo{
name: name,
mergefunc: mf,
})
attributemutex.Unlock()
}

func (a Attribute) Tag(t string) Attribute {
ai := attributenums[a]
ai.tags = append(ai.tags, t)
attributenums[a] = ai
attributemutex.Lock()
attributenums[a].tags = append(attributenums[a].tags, t)
attributemutex.Unlock()
return a
}

func (a Attribute) OnSet(onset AttributeSetFunc) Attribute {
ai := attributenums[a]
ai.onset = onset
attributenums[a] = ai
attributemutex.Lock()
attributenums[a].onset = onset
attributemutex.Unlock()
return a
}

func (a Attribute) OnGet(onget AttributeGetFunc) Attribute {
ai := attributenums[a]
ai.onget = onget
attributenums[a] = ai
attributemutex.Lock()
attributenums[a].onget = onget
attributemutex.Unlock()
return a
}

Expand Down
37 changes: 14 additions & 23 deletions modules/engine/edge.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,11 @@ type EdgeInfo struct {
}

func (eb EdgeBitmap) Set(edge Edge) EdgeBitmap {
atomic.AddUint64(&EdgePopularity[edge], 1)
return eb.set(edge)
}

func (eb *EdgeBitmap) AtomicSet(edge Edge) {
atomic.AddUint64(&EdgePopularity[edge], 1)
bits := uint64(1) << (edge % 64)
index := int(edge) / 64
index, bits := bitIndex(edge)

for {
oldvalue := atomic.LoadUint64(&eb[index])
Expand Down Expand Up @@ -89,22 +86,25 @@ func (eb *EdgeBitmap) AtomicOr(edges EdgeBitmap) {

func (eb EdgeBitmap) set(edge Edge) EdgeBitmap {
newpm := eb
bits := uint64(1) << (edge % 64)
newpm[int(edge)/64] = eb[int(edge)/64] | bits
index, bits := bitIndex(edge)
newpm[index] = eb[index] | bits
return newpm
}

func (eb EdgeBitmap) Clear(edge Edge) EdgeBitmap {
newpm := eb
bits := uint64(1) << (edge % 64)
newpm[int(edge)/64] = eb[int(edge)/64] &^ bits
index, bits := bitIndex(edge)
newpm[index] = eb[index] &^ bits
return newpm
}

func bitIndex(edge Edge) (int, uint64) {
return int(edge) >> 6, uint64(1) << (edge & 63)
}

func (eb *EdgeBitmap) AtomicClear(edge Edge) {
atomic.AddUint64(&EdgePopularity[edge], 1)
bits := uint64(1) << (edge % 64)
index := int(edge) / 64
index, bits := bitIndex(edge)

for {
oldvalue := atomic.LoadUint64(&eb[index])
Expand Down Expand Up @@ -174,7 +174,7 @@ func (eb EdgeBitmap) IsBlank() bool {
func (eb EdgeBitmap) Edges() []Edge {
result := make([]Edge, eb.Count())
var n int
for i := 0; i < len(edgeInfos); i++ {
for i := 0; i < len(edgeInfos) && n < len(result); i++ {
if eb.IsSet(Edge(i)) {
result[n] = Edge(i)
n++
Expand Down Expand Up @@ -319,8 +319,9 @@ const (
In EdgeDirection = 1
)

func (m EdgeBitmap) IsSet(method Edge) bool {
return (m[method/64] & (1 << (method % 64))) != 0 // Uuuuh, nasty and unreadable
func (m EdgeBitmap) IsSet(edge Edge) bool {
index, bits := bitIndex(edge)
return (m[index] & bits) != 0
}

func (m EdgeBitmap) MaxProbability(source, target *Object) Probability {
Expand Down Expand Up @@ -363,13 +364,3 @@ func (m EdgeBitmap) StringSlice() []string {
}
return result
}

func (m EdgeBitmap) StringBoolMap() map[string]bool {
var result = make(map[string]bool)
for i := 0; i < len(edgeInfos); i++ {
if m.IsSet(Edge(i)) {
result["pwn_"+Edge(i).String()] = true
}
}
return result
}
Loading

0 comments on commit a7cb3ba

Please sign in to comment.