Skip to content

Commit 02981b1

Browse files
committed
Remove smb from ldap proto
1 parent 2e1f225 commit 02981b1

2 files changed

Lines changed: 34 additions & 119 deletions

File tree

nxc/protocols/ldap.py

Lines changed: 34 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
from impacket.ldap import ldaptypes
3232
from impacket.ldap import ldapasn1 as ldapasn1_impacket
3333
from impacket.ldap.ldap import LDAPFilterSyntaxError
34-
from impacket.smb import SMB_DIALECT
3534
from impacket.smbconnection import SMBConnection, SessionError
35+
from impacket.ntlm import getNTLMSSPType1
3636

3737
from nxc.config import process_secret, host_info_colors
3838
from nxc.connection import connection
@@ -42,6 +42,7 @@
4242
from nxc.protocols.ldap.gmsa import MSDS_MANAGEDPASSWORD_BLOB
4343
from nxc.protocols.ldap.kerberos import KerberosAttacks
4444
from nxc.parsers.ldap_results import parse_result_attributes
45+
from nxc.helpers.ntlm_parser import parse_challenge
4546

4647
ldap_error_status = {
4748
"1": "STATUS_NOT_SUPPORTED",
@@ -163,25 +164,25 @@ def proto_logger(self):
163164
}
164165
)
165166

166-
def get_ldap_info(self, host):
167+
def create_conn_obj(self):
167168
try:
168169
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
169-
ldap_url = f"{proto}://{host}"
170+
ldap_url = f"{proto}://{self.host}"
170171
self.logger.info(f"Connecting to {ldap_url} with no baseDN")
171172
try:
172-
ldap_connection = ldap_impacket.LDAPConnection(ldap_url, dstIp=self.host)
173-
if ldap_connection:
174-
self.logger.debug(f"ldap_connection: {ldap_connection}")
173+
self.ldap_connection = ldap_impacket.LDAPConnection(ldap_url, dstIp=self.host)
174+
if self.ldap_connection:
175+
self.logger.debug(f"ldap_connection: {self.ldap_connection}")
175176
except SysCallError as e:
176177
if proto == "ldaps":
177178
self.logger.fail(f"LDAPs connection to {ldap_url} failed - {e}")
178179
# https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/enable-ldap-over-ssl-3rd-certification-authority
179180
self.logger.fail("Even if the port is open, LDAPS may not be configured")
180181
else:
181182
self.logger.fail(f"LDAP connection to {ldap_url} failed: {e}")
182-
exit(1)
183+
return False
183184

