Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions ad/GOAD-variant-1/data/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,11 @@
}
},
"shares": {
"thewall": {
"records": {
"change": "CLOUD\\anthony.green,CLOUD\\carol.peterson",
"full": "CLOUD\\AdministrationSquad",
"list": "yes",
"path": "C:\\thewall",
"path": "C:\\records",
"read": "Users"
}
}
Expand Down Expand Up @@ -358,7 +358,7 @@
],
"path": "CN=Users,DC=Cloud,DC=sigmatech,DC=local",
"spns": [
"HTTP/thewall.cloud.sigmatech.local"
"HTTP/records.cloud.sigmatech.local"
]
},
"carol.peterson": {
Expand Down Expand Up @@ -443,9 +443,9 @@
"patrick": {
"firstname": "patrick",
"surname": "patrick",
"password": "kkzof",
"password": "patrick",
"city": "Atlanta",
"description": "Brainless Giant",
"description": "Patrick",
"groups": [
"AdministrationSquad"
],
Expand Down
4 changes: 2 additions & 2 deletions ad/GOAD-variant-1/data/dev-overlay.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
],
"vulns_vars": {
"shares": {
"thewall": {
"path": "C:\\thewall",
"records": {
"path": "C:\\records",
"list": "yes",
"full": "CLOUD\\AdministrationSquad",
"change": "CLOUD\\anthony.green,CLOUD\\carol.peterson",
Expand Down
6 changes: 5 additions & 1 deletion ad/GOAD-variant-1/mapping.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"_W1sper_$": "SGW7xb\u0026$h",
"cersei": "ttimzd",
"fr3edom": "1g16kx7",
"hodor": "kkzof",
"hodor": "patrick",
"horse": "fdmzy",
"iamthekingoftheworld": "ludexcyrpmgdaekcwrsw",
"iknownothing": "sigpubdmehqv",
Expand Down Expand Up @@ -156,6 +156,9 @@
"Westerlands": "Northern"
},
"acls": {},
"shares": {
"thewall": "records"
},
"misc": {
"Arya": "frank",
"BRAAVOS": "AURORA2",
Expand Down Expand Up @@ -248,6 +251,7 @@
"stark": "phillips",
"targaryen": "wright",
"tarly": "peterson",
"thewallserver": "recordsserver",
"tyron": "raymond",
"tywin": "catherine",
"varys": "collins",
Expand Down
2 changes: 1 addition & 1 deletion ad/GOAD-variant-1/scripts/archives/kerberoasting.ps1
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Set-ADUser -Identity "anthony.green" -ServicePrincipalNames @{Add='HTTP/thewallserver'}
Set-ADUser -Identity "anthony.green" -ServicePrincipalNames @{Add='HTTP/recordsserver'}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$identity = 'anthony.green'
$spn = 'CIFS/thewall.cloud.sigmatech.local'
$spn = 'CIFS/records.cloud.sigmatech.local'
$delegateTo = @('CIFS/delta.cloud.sigmatech.local', 'CIFS/delta')

# Re-adding a value a multi-valued attribute already holds is an LDAP constraint
Expand Down
19 changes: 19 additions & 0 deletions cli/internal/variant/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ var preservedUsernames = map[string]bool{"sql_svc": true}
// in unrelated contexts.
const minShareNameLength = 5

// shareAliases maps a share name to compound forms of it that appear elsewhere
// in upstream GOAD. Share replacement is word-boundary matched so the JSON key
// and the path (C:\thewall) move together without touching substrings, but that
// same boundary means \bthewall\b cannot reach "thewallserver" in the archived
// kerberoasting script. Each alias is rewritten to the new share name plus
// whatever the alias appended, keeping the parallel form.
var shareAliases = map[string][]string{
"thewall": {"thewallserver"},
}

// hostnameAliases maps canonical hostnames to known typos/aliases in upstream GOAD.
var hostnameAliases = map[string][]string{
"braavos": {"Bravos"},
Expand Down Expand Up @@ -697,6 +707,15 @@ func (g *Generator) mapShares(config *LabConfig) {
newName := g.nameGen.GenerateShareName()
g.mappings.Shares[shareName] = newName
fmt.Printf(" %s -> %s\n", shareName, newName)

// Compound forms go to Misc for plain (non-boundary) replacement.
// They are longer than the bare share name, and the replacement
// list is sorted longest-first, so they match before it does.
for _, alias := range shareAliases[shareName] {
suffix := strings.TrimPrefix(alias, shareName)
g.mappings.Misc[alias] = newName + suffix
fmt.Printf(" %s -> %s (compound)\n", alias, newName+suffix)
}
}
}
}
Expand Down
34 changes: 34 additions & 0 deletions cli/internal/variant/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,3 +627,37 @@ func TestPasswordEqualToUsernamePreservesPairing(t *testing.T) {
}
}
}

// TestCompoundShareNameRewritten covers "thewallserver" in the archived
// kerberoasting script. Share replacement is word-boundary matched, so the bare
// share mapping cannot reach a compound form and it survived into variants.
func TestCompoundShareNameRewritten(t *testing.T) {
sourceDir, targetDir := setupTestSource(t)
scriptPath := filepath.Join(sourceDir, "scripts", "kerberoasting.ps1")
if err := os.WriteFile(scriptPath,
[]byte(`Set-ADUser -Identity "jon.snow" -ServicePrincipalNames @{Add='HTTP/thewallserver'}`+"\n"),
0o644); err != nil {
t.Fatal(err)
}

gen := NewGenerator(sourceDir, targetDir, "test-compound")
if err := gen.Run(); err != nil {
t.Fatalf("generator failed: %v", err)
}

out, err := os.ReadFile(filepath.Join(targetDir, "scripts", "kerberoasting.ps1"))
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(out), "thewall") {
t.Errorf("compound share name survived: %s", out)
}

newShare := gen.mappings.Shares["thewall"]
if newShare == "" {
t.Fatal("thewall share was not mapped")
}
if !strings.Contains(string(out), newShare+"server") {
t.Errorf("got %q, want it to contain %q", out, newShare+"server")
}
}
Loading