Skip to content

Comments

FIx Reuse delegated ST across Kerberos RPC connections#1104

Open
azoxlpf wants to merge 1 commit intoPennyw0rth:mainfrom
azoxlpf:fix/delegate-st-propagation
Open

FIx Reuse delegated ST across Kerberos RPC connections#1104
azoxlpf wants to merge 1 commit intoPennyw0rth:mainfrom
azoxlpf:fix/delegate-st-propagation

Conversation

@azoxlpf
Copy link
Contributor

@azoxlpf azoxlpf commented Feb 10, 2026

Description

After @mpgn review on PR #1078) I noticed that when using --delegate (with or without --use-kcache), the Service Ticket (ST) obtained via S4U2Proxy was only used for the initial SMB connection. Subsequent operations that open new connections (e.g. --users, -x, --pass-pol) were creating new SMB/RPC transports without that delegated ST. They then tried to authenticate as the delegated user with the original credentials (e.g. machine account password), which fails with KDC_ERR_PREAUTH_FAILED or STATUS_MORE_PROCESSING_REQUIRED because the delegated user has no usable password in that context.

This PR fixes that by storing the delegated ST after S4U2Proxy in smb.py (self.delegated_st) and passing it into every code path that builds a new Kerberos-authenticated connection : SAMR (users, passpol, samrfunc), exec methods (atexec, smbexec, wmiexec, mmcexec), and any transport that accepts a ST.

Type of change

Insert an "x" inside the brackets for relevant items (do not delete options)

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Deprecation of feature or functionality
  • This change requires a documentation update
  • This requires a third party update (such as Impacket, Dploot, lsassy, etc)

Screenshots (if appropriate):

Before :

2 3

After :

4 5

Checklist:

Insert an "x" inside the brackets for completed and relevant items (do not delete options)

  • I have ran Ruff against my changes (via poetry: poetry run python -m ruff check . --preview, use --fix to automatically fix what it can)
  • I have added or updated the tests/e2e_commands.txt file if necessary (new modules or features are required to be added to the e2e tests)
  • New and existing e2e tests pass locally with my changes
  • If reliant on changes of third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (PR here: https://github.com/Pennyw0rth/NetExec-Wiki)

@NeffIsBack
Copy link
Member

Thanks for the PR!

So my thought on this: Similar to what has been brought up in #1080 we do use RPC in a ton of different places, without having the setup streamlined. In this particular case, the issue is that RPC functions do not take into account that STs might already exist. This pretty accurately points at the bigger picture: each RPC connection must check every rare circumstance that could exist. So in order to fix it we would have to 1. find every use of RPC and 2. prevent new RPC usages from missing this fix.

To fix this (and other issues like kerb auth etc.), It might be time that we create an RPC class that handles all of this under the hood and in one place. My initial idea was that we create a class that:

  • requires an NetExec protocol object as first parameter
  • uses all existing information about authentication to either use the existing SMB conn if possible or create a new one with the auth info in NetExec
  • also add a variable that forces recreation of the connection as TCP connection instead of SMB

Then we would have one single places to make sure that RPC auth works with everything that exists (kerberos, all sorts of tickets, RPC signing/sealing etc.) and optionally using an existing SMB connection, without the need for recreating connections.

Imo that could ease development, especially in modules where a lot of new RPC connections are established and each time kerberos/nt hashes/tickets etc. have to be checked. Let my know what you guys think.

@azoxlpf
Copy link
Contributor Author

azoxlpf commented Feb 17, 2026

I agree with this approach. A centralized RPC class is the right direction. I had considered it but went for a smaller change to avoid a large refactor. That said, having a single place to handle auth (Kerberos, hashes, tickets, SMB reuse, etc.) is clearly the better long-term solution and would prevent issues like the ST propagation one.

@Marshall-Hallenbeck
Copy link
Collaborator

To fix this (and other issues like kerb auth etc.), It might be time that we create an RPC class that handles all of this under the hood and in one place. My initial idea was that we create a class that:

  • requires an NetExec protocol object as first parameter
  • uses all existing information about authentication to either use the existing SMB conn if possible or create a new one with the auth info in NetExec
  • also add a variable that forces recreation of the connection as TCP connection instead of SMB

I was working on trying to do this for other protocols, so instead of modules creating their own full connections, we could just pass existing objects and get a new one in a new protocol, and unfortunately the way we have things setup, it auto-executes a bunch of stuff on initialization, so we have to completely refactor it.

You know I'm all for refactors though :P if we do it, it will make a lot of things easier.

@NeffIsBack
Copy link
Member

To fix this (and other issues like kerb auth etc.), It might be time that we create an RPC class that handles all of this under the hood and in one place. My initial idea was that we create a class that:

  • requires an NetExec protocol object as first parameter
  • uses all existing information about authentication to either use the existing SMB conn if possible or create a new one with the auth info in NetExec
  • also add a variable that forces recreation of the connection as TCP connection instead of SMB

I was working on trying to do this for other protocols, so instead of modules creating their own full connections, we could just pass existing objects and get a new one in a new protocol, and unfortunately the way we have things setup, it auto-executes a bunch of stuff on initialization, so we have to completely refactor it.

You know I'm all for refactors though :P if we do it, it will make a lot of things easier.

Yeah we should not create a NetExec rpc connection object/class because that does a ton of logic that isn't needed. We should just create a new class that lives under nxc/helpers/ for example and uses an nxc connection object to establish that rpc connection (or continues using the existing SMB connection).

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.

3 participants