184-
resp = ldap_connection.search(
185+
resp = self.ldap_connection.search(
185186
scope=ldapasn1_impacket.Scope("baseObject"),
186187
attributes=["defaultNamingContext", "dnsHostName"],
187188
sizeLimit=0,
@@ -208,42 +209,18 @@ def get_ldap_info(self, host):
208209
self.logger.debug("Exception:", exc_info=True)
209210
self.logger.info(f"Skipping item, cannot process due to error {e}")
210211
except OSError:
211-
return [None, None, None]
212+
return False
212213
self.logger.debug(f"Target: {target}; target_domain: {target_domain}; base_dn: {base_dn}")
213-
return [target, target_domain, base_dn]
214-
215-
def get_os_arch(self):
216-
try:
217-
string_binding = rf"ncacn_ip_tcp:{self.host}[135]"
218-
transport = DCERPCTransportFactory(string_binding)
219-
transport.setRemoteHost(self.host)
220-
transport.set_connect_timeout(5)
221-
dce = transport.get_dce_rpc()
222-
if self.args.kerberos:
223-
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
224-
dce.connect()
225-
try:
226-
dce.bind(
227-
MSRPC_UUID_PORTMAP,
228-
transfer_syntax=("71710533-BEBA-4937-8319-B5DBEF9CCC36", "1.0"),
229-
)
230-
except DCERPCException as e:
231-
if str(e).find("syntaxes_not_supported") >= 0:
232-
dce.disconnect()
233-
return 32
234-
else:
235-
dce.disconnect()
236-
return 64
237-
except Exception as e:
238-
self.logger.fail(f"Error retrieving os arch of {self.host}: {e!s}")
239-
240-
return 0
214+
self.target = target
215+
self.targetDomain = target_domain
216+
self.baseDN = base_dn
217+
return True
241218

242219
def get_ldap_username(self):
243220
extended_request = ldapasn1_impacket.ExtendedRequest()
244221
extended_request["requestName"] = "1.3.6.1.4.1.4203.1.11.3" # whoami
245222

246-
response = self.ldapConnection.sendReceive(extended_request)
223+
response = self.ldap_connection.sendReceive(extended_request)
247224
for message in response:
248225
search_result = message["protocolOp"].getComponent()
249226
if search_result["resultCode"] == ldapasn1_impacket.ResultCode("success"):
@@ -254,46 +231,26 @@ def get_ldap_username(self):
254231
return ""
255232

256233
def enum_host_info(self):
257-
self.target, self.targetDomain, self.baseDN = self.get_ldap_info(self.host)
258234
self.baseDN = self.args.base_dn if self.args.base_dn else self.baseDN # Allow overwriting baseDN from args
259235
self.hostname = self.target
260236
self.remoteName = self.target
261237
self.domain = self.targetDomain
262-
# smb no open, specify the domain
263-
if not self.args.no_smb:
264-
self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]
265238

266-
try:
267-
self.conn.login("", "")
268-
except BrokenPipeError as e:
269-
self.logger.fail(f"Broken Pipe Error while attempting to login: {e}")
270-
except Exception as e:
271-
if "STATUS_NOT_SUPPORTED" in str(e):
272-
self.no_ntlm = True
273-
if not self.no_ntlm:
274-
self.hostname = self.conn.getServerName()
275-
self.targetDomain = self.domain = self.conn.getServerDNSDomainName()
276-
self.server_os = self.conn.getServerOS()
277-
self.signing = self.conn.isSigningRequired() if self.smbv1 else self.conn._SMBConnection._Connection["RequireSigning"]
278-
self.os_arch = self.get_os_arch()
279-
self.logger.extra["hostname"] = self.hostname
280-
281-
if not self.domain:
282-
self.domain = self.hostname
283-
if self.args.domain:
284-
self.domain = self.args.domain
285-
if self.args.local_auth:
286-
self.domain = self.hostname
287-
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
288-
289-
try: # noqa: SIM105
290-
# DC's seem to want us to logoff first, windows workstations sometimes reset the connection
291-
self.conn.logoff()
292-
except Exception:
293-
pass
294-
295-
# Re-connect since we logged off
296-
self.create_conn_obj()
239+
ntlm_challenge = None
240+
bindRequest = ldapasn1_impacket.BindRequest()
241+
bindRequest['version'] = 3
242+
bindRequest['name'] = ""
243+
negotiate = getNTLMSSPType1()
244+
bindRequest['authentication']['sicilyNegotiate'] = negotiate.getData()
245+
try:
246+
response = self.ldap_connection.sendReceive(bindRequest)[0]['protocolOp']
247+
ntlm_challenge = bytes(response['bindResponse']['matchedDN'])
248+
except Exception as e:
249+
self.logger.debug(f"Failed to get target {self.host} ntlm challenge, error: {e!s}")
250+
251+
if ntlm_challenge:
252+
ntlm_info = parse_challenge(ntlm_challenge)
253+
self.server_os = ntlm_info["os_version"]
297254

298255
if not self.kdcHost and self.domain:
299256
result = self.resolver(self.domain)
@@ -304,17 +261,10 @@ def enum_host_info(self):
304261

305262
def print_host_info(self):
306263
self.logger.debug("Printing host info for LDAP")
307-
if self.args.no_smb:
308-
self.logger.extra["protocol"] = "LDAP" if self.port == 389 else "LDAPS"
309-
self.logger.extra["port"] = self.port
310-
self.logger.display(f'{self.baseDN} (Hostname: {self.hostname.split(".")[0]}) (domain: {self.domain})')
311-
else:
312-
self.logger.extra["protocol"] = "SMB" if not self.no_ntlm else "LDAP"
313-
self.logger.extra["port"] = "445" if not self.no_ntlm else "389"
314-
signing = colored(f"signing:{self.signing}", host_info_colors[0], attrs=["bold"]) if self.signing else colored(f"signing:{self.signing}", host_info_colors[1], attrs=["bold"])
315-
smbv1 = colored(f"SMBv1:{self.smbv1}", host_info_colors[2], attrs=["bold"]) if self.smbv1 else colored(f"SMBv1:{self.smbv1}", host_info_colors[3], attrs=["bold"])
316-
self.logger.display(f"{self.server_os}{f' x{self.os_arch}' if self.os_arch else ''} (name:{self.hostname}) (domain:{self.targetDomain}) ({signing}) ({smbv1})")
317-
self.logger.extra["protocol"] = "LDAP"
264+
self.logger.extra["protocol"] = "LDAP" if str(self.port) == "389" else "LDAPS"
265+
self.logger.extra["port"] = self.port
266+
self.logger.extra["hostname"] = self.target.split(".")[0].upper()
267+
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain})")
318268

