@@ -477,6 +477,136 @@ def set_domain(self, domain):
477477 def as_dict (self ):
478478 return {'credentials' :OrderedDict ({'auth_method' :self .auth_method , 'user' :self .user , 'password' :self .pw , 'domain' :self .domain , 'ticket_file' :self .ticket_file , 'nthash' :self .nthash , 'random_user' :self .random_user })}
479479
480+ class SmbConnection ():
481+ def __init__ (self , target , creds = Credentials (), dialect = None ):
482+ self .target = target
483+ self .creds = creds
484+ self .dialect = dialect
485+ self ._conn = None
486+
487+ self .connect ()
488+
489+ def connect (self ):
490+ self ._conn = smbconnection .SMBConnection (self .target .host , self .target .host , sess_port = self .target .port , timeout = self .target .timeout , preferredDialect = self .dialect )
491+
492+ def login (self ):
493+ creds = self .creds
494+
495+ # Take a backup of the environment, in case we modify it for Kerberos
496+ env = os .environ .copy ()
497+ try :
498+ if creds .ticket_file :
499+ # Currently we let impacket extract user and domain from the ticket
500+ os .environ ['KRB5CCNAME' ] = creds .ticket_file
501+ self ._conn .kerberosLogin ('' , creds .pw , domain = '' , useCache = True )
502+ elif creds .nthash :
503+ self ._conn .login (creds .user , creds .pw , domain = creds .domain , nthash = creds .nthash )
504+ else :
505+ self ._conn .login (creds .user , creds .pw , creds .domain )
506+ except Exception as e :
507+ #FIXME: Might need adjustment
508+ return Result ((None , None ), process_impacket_smb_exception (e , self .target ))
509+ finally :
510+ # Restore environment in any case
511+ os .environ .clear ()
512+ os .environ .update (env )
513+
514+ def close (self ):
515+ self ._conn .close ()
516+
517+ def get_dialect (self ):
518+ return self ._conn .getDialect ()
519+
520+ def is_signing_required (self ):
521+ return self ._conn .isSigningRequired ()
522+
523+ def get_raw (self ):
524+ return self ._conn
525+
526+ def get_server_lanman (self ):
527+ return self ._conn .getSMBServer ().get_server_lanman ()
528+
529+ def get_server_os (self ):
530+ return self ._conn .getSMBServer ().get_server_os ()
531+
532+ def get_server_os_major (self ):
533+ return self ._conn .getServerOSMajor ()
534+
535+ def get_server_os_minor (self ):
536+ return self ._conn .getServerOSMinor ()
537+
538+ def get_server_os_build (self ):
539+ return self ._conn .getServerOSBuild ()
540+
541+ def get_server_domain (self ):
542+ return self ._conn .getServerDomain ()
543+
544+ def get_server_name (self ):
545+ return self ._conn .getServerName ()
546+
547+ def get_server_dns_hostname (self ):
548+ return self ._conn .getServerDNSHostName ()
549+
550+ def get_server_dns_domainname (self ):
551+ return self ._conn .getServerDNSDomainName ()
552+
553+ class DceRpc ():
554+ def __init__ (self , smb_conn ):
555+ self ._smb_conn = smb_conn
556+ self .dce = None
557+ self .filename = None
558+ self .msrpc_uuid = None
559+
560+ if isinstance (self , SAMR ):
561+ self .filename = r'\samr'
562+ self .msrpc_uuid = samr .MSRPC_UUID_SAMR
563+
564+ self ._connect ()
565+
566+ def _connect (self ):
567+ rpctransport = transport .SMBTransport (smb_connection = self ._smb_conn .get_raw (), filename = self .filename , remoteName = self ._smb_conn .target .host )
568+ self .dce = DCERPC_v5 (rpctransport )
569+ self .dce .connect ()
570+ self .dce .bind (self .msrpc_uuid )
571+
572+ class SAMR (DceRpc ):
573+ def __init__ (self , smb_conn = None ):
574+ super ().__init__ (smb_conn )
575+
576+ self ._server_handle = self ._get_server_handle ()
577+
578+ def _get_server_handle (self ):
579+ resp = samr .hSamrConnect (self .dce )
580+ return resp ['ServerHandle' ]
581+
582+ def get_domains (self ):
583+ resp = samr .hSamrEnumerateDomainsInSamServer (self .dce , self ._server_handle )
584+ domains = resp ['Buffer' ]['Buffer' ]
585+ domain_names = []
586+ for domain in domains :
587+ domain_names .append (domain ['Name' ])
588+ return domain_names
589+
590+ def get_domain_handle (self , domain_name ):
591+ resp = samr .hSamrLookupDomainInSamServer (self .dce , self ._server_handle , domain_name )
592+ resp = samr .hSamrOpenDomain (self .dce , serverHandle = self ._server_handle , domainId = resp ['DomainId' ])
593+ return resp ['DomainHandle' ]
594+
595+ def get_domain_password_information (self , domain_handle ):
596+ resp = samr .hSamrQueryInformationDomain2 (self .dce , domainHandle = domain_handle , domainInformationClass = samr .DOMAIN_INFORMATION_CLASS .DomainPasswordInformation )
597+ return resp ['Buffer' ]['Password' ]
598+
599+ def get_domain_lockout_information (self , domain_handle ):
600+ resp = samr .hSamrQueryInformationDomain2 (self .dce , domainHandle = domain_handle , domainInformationClass = samr .DOMAIN_INFORMATION_CLASS .DomainLockoutInformation )
601+ return resp ['Buffer' ]['Lockout' ]
602+
603+ def get_domain_logoff_information (self , domain_handle ):
604+ resp = samr .hSamrQueryInformationDomain2 (self .dce , domainHandle = domain_handle , domainInformationClass = samr .DOMAIN_INFORMATION_CLASS .DomainLogoffInformation )
605+ return resp ['Buffer' ]['Logoff' ]
606+
607+ def query_display_information (self , domain_handle ):
608+ resp = samr .hSamrQueryDisplayInformation (self .dce , domainHandle = domain_handle )
609+ return resp ['Buffer' ]['UserInformation' ]['Buffer' ]
480610
481611class SambaTool ():
482612 '''
@@ -971,7 +1101,7 @@ def check_smb_dialects(self):
9711101 last_supported_dialect = None
9721102 for dialect in smb_dialects :
9731103 try :
974- smb_conn = smbconnection . SMBConnection (self .target . host , self . target . host , sess_port = self . target . port , timeout = self . target . timeout , preferredDialect = dialect )
1104+ smb_conn = SmbConnection (self .target , dialect = dialect )
9751105 smb_conn .close ()
9761106 supported [SMB_DIALECTS [dialect ]] = True
9771107 last_supported_dialect = dialect
@@ -990,10 +1120,10 @@ def check_smb_dialects(self):
9901120 preferred_dialect = last_supported_dialect
9911121
9921122 try :
993- smb_conn = smbconnection . SMBConnection (self .target . host , self . target . host , sess_port = self . target . port , timeout = self . target . timeout , preferredDialect = preferred_dialect )
994- preferred_dialect = smb_conn .getDialect ()
1123+ smb_conn = SmbConnection (self .target , dialect = preferred_dialect )
1124+ preferred_dialect = smb_conn .get_dialect ()
9951125 # Check whether SMB signing is required or optional - since this seems to be a global setting, we check it only for the preferred dialect
996- output ["SMB signing required" ] = smb_conn .isSigningRequired ()
1126+ output ["SMB signing required" ] = smb_conn .is_signing_required ()
9971127 smb_conn .close ()
9981128
9991129 output ["Preferred dialect" ] = SMB_DIALECTS [preferred_dialect ]
@@ -1286,8 +1416,8 @@ def enum_from_smb(self):
12861416
12871417 smb_conn = None
12881418 try :
1289- smb_conn = smbconnection . SMBConnection ( remoteName = self .target . host , remoteHost = self . target . host , sess_port = self . target . port , timeout = self . target . timeout , preferredDialect = self .target .smb_preferred_dialect )
1290- smb_conn .login ("" , "" , "" )
1419+ smb_conn = SmbConnection ( self .target , Credentials (), dialect = self .target .smb_preferred_dialect )
1420+ smb_conn .login ()
12911421 except Exception as e :
12921422 error_msg = process_impacket_smb_exception (e , self .target )
12931423 # STATUS_ACCESS_DENIED is the only error we can safely ignore. It basically tells us that a
@@ -1298,10 +1428,10 @@ def enum_from_smb(self):
12981428 # For SMBv1 we can typically find Domain in the "Session Setup AndX Response" packet.
12991429 # For SMBv2 and later we find additional information like the DNS name and the DNS FQDN.
13001430 try :
1301- smb_domain_info ["NetBIOS domain name" ] = smb_conn .getServerDomain ()
1302- smb_domain_info ["NetBIOS computer name" ] = smb_conn .getServerName ()
1303- smb_domain_info ["FQDN" ] = smb_conn .getServerDNSHostName ().rstrip ('\x00 ' )
1304- smb_domain_info ["DNS domain" ] = smb_conn .getServerDNSDomainName ().rstrip ('\x00 ' )
1431+ smb_domain_info ["NetBIOS domain name" ] = smb_conn .get_server_domain ()
1432+ smb_domain_info ["NetBIOS computer name" ] = smb_conn .get_server_name ()
1433+ smb_domain_info ["FQDN" ] = smb_conn .get_server_dns_hostname ().rstrip ('\x00 ' )
1434+ smb_domain_info ["DNS domain" ] = smb_conn .get_server_dns_domainname ().rstrip ('\x00 ' )
13051435 except :
13061436 pass
13071437
@@ -1591,8 +1721,8 @@ def enum_from_smb(self):
15911721 if self .target .smb1_supported :
15921722 smb_conn = None
15931723 try :
1594- smb_conn = smbconnection . SMBConnection ( remoteName = self .target . host , remoteHost = self . target . host , sess_port = self . target . port , timeout = self . target . timeout , preferredDialect = SMB_DIALECT )
1595- smb_conn .login ("" , "" , "" )
1724+ smb_conn = SmbConnection ( self .target , dialect = SMB_DIALECT )
1725+ smb_conn .login ()
15961726 except Exception as e :
15971727 error_msg = process_impacket_smb_exception (e , self .target )
15981728 if not "STATUS_ACCESS_DENIED" in error_msg :
@@ -1603,11 +1733,11 @@ def enum_from_smb(self):
16031733 os_info ["OS release" ] = "not supported"
16041734
16051735 try :
1606- native_lanman = smb_conn .getSMBServer (). get_server_lanman ()
1736+ native_lanman = smb_conn .get_server_lanman ()
16071737 if native_lanman :
16081738 os_info ["Native LAN manager" ] = f"{ native_lanman } "
16091739
1610- native_os = smb_conn .getSMBServer (). get_server_os ()
1740+ native_os = smb_conn .get_server_os ()
16111741 if native_os :
16121742 os_info ["Native OS" ] = f"{ native_os } "
16131743 match = re .search (r"Windows ([0-9])\.([0-9])" , native_os )
@@ -1623,8 +1753,8 @@ def enum_from_smb(self):
16231753 if not self .target .smb1_only :
16241754 smb_conn = None
16251755 try :
1626- smb_conn = smbconnection . SMBConnection ( remoteName = self .target . host , remoteHost = self . target . host , sess_port = self . target . port , timeout = self . target . timeout , preferredDialect = self .target .smb_preferred_dialect )
1627- smb_conn .login ("" , "" , "" )
1756+ smb_conn = SmbConnection ( self .target , dialect = self .target .smb_preferred_dialect )
1757+ smb_conn .login ()
16281758 except Exception as e :
16291759 error_msg = process_impacket_smb_exception (e , self .target )
16301760 if not "STATUS_ACCESS_DENIED" in error_msg :
@@ -1635,13 +1765,13 @@ def enum_from_smb(self):
16351765 os_info ["Native OS" ] = "not supported"
16361766
16371767 try :
1638- os_major = smb_conn .getServerOSMajor ()
1639- os_minor = smb_conn .getServerOSMinor ()
1768+ os_major = smb_conn .get_server_os_major ()
1769+ os_minor = smb_conn .get_server_os_minor ()
16401770 except :
16411771 pass
16421772
16431773 try :
1644- os_build = smb_conn .getServerOSBuild ()
1774+ os_build = smb_conn .get_server_os_build ()
16451775 if os_build is not None :
16461776 os_info ["OS build" ] = f"{ os_build } "
16471777 if str (os_build ) in OS_RELEASE :
@@ -2350,6 +2480,7 @@ def check_access(self, share):
23502480 return Result ({"mapping" :"denied" , "listing" :"n/a" }, "Mapping: DENIED, Listing: N/A" )
23512481
23522482 if "NT_STATUS_INVALID_INFO_CLASS" in result .retmsg \
2483+ or "NT_STATUS_CONNECTION_REFUSED listing" in result .retmsg \
23532484 or "NT_STATUS_NETWORK_ACCESS_DENIED" in result .retmsg \
23542485 or "NT_STATUS_NOT_A_DIRECTORY" in result .retmsg \
23552486 or "NT_STATUS_NO_SUCH_FILE" in result .retmsg :
0 commit comments