-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for LDAP and LDAPS protocols in ntlmrelayx SOCKS #1825
Add support for LDAP and LDAPS protocols in ntlmrelayx SOCKS #1825
Conversation
hi @b1two . This looks like a relly interesting feature to integrate! thanks!
this was trying pywerview:
any idea what could be happening? |
Hello, thanks for testing this PR. I checked and pywerview actually performs an SMB bind to retrieve the FQDN of the domain prior to performing the action (see https://github.com/the-useless-one/pywerview/blob/973ed7933b5621d88960152bba422e6644327d34/pywerview/requester.py#L353). Since there was no relay available for SMB ( You can either use the For the second option, I did not manage to make it work without the following two patches to impacket.
The patches: diff --git a/impacket/examples/ntlmrelayx/utils/targetsutils.py b/impacket/examples/ntlmrelayx/utils/targetsutils.py
index 7e119d67..6f4fdaa8 100644
--- a/impacket/examples/ntlmrelayx/utils/targetsutils.py
+++ b/impacket/examples/ntlmrelayx/utils/targetsutils.py
@@ -157,7 +157,7 @@ class TargetsProcessor:
# Multirelay feature is disabled, general candidates are attacked just one time
elif multiRelay == False:
for target in self.generalCandidates:
- match = [x for x in self.finishedAttacks if x.hostname == target.netloc]
+ match = [x for x in self.finishedAttacks if x.hostname == target.netloc and x.scheme == target.scheme]
if len(match) == 0:
self.generalCandidates.remove(target)
return target
diff --git a/impacket/nmb.py b/impacket/nmb.py
index 7cf6412a..a7f451d6 100644
--- a/impacket/nmb.py
+++ b/impacket/nmb.py
@@ -686,10 +686,13 @@ class NetBIOSSessionPacket:
return self.type
def rawData(self):
+ trailer = self._trailer
+ if type(self._trailer) != bytes:
+ trailer = self._trailer.getData()
if self.type == NETBIOS_SESSION_MESSAGE:
- data = pack('!BBH', self.type, self.length >> 16, self.length & 0xFFFF) + self._trailer
+ data = pack('!BBH', self.type, self.length >> 16, self.length & 0xFFFF) + trailer
else:
- data = pack('!BBH', self.type, self.flags, self.length) + self._trailer
+ data = pack('!BBH', self.type, self.flags, self.length) + trailer
return data
def set_trailer(self, data): In the end, with the two relays in place, pywerview seems to work fine:
Let me know if that works on your side as well. |
This made me realize that I was building the NTLM challenge message for the client with dummy data, although it works, it may cause issues for tools that rely on the information in this message. I have a (tiny) working patch that, instead of building the whole message from scratch, uses the one that was received from the real server during the relay. I pushed the modifications in another branch: b1two@6c5f97d Do you want me to update this PR to reflect this change? |
@b1two yes please! thank you! |
Hi. I am trying to proxy Bloodhound.py using this PR but it does not fully work. Bloodhound.py requires both port 389 and port 3268 to work so I configure ntlmrelayx to setup SOCKS servers on both ports using your two patches above. However, once Bloodhound.py attempts to use port 3268, ntlmrelayx outputs "...(389) is being used at the moment!" and Bloodhound.py crashes. Any ideas? Thanks! |
@anadrianmanrique Done! No worries, I am quite busy myself these days. @dkjajhqu2h3j I will look into it, but I think that the issue is likely to come from Bloodhound.py trying to use multiple connections to the LDAP server in parallel ( |
@b1two thank you! I was able able to execute successfully pywerview, after your latest changes, by avoiding smb connection. Now, still can't make it work with impacket ldap examples, like GetNPUsers, GetADComputers or GetADUsers. Let me see if I can provide more information about this |
Thanks for testing! I found why the impacket examples did not work with the code: the impacket ldap library specifies the name of the user in the bind request (I am not really sure why) while most (all?) other tools (AD explorer and ldap3 based tools) specify the string "NTLM":
Anyway, I changed the way of identifying bind messages to the inner field of the "authentication" attribute, so that it is more robust. I also modified the way I was handling sockets while forwarding message, it is way faster to detect closed connections now. I also merged the handling of pre-auth LDAP messages:
I ran some tests on several tools, I regrouped the results and remarks below. Let me know if you think of other tools that might be of interest. adidnsdump (https://github.com/dirkjanm/adidnsdump)Tested with LDAP and LDAPs, works fine as-is. LDAP:
LDAPs:
BloodHound.py (https://github.com/dirkjanm/BloodHound.py)Tested with LDAP and LDAPs, needs a patch to prevent mutliple simultaneous LDAP connections. If you want to use other collection methods than (dirty) Patch: diff --git a/bloodhound/ad/authentication.py b/bloodhound/ad/authentication.py
index 91175a4..fa1a9f0 100644
--- a/bloodhound/ad/authentication.py
+++ b/bloodhound/ad/authentication.py
@@ -63,6 +63,7 @@ class ADAuthentication(object):
# KDC for domain of the user - fill with domain first, will be resolved later
self.userdomain_kdc = self.domain
self.auth_method = auth_method
+ ADAuthentication.conn = None
# Kerberos
self.tgt = None
@@ -98,6 +99,8 @@ class ADAuthentication(object):
else:
ldappass = self.password
ldaplogin = '%s\\%s' % (self.userdomain, self.username)
+ if ADAuthentication.conn is not None:
+ return ADAuthentication.conn
conn = Connection(server, user=ldaplogin, auto_referrals=False, password=ldappass, authentication=NTLM, receive_timeout=60, auto_range=True)
bound = False
if self.tgt is not None and self.auth_method in ('kerberos', 'auto'):
@@ -127,6 +130,7 @@ class ADAuthentication(object):
else:
logging.error('Failure to authenticate with LDAP! Error %s' % result['message'])
raise CollectionException('Could not authenticate to LDAP. Check your credentials and LDAP server requirements.')
+ ADAuthentication.conn = conn
return conn
def ldap_kerberos(self, connection, hostname): LDAP:
LDAPs:
Certipy (https://github.com/ly4k/Certipy)Tested with LDAP and LDAPs, works fine as-is. Same remark as Bloodhound.py for the LDAP:
LDAPs:
Impacket (https://github.com/fortra/impacket)Tested with LDAP and LDAPs, works fine as-is. Same remark as Bloodhound.py for the LDAP:
LDAPs:
ldeep (https://github.com/franc-pentest/ldeep)Tested with LDAP and LDAPs, works fine as-is. Note that with LDAP, ldeep tries to use TLS within the connection, which breaks the SOCKS, prefer LDAPs or use the command line option LDAP:
LDAPs:
Active Directory Explorer (https://learn.microsoft.com/en-us/sysinternals/downloads/adexplorer)Tested with LDAP and LDAPs, works as-is for LDAP (using Proxifier to forward in ntlmrelayx SOCKS) ; for LDAPs I did not manage to make it work due to certificate issues, so I used socat in the middle to receive cleartext traffic and forward it encapsulated in TLS to the LDAPs server through ntlmrelayx. LDAPs:
pywerview (https://github.com/the-useless-one/pywerview)Tested with LDAP and LDAPs, works fine as-is. LDAP:
LDAPs:
|
35e8e8d
to
2771660
Compare
@b1two hats off to you!! amazing feature, and amazing PR. We had this in our TODO for 0.13 and I couldn't come in a better moment. We appreciate how well the PR evolved. Merging now |
We will also consider your proposed changes for ### ntlmrelayx in order to be able to relay smb and ldap connections to the same target. |
Great news! Thrilled to see this merged, thank you! (Quick note: I haven't fully tested the changes in ntlmrelayx for simultaneous SMB and LDAP relays, it was more of a "quick and dirty" patch) |
Adds support for LDAP and LDAPS protocols in the SOCKS server of ntlmrelayx.
This allows the use of any tool that works with LDAP(s) through the relay obtained using ntlmrelayx. Specifically, this eliminates the need to reimplement every LDAP attack within the LDAP interactive shell, allowing to use of any available PoC directly through the SOCKS server provided by ntlmrelayx.
Some technical details about the implementation:
impacket/examples/ntlmrelayx/clients/ldaprelayclient.py
) that was not required until now. It simply performs a basic LDAP query to keep the connection alive.It should fix #514.
Please let me know if any adjustments or improvements are needed.
Short usage example: