Skip to content

Commit

Permalink
Reply to generic LDAP messages that comes before authentication and d…
Browse files Browse the repository at this point in the history
…rop unbind LDAP messages
  • Loading branch information
b1two committed Oct 30, 2024
1 parent 6c5f97d commit eaf1ae1
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 5 deletions.
62 changes: 59 additions & 3 deletions impacket/examples/ntlmrelayx/servers/socksplugins/ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from impacket import LOG, ntlm
from impacket.examples.ntlmrelayx.servers.socksserver import SocksRelay
from impacket.ldap.ldap import LDAPSessionError
from impacket.ldap.ldapasn1 import KNOWN_NOTIFICATIONS, LDAPDN, NOTIFICATION_DISCONNECT, BindRequest, BindResponse, LDAPMessage, LDAPString, ResultCode
from impacket.ldap.ldapasn1 import KNOWN_NOTIFICATIONS, LDAPDN, NOTIFICATION_DISCONNECT, BindRequest, BindResponse, SearchRequest, SearchResultEntry, SearchResultDone, LDAPMessage, LDAPString, ResultCode, PartialAttributeList, PartialAttribute, AttributeValue, UnbindRequest
from impacket.ntlm import NTLMSSP_NEGOTIATE_SIGN, NTLMSSP_NEGOTIATE_SEAL

PLUGIN_CLASS = 'LDAPSocksRelay'
Expand Down Expand Up @@ -126,6 +126,53 @@ def skipAuthentication(self):
self.send(bindresponse, message['messageID'])

return True
else:
msg_component = message['protocolOp'].getComponent()
if msg_component.isSameTypeWith(SearchRequest):
# Search request
if msg_component['attributes'][0] == LDAPString('supportedCapabilities'):
response = SearchResultEntry()
response['objectName'] = LDAPDN('')
response['attributes'] = PartialAttributeList()

attribs = PartialAttribute()
attribs.setComponentByName('type', 'supportedCapabilities')
attribs.setComponentByName('vals', univ.SetOf(componentType=AttributeValue()))
# LDAP_CAP_ACTIVE_DIRECTORY_OID
attribs.getComponentByName('vals').setComponentByPosition(0, AttributeValue('1.2.840.113556.1.4.800'))
# LDAP_CAP_ACTIVE_DIRECTORY_V51_OID
attribs.getComponentByName('vals').setComponentByPosition(1, AttributeValue('1.2.840.113556.1.4.1670'))
# LDAP_CAP_ACTIVE_DIRECTORY_LDAP_INTEG_OID
attribs.getComponentByName('vals').setComponentByPosition(2, AttributeValue('1.2.840.113556.1.4.1791'))
# ISO assigned OIDs
attribs.getComponentByName('vals').setComponentByPosition(3, AttributeValue('1.2.840.113556.1.4.1935'))
attribs.getComponentByName('vals').setComponentByPosition(4, AttributeValue('1.2.840.113556.1.4.2080'))
attribs.getComponentByName('vals').setComponentByPosition(5, AttributeValue('1.2.840.113556.1.4.2237'))

response['attributes'].append(attribs)
elif msg_component['attributes'][0] == LDAPString('supportedSASLMechanisms'):
response = SearchResultEntry()
response['objectName'] = LDAPDN('')
response['attributes'] = PartialAttributeList()

attribs = PartialAttribute()
attribs.setComponentByName('type', 'supportedSASLMechanisms')
attribs.setComponentByName('vals', univ.SetOf(componentType=AttributeValue()))
# Force NTLMSSP to avoid parsing every type of authentication
attribs.getComponentByName('vals').setComponentByPosition(0, AttributeValue('NTLM'))

response['attributes'].append(attribs)
else:
raise RuntimeError(f'Received unexpected message: {msg_component["attributes"][0]}')

# Sending message
self.send(response, message['messageID'])
# Sending searchResDone
result_done = SearchResultDone()
result_done['resultCode'] = ResultCode('success')
result_done['matchedDN'] = LDAPDN('')
result_done['diagnosticMessage'] = LDAPString('')
self.send(result_done, message['messageID'])

def recv(self):
'''Receive LDAP messages during the SOCKS client LDAP authentication.'''
Expand Down Expand Up @@ -216,13 +263,23 @@ def recv_from_send_to(self, recv_from: socket.socket, send_to: socket.socket, re
'''

while not self.stop_event.is_set():
is_ready, a, b = select.select([recv_from], [], [], 1.0)
is_ready, a, b = select.select([recv_from], [], [], 0.01)

if not is_ready:
continue

try:
data = recv_from.recv(LDAPSocksRelay.MSG_SIZE)
try:
message, remaining = decoder.decode(data, asn1Spec=LDAPMessage())
msg_component = message['protocolOp'].getComponent()
if msg_component.isSameTypeWith(UnbindRequest):
# Do not forward unbind requests, otherwise we would loose the SOCKS
continue
except Exception as e:
# Is probably not an unbind LDAP message
pass

except Exception:
if recv_from_is_server:
self.server_is_gone = True
Expand All @@ -247,4 +304,3 @@ def recv_from_send_to(self, recv_from: socket.socket, send_to: socket.socket, re

self.stop_event.set()
return

12 changes: 10 additions & 2 deletions impacket/examples/ntlmrelayx/servers/socksplugins/ldaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,21 @@ def recv_from_send_to(self, recv_from, send_to, recv_from_is_server: bool):
'''

while not self.stop_event.is_set():
if recv_from.pending() == 0 and not select.select([recv_from], [], [], 1.0)[0]:
if recv_from.pending() == 0 and not select.select([recv_from], [], [], 0.01)[0]:
# No data ready to be read from recv_from
continue

try:
data = recv_from.recv(LDAPSocksRelay.MSG_SIZE)
try:
message, remaining = decoder.decode(data, asn1Spec=LDAPMessage())
msg_component = message['protocolOp'].getComponent()
if msg_component.isSameTypeWith(UnbindRequest):
# Do not forward unbind requests, otherwise we would loose the SOCKS
continue
except Exception as e:
# Is probably not an unbind LDAP message
pass
except Exception:
if recv_from_is_server:
self.server_is_gone = True
Expand All @@ -69,4 +78,3 @@ def recv_from_send_to(self, recv_from, send_to, recv_from_is_server: bool):

self.stop_event.set()
return

0 comments on commit eaf1ae1

Please sign in to comment.