transport: add SOCKS5 -proxy flag with UDP guard and test coverage#10
Merged
transport: add SOCKS5 -proxy flag with UDP guard and test coverage#10
Conversation
Splits pkg/transport into a router (tcp.go) plus two platform-specific direct dialers: direct_libc.go preserves the existing cgo libc connect() path on Unix (so proxychains can still hook it), and direct_portable.go provides a pure-Go fallback for CGO_ENABLED=0 and Windows builds. Adds proxy.go with: - Configure(Options) / ConfigureProxy to wire a SOCKS5 URL (socks5 or socks5h), with ALL_PROXY / all_proxy env fallback read directly via os.Getenv (not proxy.FromEnvironment, whose sync.Once cache is not test-friendly). - DialContext routing through the configured proxy, or directDial. - DialUDP returning ErrUDPUnderProxy when proxied, rather than silently leaking UDP packets past the SOCKS5. - IsProxyConfigured and ProxyURL for callers that need to short-circuit features that can't be tunneled. - libcForwarder so the TCP leg to the SOCKS5 proxy itself still goes through libc, enabling proxychains -> gopacket -> -proxy chaining. Tests: - export_test.go exposes ResetForTest for test isolation (Configure panics on double-call, so each test resets package state). - socks5_server_test.go hand-rolls a minimal in-process SOCKS5 server (CONNECT + no-auth, ~130 LOC, no new deps). - proxy_test.go covers invalid scheme rejection, socks5/socks5h accept, ALL_PROXY env fallback, double-call panic, default state, DialUDP/DialContext UDP rejection, credential redaction, direct-path behavior when unconfigured, and an end-to-end round-trip through the in-process SOCKS5.
Introduces three exports in pkg/flags: - ProxyFlagUsage: the shared -proxy usage string, so tools that hand-roll their flag setup render identical help text to Parse(). - ConfigureProxy(url): calls transport.Configure and exits on error. Centralizes the "fail loud on bad -proxy" behavior. - RegisterProxyFlag(): registers -proxy on the default flag.CommandLine and returns a finalizer to call after flag.Parse(). Two-line integration for tools that don't use flags.Parse(). Parse() now uses these internally. It also calls ConfigureProxy and applies Debug/Timestamp unconditionally rather than returning early when NArg == 0; tools that take all their config via flags (listeners, etc.) were previously losing -proxy because the early return skipped Configure.
- pkg/relay/http_client.go, winrm_client.go: swap the http.Transport
DialContext from &net.Dialer{Timeout: 10s}.DialContext to a closure
that calls transport.DialContext. NTLM relay now respects -proxy.
- pkg/relay/socks.go: add a comment on the HTTP DNS passthrough path
explaining why it intentionally uses net.DialTimeout rather than
transport. That path is the outbound leg of our own SOCKS5 server,
so routing it through -proxy would double-tunnel the operator's
proxy through the operator's proxy.
- pkg/tds/sqlr.go: SQL Server Browser discovery (UDP 1434) now uses
transport.DialUDP, which surfaces ErrUDPUnderProxy when a proxy is
configured rather than silently bypassing it.
Four tools that already used flags.Parse (and therefore already had -proxy registered transparently) still reached out via net.Dial* or ran DNS resolution past the proxy. Migrate the network calls: - tools/GetADComputers: custom net.Resolver now dials via transport.DialContext, so UDP DNS through the DC surfaces ErrUDPUnderProxy cleanly rather than bypassing -proxy. - tools/changepasswd: kpasswd TCP dial now uses transport.DialTimeout. - tools/smbexec: getLocalIP() short-circuits when transport.IsProxyConfigured(). The "dial UDP to find the local source address" trick has no meaning when traffic flows through a SOCKS5 proxy. - tools/raiseChild: the DNS forest-FQDN fallback (net.LookupHost) is skipped under -proxy with a message pointing the user to -parent-dc, since net.LookupHost goes to the OS resolver and would leak DNS.
Four tools register their flags directly via the stdlib flag package rather than going through flags.Parse, so they didn't inherit -proxy and transport.Configure was never called (silent no-op for the user). Wires each to flags.RegisterProxyFlag, a two-line integration that registers -proxy and returns a finalizer to call after flag.Parse(): - tools/CheckLDAPStatus - tools/DumpNTLMInfo - tools/rpcmap (also adds the pkg/flags import) - tools/mssqlinstance (also adds the pkg/flags import; -proxy will immediately error with ErrUDPUnderProxy since the tool's whole job is a UDP SQL Browser probe, but the error is surfaced clearly instead of silently bypassing)
- README.md: expand "Proxychains Support" into "Proxy Support" with two subsections (proxychains via LD_PRELOAD, and the new -proxy SOCKS5 flag). Adds -proxy to the Common Flags table, adds a quick example, and updates the final Notes line so "all tools work through proxychains" also covers -proxy. - KNOWN_ISSUES.md: add entry #8 "UDP Features Disabled Under -proxy" with a table mapping affected tools to concrete workarounds (supply -port directly, pass -dc-host / -dc-ip / -target-ip, supply -parent-dc). Renumbers "Remaining Gaps" to #9.
Bumps the version string across pkg/flags and all tool banners to v0.1.1-beta. Marks the first feature release after the initial beta (adds the -proxy socks flag and UDP-under-proxy guard landing in this PR). Note: the version string is currently duplicated 34 times across 27 tool binaries and flags.go. A follow-up PR should refactor every tool to call flags.Banner() instead of hardcoding the literal, so future bumps are one-line changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
-proxysocks flag to every gopacket tool, alongside the existing LD_PRELOAD / proxychains path. Operators no longer need libc hooking to route traffic; proxychains still works unchanged for users who prefer it (and the two can be chained).pkg/transport): split into a router (tcp.go) plus platform-specific direct dialers (direct_libc.gofor cgo+Unix,direct_portable.gofor the portable path that unblocksCGO_ENABLED=0and Windows builds in a follow-up). Newproxy.goexposesConfigure,DialContext,DialUDP,IsProxyConfigured,ProxyURL, andErrUDPUnderProxy.-proxyis refused, not silently bypassed. Surfacing a clear error viaErrUDPUnderProxyavoids leaking the operator's real source IP through UDP when the operator explicitly asked for proxying.flags.RegisterProxyFlaghelper so the handful of tools that hand-roll their flag setup (instead of callingflags.Parse) can get-proxyin two lines.pkg/relay,pkg/tds, and the tools that reached out via rawnet.Dial*ornet.Resolver. Included an intentional carve-out inpkg/relay/socks.go(our own SOCKS5 server's outbound leg) to avoid double-tunneling the operator's proxy through the operator's proxy.ALL_PROXYenv fallback, double-call panic, UDP rejection, credential redaction, direct-path behavior, and an end-to-end round-trip.-proxysubsections; a newKNOWN_ISSUES.mdentry documenting the UDP-under-proxy limitation with per-tool workarounds.Relationship to #5
Thanks to @5amu for starting this conversation in #5. That PR solves a narrower slice of the same problem (Windows-only build via a
_windows.gofallback). This PR covers the same ground and adds more: the Windows unblock (with a stricter!cgo || windowsbuild tag soCGO_ENABLED=0Linux also works), plus the explicit-proxyCLI flag, UDP handling, chaining support, and call-site migrations. Happy to credit the file-split idea from #5. Windows cross-compile verification and stubs for the 3 libpcap-dependent tools will land in a separate follow-up PR to keep this one focused on the proxy surface.Smoke-tested
Against a live GOAD lab running in GCP, over an IAP SSH tunnel with SOCKS5 on :1080. Both
-proxy socks5h://...andproxychains4were exercised against kingslanding (sevenkingdoms.local) and winterfell (north.sevenkingdoms.local). Tools confirmed working end-to-end:lookupsid,samrdump,GetNPUsers,DumpNTLMInfo,rpcmap, cross-domain SID lookup.mssqlinstancesurfacesErrUDPUnderProxycleanly as designed. No-proxy path verified against 127.0.0.1 to confirm libcconnect()still runs unchanged (error strings match pre-refactor verbatim).Not in scope
CGO_ENABLED=0Linux builds. Thedirect_portable.goscaffolding here makes them possible; the actual cross-compile verification, Windows stubs forsniff/sniffer/split, and the README Platform Support rewrite land in a follow-up.Test plan
go build ./...cleango vet ./...cleango test ./pkg/transport/passes (13/13)-proxyand proxychains