From 3018ff793c3faed6eae29a879ad6beb862992000 Mon Sep 17 00:00:00 2001 From: Tirefire <84106878+tire-fire@users.noreply.github.com> Date: Tue, 20 Jan 2026 21:51:47 -0600 Subject: [PATCH 1/4] Add tini to runner for zombie reaping --- Dockerfile.runner | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.runner b/Dockerfile.runner index 78b61a5..95c55b8 100644 --- a/Dockerfile.runner +++ b/Dockerfile.runner @@ -13,7 +13,7 @@ RUN go build -o runner FROM alpine:3.21 WORKDIR /app COPY --from=builder /src/runner/runner runner -RUN apk add --no-cache wireguard-tools iproute2 iputils curl git openssh fortune py3-uv python3 proxychains-ng jq file sshpass bind-tools grep chrony krb5 +RUN apk add --no-cache tini wireguard-tools iproute2 iputils curl git openssh fortune py3-uv python3 proxychains-ng jq file sshpass bind-tools grep chrony krb5 # custom python check scripts COPY custom-checks/requirements.txt /app @@ -21,4 +21,4 @@ RUN ln -sf python3 /usr/bin/python RUN uv pip install --system --break-system-packages setuptools RUN uv pip install --system --break-system-packages -r /app/requirements.txt -ENTRYPOINT ["/app/runner"] +ENTRYPOINT ["/sbin/tini", "--", "/app/runner"] From dee3ecda536b5cd933c94032306ae7ac82a39abf Mon Sep 17 00:00:00 2001 From: Tirefire <84106878+tire-fire@users.noreply.github.com> Date: Wed, 21 Jan 2026 00:11:34 -0600 Subject: [PATCH 2/4] Add RDP credential checking with grdp --- engine/checks/rdp.go | 119 +++++++++++++++++++++++++++++++++++++++++-- go.mod | 2 + go.sum | 5 ++ 3 files changed, 122 insertions(+), 4 deletions(-) diff --git a/engine/checks/rdp.go b/engine/checks/rdp.go index 354936b..7099556 100644 --- a/engine/checks/rdp.go +++ b/engine/checks/rdp.go @@ -1,26 +1,137 @@ package checks import ( + "fmt" "net" "strconv" + "strings" + "sync" "time" - // why are there no good rdp libraries? + + "github.com/nakagami/grdp" ) type Rdp struct { Service + Domain string `toml:",omitempty"` } func (c Rdp) Run(teamID uint, teamIdentifier string, roundID uint, resultsChan chan Result) { definition := func(teamID uint, teamIdentifier string, checkResult Result, response chan Result) { - _, err := net.DialTimeout("tcp", c.Target+":"+strconv.Itoa(c.Port), time.Duration(c.Timeout)*time.Second) + // If no credentials configured, just do a port check + if len(c.CredLists) == 0 { + _, err := net.DialTimeout("tcp", c.Target+":"+strconv.Itoa(c.Port), time.Duration(c.Timeout)*time.Second) + if err != nil { + checkResult.Error = "connection error" + checkResult.Debug = err.Error() + response <- checkResult + return + } + checkResult.Status = true + response <- checkResult + return + } + + // Get credentials and attempt RDP authentication + username, password, err := c.getCreds(teamID) if err != nil { - checkResult.Error = "connection error" + checkResult.Error = "error getting creds" checkResult.Debug = err.Error() response <- checkResult return } - checkResult.Status = true + + // Parse domain from username if in DOMAIN\user or user@domain format + domain := c.Domain + if strings.Contains(username, "\\") { + parts := strings.SplitN(username, "\\", 2) + domain = parts[0] + username = parts[1] + } else if strings.Contains(username, "@") { + parts := strings.SplitN(username, "@", 2) + username = parts[0] + domain = parts[1] + } + + host := fmt.Sprintf("%s:%d", c.Target, c.Port) + + rdpClient := grdp.NewRdpClient(host, 800, 600) + + var loginErr error + var loginSuccess bool + var mu sync.Mutex + done := make(chan struct{}) + + rdpClient.OnError(func(err error) { + mu.Lock() + defer mu.Unlock() + if loginErr == nil { + loginErr = err + } + select { + case <-done: + default: + close(done) + } + }) + + rdpClient.OnSucces(func() { // Note: typo in library API + mu.Lock() + defer mu.Unlock() + loginSuccess = true + select { + case <-done: + default: + close(done) + } + }) + + go func() { + if err := rdpClient.Login(domain, username, password); err != nil { + mu.Lock() + defer mu.Unlock() + if loginErr == nil { + loginErr = err + } + select { + case <-done: + default: + close(done) + } + } + }() + + select { + case <-done: + case <-time.After(time.Duration(c.Timeout) * time.Second): + rdpClient.Close() + checkResult.Error = "rdp connection timeout" + checkResult.Debug = "connection timed out after " + strconv.Itoa(c.Timeout) + " seconds" + response <- checkResult + return + } + + rdpClient.Close() + + mu.Lock() + defer mu.Unlock() + + if loginSuccess { + checkResult.Status = true + checkResult.Debug = "creds used were " + username + ":" + password + response <- checkResult + return + } + + if loginErr != nil { + checkResult.Error = "rdp authentication failed for " + username + ":" + password + checkResult.Debug = loginErr.Error() + response <- checkResult + return + } + + checkResult.Error = "rdp connection failed" + checkResult.Debug = "unknown error" response <- checkResult } diff --git a/go.mod b/go.mod index fb81205..1ebef27 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/masterzen/winrm v0.0.0-20240702205601-3fad6e106085 github.com/miekg/dns v1.1.62 github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed + github.com/nakagami/grdp v0.3.9 github.com/pmezard/go-difflib v1.0.0 github.com/ramr/go-reaper v0.3.1 github.com/redis/go-redis/v9 v9.7.0 @@ -66,6 +67,7 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/kr/pretty v0.3.1 // indirect + github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde // indirect diff --git a/go.sum b/go.sum index e9d89f7..292b946 100644 --- a/go.sum +++ b/go.sum @@ -112,6 +112,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= +github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/winrm v0.0.0-20240702205601-3fad6e106085 h1:PiQLLKX4vMYlJImDzJYtQScF2BbQ0GAjPIHCDqzHHHs= @@ -120,6 +122,8 @@ github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed h1:FI2NIv6fpef6BQl2u3IZX/Cj20tfypRF4yd+uaHOMtI= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= +github.com/nakagami/grdp v0.3.9 h1:6oxmDZWk+auqCX1qjwz7r6UdkmIx02X6rJL3/zCLIAU= +github.com/nakagami/grdp v0.3.9/go.mod h1:QC6seo8SkXi+vhVhQG4P49bY6s/gRVYAd0OIxZMYOEI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -188,6 +192,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From 5c11e73cdc1dac8c3fa00fc628fa48ea3cc25f64 Mon Sep 17 00:00:00 2001 From: Tirefire <84106878+tire-fire@users.noreply.github.com> Date: Wed, 21 Jan 2026 00:12:44 -0600 Subject: [PATCH 3/4] Revert "Add RDP credential checking with grdp" This reverts commit dee3ecda536b5cd933c94032306ae7ac82a39abf. --- engine/checks/rdp.go | 119 ++----------------------------------------- go.mod | 2 - go.sum | 5 -- 3 files changed, 4 insertions(+), 122 deletions(-) diff --git a/engine/checks/rdp.go b/engine/checks/rdp.go index 7099556..354936b 100644 --- a/engine/checks/rdp.go +++ b/engine/checks/rdp.go @@ -1,137 +1,26 @@ package checks import ( - "fmt" "net" "strconv" - "strings" - "sync" "time" - - "github.com/nakagami/grdp" + // why are there no good rdp libraries? ) type Rdp struct { Service - Domain string `toml:",omitempty"` } func (c Rdp) Run(teamID uint, teamIdentifier string, roundID uint, resultsChan chan Result) { definition := func(teamID uint, teamIdentifier string, checkResult Result, response chan Result) { - // If no credentials configured, just do a port check - if len(c.CredLists) == 0 { - _, err := net.DialTimeout("tcp", c.Target+":"+strconv.Itoa(c.Port), time.Duration(c.Timeout)*time.Second) - if err != nil { - checkResult.Error = "connection error" - checkResult.Debug = err.Error() - response <- checkResult - return - } - checkResult.Status = true - response <- checkResult - return - } - - // Get credentials and attempt RDP authentication - username, password, err := c.getCreds(teamID) + _, err := net.DialTimeout("tcp", c.Target+":"+strconv.Itoa(c.Port), time.Duration(c.Timeout)*time.Second) if err != nil { - checkResult.Error = "error getting creds" + checkResult.Error = "connection error" checkResult.Debug = err.Error() response <- checkResult return } - - // Parse domain from username if in DOMAIN\user or user@domain format - domain := c.Domain - if strings.Contains(username, "\\") { - parts := strings.SplitN(username, "\\", 2) - domain = parts[0] - username = parts[1] - } else if strings.Contains(username, "@") { - parts := strings.SplitN(username, "@", 2) - username = parts[0] - domain = parts[1] - } - - host := fmt.Sprintf("%s:%d", c.Target, c.Port) - - rdpClient := grdp.NewRdpClient(host, 800, 600) - - var loginErr error - var loginSuccess bool - var mu sync.Mutex - done := make(chan struct{}) - - rdpClient.OnError(func(err error) { - mu.Lock() - defer mu.Unlock() - if loginErr == nil { - loginErr = err - } - select { - case <-done: - default: - close(done) - } - }) - - rdpClient.OnSucces(func() { // Note: typo in library API - mu.Lock() - defer mu.Unlock() - loginSuccess = true - select { - case <-done: - default: - close(done) - } - }) - - go func() { - if err := rdpClient.Login(domain, username, password); err != nil { - mu.Lock() - defer mu.Unlock() - if loginErr == nil { - loginErr = err - } - select { - case <-done: - default: - close(done) - } - } - }() - - select { - case <-done: - case <-time.After(time.Duration(c.Timeout) * time.Second): - rdpClient.Close() - checkResult.Error = "rdp connection timeout" - checkResult.Debug = "connection timed out after " + strconv.Itoa(c.Timeout) + " seconds" - response <- checkResult - return - } - - rdpClient.Close() - - mu.Lock() - defer mu.Unlock() - - if loginSuccess { - checkResult.Status = true - checkResult.Debug = "creds used were " + username + ":" + password - response <- checkResult - return - } - - if loginErr != nil { - checkResult.Error = "rdp authentication failed for " + username + ":" + password - checkResult.Debug = loginErr.Error() - response <- checkResult - return - } - - checkResult.Error = "rdp connection failed" - checkResult.Debug = "unknown error" + checkResult.Status = true response <- checkResult } diff --git a/go.mod b/go.mod index 1ebef27..fb81205 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/masterzen/winrm v0.0.0-20240702205601-3fad6e106085 github.com/miekg/dns v1.1.62 github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed - github.com/nakagami/grdp v0.3.9 github.com/pmezard/go-difflib v1.0.0 github.com/ramr/go-reaper v0.3.1 github.com/redis/go-redis/v9 v9.7.0 @@ -67,7 +66,6 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/kr/pretty v0.3.1 // indirect - github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde // indirect diff --git a/go.sum b/go.sum index 292b946..e9d89f7 100644 --- a/go.sum +++ b/go.sum @@ -112,8 +112,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= -github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/winrm v0.0.0-20240702205601-3fad6e106085 h1:PiQLLKX4vMYlJImDzJYtQScF2BbQ0GAjPIHCDqzHHHs= @@ -122,8 +120,6 @@ github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed h1:FI2NIv6fpef6BQl2u3IZX/Cj20tfypRF4yd+uaHOMtI= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= -github.com/nakagami/grdp v0.3.9 h1:6oxmDZWk+auqCX1qjwz7r6UdkmIx02X6rJL3/zCLIAU= -github.com/nakagami/grdp v0.3.9/go.mod h1:QC6seo8SkXi+vhVhQG4P49bY6s/gRVYAd0OIxZMYOEI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -192,7 +188,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From 4cf8f7e95b0cdfffd9900af18d52a28aad69cb73 Mon Sep 17 00:00:00 2001 From: Tirefire <84106878+tire-fire@users.noreply.github.com> Date: Fri, 23 Jan 2026 00:14:38 -0600 Subject: [PATCH 4/4] Pass domain to SMB NTLMInitiator --- engine/checks/smb.go | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/checks/smb.go b/engine/checks/smb.go index e04718d..659ef99 100644 --- a/engine/checks/smb.go +++ b/engine/checks/smb.go @@ -57,6 +57,7 @@ func (c Smb) Run(teamID uint, teamIdentifier string, roundID uint, resultsChan c Initiator: &smb2.NTLMInitiator{ User: username, Password: password, + Domain: c.Domain, }, }