Skip to content

kerberos, dcerpc: tunnel KDC traffic through pkg/transport#26

Merged
psycep merged 1 commit into
mainfrom
kerberos-proxy-leak
May 12, 2026
Merged

kerberos, dcerpc: tunnel KDC traffic through pkg/transport#26
psycep merged 1 commit into
mainfrom
kerberos-proxy-leak

Conversation

@psycep
Copy link
Copy Markdown
Collaborator

@psycep psycep commented May 12, 2026

Summary

  • Kerberos KDC traffic (AS/TGS/kpasswd) leaked the operator IP under -proxy because the embedded jcmturner/gokrb5/v8 used hard-coded net.DialTimeout for KDC I/O, bypassing pkg/transport entirely. DCSync (via oiweiwei/go-msrpc/ssp/krb5) and the four DCOM tools leaked the same way through a second Kerberos library.
  • Vendored jcmturner/gokrb5/v8 in-tree at pkg/third_party/gokrb5 with a required KDCDialer first argument on every client constructor — proxy-bypass is now a compile error. kerberos.TransportKDCDialer{} is the only production dialer; client.DirectDialer{} exists as an explicit escape hatch.
  • DCERPC: set krbConfig.KDCDialer on every krb5.Config, pass dcerpc.WithDialer(transport.ContextDialer{}) on every dcerpc.Dial, and use the \"ncacn_ip_tcp:\" StringBinding prefix on the OXID-pivot dial to sidestep go-msrpc's hard-coded pre-dial net.LookupIP (so FQDN resolution stays on the SOCKS5 proxy).
  • udp_preference_limit=1 and dns_lookup_kdc/realm=false stamped unconditionally; /etc/krb5.conf and $KRB5_CONFIG deliberately not read.
  • KNOWN_ISSUES.md §8 updated with the new guarantee.

Test plan

End-to-end verified against a live GOAD lab with SOCKS5 over IAP-tunneled SSH:

  • getTGT (sevenkingdoms / north / essos)
  • smbclient -k (all three DCs)
  • GetUserSPNs -k (Kerberoast — exchanges TGS via proxy)
  • GetNPUsers (AS-REP — direct transport.Dial)
  • getST (S4U2self/S4U2proxy)
  • secretsdump -k DCSync — extracted krbtgt
  • wmiexec -kwhoami returned north\\administrator
  • smbexec -k — returned nt authority\\system
  • NTLM/password regressions: smbclient, wmiexec, smbexec, secretsdump, PtH wmiexec -hashes — all clean
  • tcpdump on operator host (filter: net 10.10.10.0/24 and not host 127.0.0.1): zero packets during the entire test matrix
  • Negative control (same getTGT without -proxy): immediately emits direct SYNs to KDC, confirming leak class is real and detectable
  • go build ./..., go vet ./..., go test ./... all clean

Conflict note

Branched from main@4890b97; remote has since merged 9 PRs. Likely conflicts on go.mod/go.sum (Azure/go-ntlmssp bump), possibly KNOWN_ISSUES.md, pkg/registry/hive.go-adjacent docs, and pkg/dcerpc/drsuapi/* (out-of-scope for this PR but on the same merge path). Will rebase before merge.

@google-cla
Copy link
Copy Markdown

google-cla Bot commented May 12, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@psycep psycep force-pushed the kerberos-proxy-leak branch from f2b1d59 to efcc4f9 Compare May 12, 2026 04:15
The embedded gokrb5/v8 library hard-coded net.DialTimeout for AS/TGS
exchanges, bypassing -proxy and leaking the operator's source IP to the
KDC (UDP/88 first, TCP/88 fallback). The DCERPC Kerberos auth path used
a separate library (oiweiwei/gokrb5.fork/v9 via go-msrpc) that leaked the
same way.

Vendor jcmturner/gokrb5/v8 in-tree at pkg/third_party/gokrb5 with a
required KDCDialer first argument on every client constructor, so
proxy-bypass becomes a compile error. Wire kerberos.TransportKDCDialer
everywhere a gokrb5 client is built. Stamp udp_preference_limit=1 and
dns_lookup_kdc/realm=false unconditionally so KRB5 is TCP-only and the
OS resolver is never consulted; /etc/krb5.conf and $KRB5_CONFIG are
deliberately not read.

For DCERPC: set krbConfig.KDCDialer on every krb5.Config, pass
dcerpc.WithDialer(transport.ContextDialer{}) on every dcerpc.Dial, and
use the "ncacn_ip_tcp:" StringBinding prefix on the OXID-pivot dial so
go-msrpc's hard-coded pre-dial net.LookupIP is skipped (defers FQDN
resolution to the SOCKS5 proxy).

Verified against a live GOAD lab: 8 Kerberos-touching tools plus 5
NTLM/password/PtH regressions all operate through SOCKS5 with zero
direct packets to the AD subnet. Negative control (no -proxy)
immediately emits direct SYNs to the KDC, confirming both the leak
class and the fix.
@psycep psycep force-pushed the kerberos-proxy-leak branch from efcc4f9 to 8eea029 Compare May 12, 2026 04:23
@psycep psycep merged commit 33758f7 into main May 12, 2026
2 checks passed
@psycep psycep deleted the kerberos-proxy-leak branch May 12, 2026 04:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant