Skip to content

DCE/RPC bind_ack presentation-context result is never checked, so rejected binds look successful #45

Description

@sgibson-vc

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions