Thanks @psycep for putting this out -- we are going to get a lot of use out of it!
Summary
A bind_ack carries a p_result_list stating whether each proposed presentation context was accepted. pkg/dcerpc/client.go validates the PDU type and stops there, so a provider_rejection is indistinguishable from an accepted bind.
All five bind paths are affected — each checks only PacketType:
| Function |
Line |
Used by |
BindWithSyntax (and Bind) |
177-179 |
tools/rpcmap, tools/rpcdump |
BindAuth |
783-785 |
NTLM-authenticated binds |
BindAuthMulti |
1005-1007 |
pkg/dcerpc/dcom |
BindAuthKerberos |
1243-1245 |
Kerberos binds |
BindAuthKerberosWithHandler |
1491-1493 |
Kerberos binds with a custom handler |
BindAuthMulti already parses the result list correctly (nResults(1) + Reserved(3) + N × 24) and logs each Result/Reason, but never returns an error for a rejection — the parser is there, nothing acts on its answer.
Impacket checks this client-side in impacket/dcerpc/v5/rpcrt.py:1618-1629 and raises DCERPCException("Bind context %d rejected: ..."), with a hint for exactly this case: "(this usually means the interface isn't listening on the given endpoint)".
Effect 1: tools/rpcmap reports interfaces that do not exist
rpcmap decides availability from Bind returning nil (tools/rpcmap/main.go:360, isBindable). Since a rejected bind returns nil, every probed UUID looks available. Against a DC on TCP/135, no credentials needed:
$ go run ./tools/rpcmap -brute-uuids -brute-versions -version-max 5 \
-uuid e1af8308-5d1f-11c9-91a4-08002b14a0fa 'ncacn_ip_tcp:192.168.100.10[135]'
UUID : E1AF8308-5D1F-11C9-91A4-08002B14A0FA v0.0
UUID : E1AF8308-5D1F-11C9-91A4-08002B14A0FA v1.0
UUID : E1AF8308-5D1F-11C9-91A4-08002B14A0FA v2.0
UUID : E1AF8308-5D1F-11C9-91A4-08002B14A0FA v3.0
UUID : E1AF8308-5D1F-11C9-91A4-08002B14A0FA v4.0
UUID : E1AF8308-5D1F-11C9-91A4-08002B14A0FA v5.0
[*] Found 6 UUID(s) via bruteforce.
Impacket output:
(env) env$ pip freeze | grep impacket
impacket==0.13.1
(env) env$ ./bin/rpcmap.py -brute-uuids -brute-versions -version-max 5 -uuid "E1AF8308-5D1F-11C9-91A4-08002B14A0FA v3.0" 'ncacn_ip_tcp:192.168.100.10[135]'
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
Procotol: N/A
Provider: rpcss.dll
UUID: E1AF8308-5D1F-11C9-91A4-08002B14A0FA v3.0
Versions 0: abstract_syntax_not_supported (version not supported)
Versions 1: abstract_syntax_not_supported (version not supported)
Versions 2: abstract_syntax_not_supported (version not supported)
Versions 3: success
Versions 4-5: abstract_syntax_not_supported (version not supported)
[*] Tested 1 UUID(s)
(env) env$
Only v3.0 exists. Likewise, probing SAMR's UUID on 135 — where SAMR is not served, it lives on \pipe\lsass — reports a hit:
gopacket$ go run ./tools/rpcmap -brute-uuids -uuid 12345778-1234-abcd-ef00-0123456789ac 'ncacn_ip_tcp:192.168.100.10[135]'
gopacket v0.1.1-beta - Copyright 2026 Google LLC
[*] Trying to connect to 192.168.100.10:135...
[*] MGMT interface not available, falling back to UUID bruteforce...
Protocol: [MS-SAMR]: Security Account Manager (SAM) Remote Protocol
Provider: samsrv.dll
UUID : 12345778-1234-ABCD-EF00-0123456789AC v1.0
[*] Found 1 UUID(s) via bruteforce.
gopacket$
MGMT enumeration of the same target lists 13 interfaces, does not include SAMR, but does include a single instance of the endpoint mapper service, so the tool contradicts itself.
gopacket$ go run ./tools/rpcmap 'ncacn_ip_tcp:192.168.100.10[135]'
gopacket v0.1.1-beta - Copyright 2026 Google LLC
[*] Trying to connect to 192.168.100.10:135...
[*] Bound to MGMT interface, querying remote interface list...
Protocol: N/A
Provider: rpcss.dll
UUID : 00000136-0000-0000-C000-000000000046 v0.0
Protocol: [MS-DCOM]: Distributed Component Object Model (DCOM) Remote
Provider: rpcss.dll
UUID : 000001A0-0000-0000-C000-000000000046 v0.0
Protocol: N/A
Provider: rpcss.dll
UUID : 0B0A6584-9E0F-11CF-A3CF-00805F68CB1B v1.1
Protocol: N/A
Provider: rpcss.dll
UUID : 1D55B526-C137-46C5-AB79-638F2A68E869 v1.0
Protocol: N/A
Provider: rpcss.dll
UUID : 412F241E-C12A-11CE-ABFF-0020AF6E7A17 v0.2
Protocol: [MS-DCOM]: Distributed Component Object Model (DCOM) Remote
Provider: rpcss.dll
UUID : 4D9F4AB8-7D1C-11CF-861E-0020AF6E7C57 v0.0
Protocol: N/A
Provider: rpcss.dll
UUID : 64FE0B7F-9EF5-4553-A7DB-9A1975777554 v1.0
Protocol: [MS-DCOM]: Distributed Component Object Model (DCOM) Remote
Provider: rpcss.dll
UUID : 99FCFEC4-5260-101B-BBCB-00AA0021347A v0.0
Protocol: [MS-RPCE]: Remote Management Interface
Provider: rpcrt4.dll
UUID : AFA8BD80-7D8A-11C9-BEF4-08002B102989 v1.0
Protocol: N/A
Provider: rpcss.dll
UUID : B9E79E60-3D52-11CE-AAA1-00006901293F v0.2
Protocol: N/A
Provider: rpcss.dll
UUID : C6F3EE72-CE7E-11D1-B71E-00C04FC3111A v1.0
Protocol: [MS-RPCE]: Endpoint Mapper
Provider: rpcss.dll
UUID : E1AF8308-5D1F-11C9-91A4-08002B14A0FA v3.0
Protocol: N/A
Provider: rpcss.dll
UUID : E60C73E6-88F9-11CF-9AF1-0020AF6E72F4 v2.0
[*] Received 13 interfaces from MGMT.
gopacket$
Effect 2: a rejected bind surfaces later as a misleading fault
On the authenticated paths the rejection is masked and the first call fails with nca_s_unk_if (0x1c010003), pointing at the call rather than the bind. Minimal program using pkg/dcerpc directly, same two probes:
=== epmapper (served here) ===
[!] Bind returned nil
[*] first Call(opnum=0) failed: RPC Fault: status=0x000006d8 <- application error, bind was fine
=== SAMR (not served here) ===
[!] Bind returned nil <- rejection ignored
[*] first Call(opnum=0) failed: RPC Fault: status=0x1c010003 <- nca_s_unk_if
Impacket, same target, same two probes, unmodified:
=== epmapper ===
[*] bind succeeded
=== SAMR ===
[+] bind raised at bind time: Bind context 1 rejected: provider_rejection; abstract_syntax_not_supported (this usually means the interface isn't listening on the given endpoint)
Environment
- gopacket commit:
627c8b47e731238732e7e41686ea3456e0744371 (main)
- Target: Windows Server 2025 DC, TCP/135, unauthenticated bind
- Impacket 0.13.1 for comparison
Nothing here depends on target hardening or credentials — any DC with 135 reachable
reproduces it.
Suggested fix
A validateBindAck(body []byte) error helper called from all five bind paths, withBindAuthMulti's ad-hoc parse block replaced by it. Happy to open the PR; it is ~130 lines of new file plus five call sites, and I have it running against the DC above.
Behavior with the fix:
gopacket$ go run ./tools/rpcmap -brute-uuids -brute-versions -version-max 5 -uuid e1af8308-5d1f-11c9-91a4-08002b14a0fa 'ncacn_ip_tcp:192.168.100.10[135]'
gopacket v0.1.1-beta - Copyright 2026 Google LLC
[*] Trying to connect to 192.168.100.10:135...
[*] MGMT interface not available, falling back to UUID bruteforce...
Protocol: [MS-RPCE]: Endpoint Mapper
Provider: rpcss.dll
UUID : E1AF8308-5D1F-11C9-91A4-08002B14A0FA v3.0
[*] Found 1 UUID(s) via bruteforce.
gopacket$
gopacket$ go run ./tools/rpcmap -brute-uuids -uuid 12345778-1234-abcd-ef00-0123456789ac 'ncacn_ip_tcp:192.168.100.10[135]'
gopacket v0.1.1-beta - Copyright 2026 Google LLC
[*] Trying to connect to 192.168.100.10:135...
[*] MGMT interface not available, falling back to UUID bruteforce...
[*] Found 0 UUID(s) via bruteforce.
gopacket$
Design points worth review:
- Result and reason should be decoded to names, mirroring impacket's message including the endpoint hint, so the error is actionable.
acceptance(0) and negotiate_ack(3) should be accepted. Impacket only accepts 0, but negotiate_ack is the legitimate positive result for bind-time feature negotiation (BTFN) ([MS-RPCE] 2.2.2.4), so allowing it avoids breaking a BTFN context if one is added later.
- The helper takes the PDU body (header removed) because the unauthenticated path keeps only the body. The 16-byte common header is 4-byte aligned, so the
p_result_list alignment is unaffected.
- It fails open when the PDU is too short to hold a result list, logging via
build.DebugLog. An explicit rejection is well-formed; failing closed on a truncated ack would risk breaking working binds for no benefit. Happy to make that strict instead if you'd prefer.
Regression checks done:
- No false negatives:
rpcmap -brute-versions still finds epmapper at v3.0, and binds to genuinely served interfaces still succeed.
BindAuthMulti now fails if any context is rejected, matching impacket. Its only caller is dcom.BindInterfaces, which currently has no callers, so no live path changes behavior.
- No bogus-bind pattern exists in the tree (no equivalent of impacket's
bogus_binds), so nothing relies on rejections being ignored.
go build ./..., go vet, gofmt -l clean; go test ./... unchanged.
Thanks @psycep for putting this out -- we are going to get a lot of use out of it!
Summary
A
bind_ackcarries ap_result_liststating whether each proposed presentation context was accepted.pkg/dcerpc/client.govalidates the PDU type and stops there, so aprovider_rejectionis indistinguishable from an accepted bind.All five bind paths are affected — each checks only
PacketType:BindWithSyntax(andBind)tools/rpcmap,tools/rpcdumpBindAuthBindAuthMultipkg/dcerpc/dcomBindAuthKerberosBindAuthKerberosWithHandlerBindAuthMultialready parses the result list correctly (nResults(1) + Reserved(3) + N × 24) and logs eachResult/Reason, but never returns an error for a rejection — the parser is there, nothing acts on its answer.Impacket checks this client-side in
impacket/dcerpc/v5/rpcrt.py:1618-1629and raisesDCERPCException("Bind context %d rejected: ..."), with a hint for exactly this case: "(this usually means the interface isn't listening on the given endpoint)".Effect 1: tools/rpcmap reports interfaces that do not exist
rpcmapdecides availability fromBindreturning nil (tools/rpcmap/main.go:360,isBindable). Since a rejected bind returns nil, every probed UUID looks available. Against a DC on TCP/135, no credentials needed:Impacket output:
Only v3.0 exists. Likewise, probing SAMR's UUID on 135 — where SAMR is not served, it lives on
\pipe\lsass— reports a hit:MGMT enumeration of the same target lists 13 interfaces, does not include SAMR, but does include a single instance of the endpoint mapper service, so the tool contradicts itself.
Effect 2: a rejected bind surfaces later as a misleading fault
On the authenticated paths the rejection is masked and the first call fails with
nca_s_unk_if (0x1c010003), pointing at the call rather than the bind. Minimal program usingpkg/dcerpcdirectly, same two probes:Impacket, same target, same two probes, unmodified:
Environment
627c8b47e731238732e7e41686ea3456e0744371(main)Nothing here depends on target hardening or credentials — any DC with 135 reachable
reproduces it.
Suggested fix
A
validateBindAck(body []byte) errorhelper called from all five bind paths, withBindAuthMulti's ad-hoc parse block replaced by it. Happy to open the PR; it is ~130 lines of new file plus five call sites, and I have it running against the DC above.Behavior with the fix:
Design points worth review:
acceptance(0)andnegotiate_ack(3)should be accepted. Impacket only accepts 0, butnegotiate_ackis the legitimate positive result for bind-time feature negotiation (BTFN) ([MS-RPCE] 2.2.2.4), so allowing it avoids breaking a BTFN context if one is added later.p_result_listalignment is unaffected.build.DebugLog. An explicit rejection is well-formed; failing closed on a truncated ack would risk breaking working binds for no benefit. Happy to make that strict instead if you'd prefer.Regression checks done:
rpcmap -brute-versionsstill finds epmapper at v3.0, and binds to genuinely served interfaces still succeed.BindAuthMultinow fails if any context is rejected, matching impacket. Its only caller isdcom.BindInterfaces, which currently has no callers, so no live path changes behavior.bogus_binds), so nothing relies on rejections being ignored.go build ./...,go vet,gofmt -lclean;go test ./...unchanged.