DCE/RPC bind_ack presentation-context result is never checked, so rejected binds look successful
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 708
- Forks
- 61
- PR merge metrics
- No merged PRs in 30d
Description
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, with`BindAuthMulti`'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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in pkg/dcerpc/client.go at the five bind paths listed in the issue, then inspect BindAuthMulti's existing result-list parsing. Verify the behavior through tools/rpcmap and the existing Go checks. Done means rejected presentation contexts return an actionable bind error while genuinely served interfaces still bind successfully; run go build ./..., go vet, gofmt -l, and go test ./....
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 57/100