319269
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
320270
self.username = username
@@ -594,40 +544,6 @@ def hash_login(self, domain, username, ntlm_hash):
594544
self.logger.fail(f"{self.domain}\\{self.username}:{process_secret(self.password)} {'Error connecting to the domain, are you sure LDAP service is running on the target?'} \nError: {e}")
595545
return False
596546

597-
def create_smbv1_conn(self):
598-
self.logger.debug("Creating smbv1 connection object")
599-
try:
600-
self.conn = SMBConnection(self.host, self.host, None, 445, preferredDialect=SMB_DIALECT)
601-
self.smbv1 = True
602-
if self.conn:
603-
self.logger.debug("SMBv1 Connection successful")
604-
except OSError as e:
605-
if str(e).find("Connection reset by peer") != -1:
606-
self.logger.debug(f"SMBv1 might be disabled on {self.host}")
607-
return False
608-
except Exception as e:
609-
self.logger.debug(f"Error creating SMBv1 connection to {self.host}: {e}")
610-
return False
611-
return True
612-
613-
def create_smbv3_conn(self):
614-
self.logger.debug("Creating smbv3 connection object")
615-
try:
616-
self.conn = SMBConnection(self.host, self.host, None, 445)
617-
self.smbv1 = False
618-
if self.conn:
619-
self.logger.debug("SMBv3 Connection successful")
620-
except OSError:
621-
return False
622-
except Exception as e:
623-
self.logger.debug(f"Error creating SMBv3 connection to {self.host}: {e}")
624-
return False
625-
626-
return True
627-
628-
def create_conn_obj(self):
629-
return bool(self.args.no_smb or self.create_smbv1_conn() or self.create_smbv3_conn())
630-
631547
def get_sid(self):
632548
self.logger.highlight(f"Domain SID {self.sid_domain}")
633549

nxc/protocols/ldap/proto_args.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ def proto_args(parser, parents):
55
ldap_parser = parser.add_parser("ldap", help="own stuff using LDAP", parents=parents, formatter_class=DisplayDefaultsNotNone)
66
ldap_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
77
ldap_parser.add_argument("--port", type=int, default=389, help="LDAP port")
8-
ldap_parser.add_argument("--no-smb", action="store_true", help="No smb connection")
98

109
dgroup = ldap_parser.add_mutually_exclusive_group()
1110
dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, default=None, help="domain to authenticate to")

0 commit comments

Comments
 (0)