add ldap3 Kerberos authentication function
Description
This PR introduces a pure-Python LDAP bind routine that uses the ldap3 library with SPNEGO / Kerberos authentication (ldap3_kerberos_login). Until now NetExec bound over LDAP exclusively with Impacket’s low-level
LDAPConnection, which:
- accepts every secret type (password, NT/LM hash, AES key, ccache),
- but does not expose high-level CRUD helpers (
modify(),add(),delete(), …).
ldap3, on the other hand, offers a complete Pythonic API for directory operations but lacked a Kerberos path inside NetExec. The new function bridges that gap:
- requests (or re-uses) a TGT/TGS, wraps it in a SPNEGO blob and completes
a SASL bind through
ldap3, - normalises both TGT and TGS into a common dict format
(
{"KDC_REP": blob, "cipher": …, "sessionKey": …}) so downstream code can consume them without branching.
Why ldap3 matters
- Enables attribute manipulations required by the upcoming
--targetedkerberoastPR (temporary add/remove ofservicePrincipalName) Add targeted Kerberoasting by injecting/removing temporary SPNs - Provides an API that future features (delegation abuse, ACL edits, etc.) can reuse without re-implementing ASN.1.
- Keeps everything pure Python – no additional binaries or
system libraries beyond the already-present
ldap3.
Dependencies
- No new external dependencies.
ldap3,impacket,pyasn1andCryptodomeare already shipped with NetExec.
Summary
Add a fully-featured
ldap3Kerberos bind, paving the way for SPN-injection workflows (Targeted Kerberoast) and any future PR that needs to modify LDAP objects while remaining compatible with all credential types.
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)
- [X] 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)
Setup guide for the review
| Component | Version / Build |
|---|---|
| Exegol (free) | 3.16 |
| Host OS | Ubuntu 22.04.5 LTS |
| Python | 3.11 (system default inside Exegol 3.16) |
| Target DC | Windows Server 2022 (build 20348) – AD domain AZOX.HACKLAB |
| Test workstation | Windows 10 21H2 (joined to the same domain) |
No additional packages were installed; everything runs with the libraries already shipped in NetExec (ldap3, impacket, pyasn1, Cryptodome).
Screenshots (if appropriate):
The capture shows the --test helper (a thin wrapper that simply calls ldap3_kerberos_login) succeeding with a clear-text password, an NT hash, AES-256 and AES-128 keys, and finally with a ticket pulled from the local ccache :
Checklist:
Insert an "x" inside the brackets for completed and relevant items (do not delete options)
- [X] I have ran Ruff against my changes (via poetry:
poetry run python -m ruff check . --preview, use--fixto automatically fix what it can) - [ ] I have added or updated the
tests/e2e_commands.txtfile 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
- [X] 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)
Hi, first of all thanks for your work!
We have discussed switching to ldap3 instead of impackets ldap implementation already internally a while ago and decided to not switch for several reasons:
- iirc ldap3 does not support encryption/signing without external, non-python packages
- NetExec is build on the impacket implementation, switching to ldap3 would require massive changes
- The dev team and the community knows impacket much better by now compared to ldap3 which makes it harder to debug errors (not the strongest argument, but definitely doesn't make it easier to switch)
Imo if we should need the TGT/TGS of the connection we should implement getter/setter methods in impacket to retrieve them from the connection.
The missing CRUD methods are indeed very annoying, but i think before we implement and establish a different kerberos login method which enables you to do other things than with the standard kerberos login we should try to implement these methods in impacket. Otherwise we will run into situations where we have a different ldap connection than assumed and stuff will fall apart due to missing functions.
In the meantime i would go for the same approach as other modules do that need the CRUD methods: Use the existing credentials in NetExec to establish a new ldap3 connection inside that module.
That are at least my thoughts :)
Hi, first of all thanks for your work!
We have discussed switching to ldap3 instead of impackets ldap implementation already internally a while ago and decided to not switch for several reasons:
iirc ldap3 does not support encryption/signing without external, non-python packages NetExec is build on the impacket implementation, switching to ldap3 would require massive changes The dev team and the community knows impacket much better by now compared to ldap3 which makes it harder to debug errors (not the strongest argument, but definitely doesn't make it easier to switch) Imo if we should need the TGT/TGS of the connection we should implement getter/setter methods in impacket to retrieve them from the connection.
The missing CRUD methods are indeed very annoying, but i think before we implement and establish a different kerberos login method which enables you to do other things than with the standard kerberos login we should try to implement these methods in impacket. Otherwise we will run into situations where we have a different ldap connection than assumed and stuff will fall apart due to missing functions.
In the meantime i would go for the same approach as other modules do that need the CRUD methods: Use the existing credentials in NetExec to establish a new ldap3 connection inside that module.
Thanks for the detailed feedback!
Just to clarify: this PR does not aim to replace Impacket’s LDAP implementation or change how NetExec handles LDAP globally. It simply introduces a utility function (ldap3_kerberos_login) that creates a ldap3 connection when needed — for example, in upcoming features like --targetedkerberoast that require LDAP operations such as modify(), add(), etc.
I completely understand the concerns about moving away from Impacket — but this PR is not proposing such a migration. If it helps, I’m happy to rename or further isolate the function to make it clear that it’s a helper, meant to be used only when needed, without affecting the core behavior.
Hi, first of all thanks for your work! We have discussed switching to ldap3 instead of impackets ldap implementation already internally a while ago and decided to not switch for several reasons: iirc ldap3 does not support encryption/signing without external, non-python packages NetExec is build on the impacket implementation, switching to ldap3 would require massive changes The dev team and the community knows impacket much better by now compared to ldap3 which makes it harder to debug errors (not the strongest argument, but definitely doesn't make it easier to switch) Imo if we should need the TGT/TGS of the connection we should implement getter/setter methods in impacket to retrieve them from the connection. The missing CRUD methods are indeed very annoying, but i think before we implement and establish a different kerberos login method which enables you to do other things than with the standard kerberos login we should try to implement these methods in impacket. Otherwise we will run into situations where we have a different ldap connection than assumed and stuff will fall apart due to missing functions. In the meantime i would go for the same approach as other modules do that need the CRUD methods: Use the existing credentials in NetExec to establish a new ldap3 connection inside that module.
Thanks for the detailed feedback!
Just to clarify: this PR does not aim to replace Impacket’s LDAP implementation or change how NetExec handles LDAP globally. It simply introduces a utility function (ldap3_kerberos_login) that creates a ldap3 connection when needed — for example, in upcoming features like --targetedkerberoast that require LDAP operations such as modify(), add(), etc.
I completely understand the concerns about moving away from Impacket — but this PR is not proposing such a migration. If it helps, I’m happy to rename or further isolate the function to make it clear that it’s a helper, meant to be used only when needed, without affecting the core behavior.
Okay got it! I think until we have CRUD methods in impackets LDAP version it is fine to provide a method to establish a ldap3 LDAP connection which can be used without having to invest time into implementing the ldap3 bind&auth part.
I think we should indeed rename it to something like get_ldap3_connection or something similar, which automatically picks up the supplied credential set and arguments with which we have established the impacket ldap connection. Basically self.get_ldap3_connection() and this internally checks if there are nt/lm/aes hashes or if we use kerberos/ntlm/ccache etc. However, i think we should also respect the --kerberos flag and only authenticate via tickets if netexec is told to.
Furthermore, does this handshake require the gssapi package of linux? Does this work with native python code as you have implemented the TGT/TGS flow manually? Otherwise we might run into problems when running on other platforms such as Windows or MacOS.
Hi, first of all thanks for your work! We have discussed switching to ldap3 instead of impackets ldap implementation already internally a while ago and decided to not switch for several reasons: iirc ldap3 does not support encryption/signing without external, non-python packages NetExec is build on the impacket implementation, switching to ldap3 would require massive changes The dev team and the community knows impacket much better by now compared to ldap3 which makes it harder to debug errors (not the strongest argument, but definitely doesn't make it easier to switch) Imo if we should need the TGT/TGS of the connection we should implement getter/setter methods in impacket to retrieve them from the connection. The missing CRUD methods are indeed very annoying, but i think before we implement and establish a different kerberos login method which enables you to do other things than with the standard kerberos login we should try to implement these methods in impacket. Otherwise we will run into situations where we have a different ldap connection than assumed and stuff will fall apart due to missing functions. In the meantime i would go for the same approach as other modules do that need the CRUD methods: Use the existing credentials in NetExec to establish a new ldap3 connection inside that module.
Thanks for the detailed feedback! Just to clarify: this PR does not aim to replace Impacket’s LDAP implementation or change how NetExec handles LDAP globally. It simply introduces a utility function (ldap3_kerberos_login) that creates a ldap3 connection when needed — for example, in upcoming features like --targetedkerberoast that require LDAP operations such as modify(), add(), etc. I completely understand the concerns about moving away from Impacket — but this PR is not proposing such a migration. If it helps, I’m happy to rename or further isolate the function to make it clear that it’s a helper, meant to be used only when needed, without affecting the core behavior.
Okay got it! I think until we have CRUD methods in impackets LDAP version it is fine to provide a method to establish a ldap3 LDAP connection which can be used without having to invest time into implementing the ldap3 bind&auth part.
I think we should indeed rename it to something like
get_ldap3_connectionor something similar, which automatically picks up the supplied credential set and arguments with which we have established the impacket ldap connection. Basicallyself.get_ldap3_connection()and this internally checks if there are nt/lm/aes hashes or if we use kerberos/ntlm/ccache etc. However, i think we should also respect the--kerberosflag and only authenticate via tickets if netexec is told to.Furthermore, does this handshake require the gssapi package of linux? Does this work with native python code as you have implemented the TGT/TGS flow manually? Otherwise we might run into problems when running on other platforms such as Windows or MacOS.
Got it ! I’ll rename it to get_ldap3_connection() and make it automatically reuse the credentials/args from the Impacket LDAP connection, respecting --kerberos exactly like the classic flow.
It works with both NTLM and Kerberos (TGT/TGS built in pure Python), and does not depend on the system’s gssapi package, I tested successfully on Linux without it, and on Windows. I don’t have a Mac to test, but it should work as it’s platform-independent.
Windows AES :
Windows kcache :
I would name it create_ldap3_connection, not "get", since that could mean it already exists and you are asking to retrieve it.
I’m fine with either naming. It’s up to you.
I’ve updated the PR according to the feedback:
-
Function renamed to create_ldap3_connection as suggested.
-
It now automatically reuses the credentials and arguments from the existing Impacket LDAP connection.
-
Fully respects the --kerberos flag and authentication flow (NTLM/Kerberos/ccache detection).
-
Supports both NTLM and Kerberos in pure Python (no gssapi dependency), tested successfully on Linux and Windows.
-
Works with both LDAP (389) and LDAPS (636), automatically choosing/fallback based on
--portand server requirements.
Let me know if anything else should be adjusted.
Interesting pr :)