@@ -762,51 +762,126 @@ def dc_list(self):
762762 resolv .nameservers = [self .host ]
763763 resolv .timeout = self .args .dns_timeout
764764
765+ # Function to resolve and display hostnames
766+ def resolve_and_display_hostname (name , domain_name = None ):
767+ prefix = f"[{ domain_name } ] " if domain_name else ""
768+ try :
769+ # Resolve using DNS server for A, AAAA, CNAME, PTR, and NS records
770+ for record_type in ["A" , "AAAA" , "CNAME" , "PTR" , "NS" ]:
771+ try :
772+ answers = resolv .resolve (name , record_type , tcp = self .args .dns_tcp )
773+ for rdata in answers :
774+ if record_type in ["A" , "AAAA" ]:
775+ ip_address = rdata .to_text ()
776+ self .logger .highlight (f"{ prefix } { name } = { colored (ip_address , host_info_colors [0 ])} " )
777+ return
778+ elif record_type == "CNAME" :
779+ self .logger .highlight (f"{ prefix } { name } CNAME = { colored (rdata .to_text (), host_info_colors [0 ])} " )
780+ return
781+ elif record_type == "PTR" :
782+ self .logger .highlight (f"{ prefix } { name } PTR = { colored (rdata .to_text (), host_info_colors [0 ])} " )
783+ return
784+ elif record_type == "NS" :
785+ self .logger .highlight (f"{ prefix } { name } NS = { colored (rdata .to_text (), host_info_colors [0 ])} " )
786+ return
787+ except resolver .NXDOMAIN :
788+ self .logger .fail (f"{ prefix } { name } = Host not found (NXDOMAIN)" )
789+ except resolver .Timeout :
790+ self .logger .fail (f"{ prefix } { name } = Connection timed out" )
791+ except resolver .NoAnswer :
792+ self .logger .fail (f"{ prefix } { name } = DNS server did not respond" )
793+ except Exception as e :
794+ self .logger .fail (f"{ prefix } { name } encountered an unexpected error: { e } " )
795+ except Exception as e :
796+ self .logger .fail (f"Skipping item(dNSHostName) { prefix } { name } , error: { e } " )
797+
798+ # Find all domain controllers in the current domain
799+ self .logger .info ("Enumerating Domain Controllers in current domain..." )
765800 search_filter = "(&(objectCategory=computer)(primaryGroupId=516))"
766801 attributes = ["dNSHostName" ]
802+ resp = self .search (search_filter , attributes )
803+ resp_parse = parse_result_attributes (resp )
804+ for item in resp_parse :
805+ if "dNSHostName" in item : # Get dNSHostName attribute
806+ name = item ["dNSHostName" ]
807+ resolve_and_display_hostname (name )
808+
809+ # Find all trusted domains
810+ self .logger .info ("Enumerating Trusted Domains..." )
811+ search_filter = "(objectClass=trustedDomain)"
812+ attributes = ["name" , "trustDirection" , "trustType" , "trustAttributes" , "flatName" ]
767813 resp = self .search (search_filter , attributes , 0 )
768- resp_parsed = parse_result_attributes (resp )
814+ trust_resp_parse = parse_result_attributes (resp )
769815
770- for item in resp_parsed :
771- name = item .get ("dNSHostName" , "" ) # Get dNSHostName attribute or empty string
816+ for trust in trust_resp_parse :
772817 try :
773- # Resolve using DNS server for A, AAAA, CNAME, PTR, and NS records
774- if name :
775- found_record = False # Flag to check if any record is found
776-
777- for record_type in ["A" , "AAAA" , "CNAME" , "PTR" , "NS" ]:
778- if found_record :
779- break # If a record has been found, stop checking further
780-
781- try :
782- answers = resolv .resolve (name , record_type , tcp = self .args .dns_tcp )
783- for rdata in answers :
784- if record_type in ["A" , "AAAA" ]:
785- ip_address = rdata .to_text ()
786- self .logger .highlight (f"{ name } = { colored (ip_address , host_info_colors [0 ])} " )
787- found_record = True # Set flag to true since a record is found
788- elif record_type == "CNAME" :
789- self .logger .highlight (f"{ name } CNAME = { colored (rdata .to_text (), host_info_colors [0 ])} " )
790- found_record = True
791- elif record_type == "PTR" :
792- self .logger .highlight (f"{ name } PTR = { colored (rdata .to_text (), host_info_colors [0 ])} " )
793- found_record = True
794- elif record_type == "NS" :
795- self .logger .highlight (f"{ name } NS = { colored (rdata .to_text (), host_info_colors [0 ])} " )
796- found_record = True
797- except resolv .NXDOMAIN :
798- self .logger .fail (f"{ name } = Host not found (NXDOMAIN)" )
799- except resolv .Timeout :
800- self .logger .fail (f"{ name } = Connection timed out" )
801- except resolv .NoAnswer :
802- self .logger .fail (f"{ name } = DNS server did not respond" )
803- except Exception as e :
804- self .logger .fail (f"{ name } encountered an unexpected error: { e } " )
805- else :
806- self .logger .fail ("dNSHostName value is empty, unable to process." )
818+ trust_name = trust ["name" ]
819+ trust_flat_name = trust ["flatName" ]
820+ trust_direction = int (trust ["trustDirection" ])
821+ trust_type = int (trust ["trustType" ])
822+ trust_attributes = int (trust ["trustAttributes" ])
823+
824+ # See: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e9a2d23c-c31e-4a6f-88a0-6646fdb51a3c
825+ trust_attribute_flags = {
826+ 0x1 : "Non-Transitive" ,
827+ 0x2 : "Uplevel-Only" ,
828+ 0x4 : "Quarantined Domain" ,
829+ 0x8 : "Forest Transitive" ,
830+ 0x10 : "Cross Organization" ,
831+ 0x20 : "Within Forest" ,
832+ 0x40 : "Treat as External" ,
833+ 0x80 : "Uses RC4 Encryption" ,
834+ 0x200 : "Cross Organization No TGT Delegation" ,
835+ 0x800 : "Cross Organization Enable TGT Delegation" ,
836+ 0x2000 : "PAM Trust"
837+ }
838+
839+ # For check if multiple posibble flags, like Uplevel-Only, Treat as External
840+ trust_attributes_text = ", " .join (
841+ text for flag , text in trust_attribute_flags .items ()
842+ if trust_attributes & flag
843+ ) or "Other" # If Trust attrs not known
844+
845+ # Convert trust direction/type to human-readable format
846+ direction_text = {
847+ 0 : "Disabled" ,
848+ 1 : "Inbound" ,
849+ 2 : "Outbound" ,
850+ 3 : "Bidirectional" ,
851+ }[trust_direction ]
852+
853+ trust_type_text = {
854+ 1 : "Windows NT" ,
855+ 2 : "Active Directory" ,
856+ 3 : "Kerberos" ,
857+ 4 : "Unknown" ,
858+ 5 : "Azure Active Directory" ,
859+ }[trust_type ]
860+
861+ self .logger .info (f"Processing trusted domain: { trust_name } ({ trust_flat_name } )" )
862+ self .logger .info (f"Trust type: { trust_type_text } , Direction: { direction_text } , Trust Attributes: { trust_attributes_text } " )
863+
807864 except Exception as e :
808- self .logger .fail ("General Error:" , exc_info = True )
809- self .logger .fail (f"Skipping item(dNSHostName) { name } , error: { e } " )
865+ self .logger .fail (f"Failed { e } in trust entry: { trust } " )
866+
867+ # Only process if it's an Active Directory trust
868+ if int (trust_type ) == 2 :
869+ # Try to find domain controllers in trusted domain using DNS
870+ # Check if we can resolve the trusted domain's DC using DNS
871+ dc_dns_name = f"_ldap._tcp.dc._msdcs.{ trust_name } "
872+ try :
873+ srv_records = resolv .resolve (dc_dns_name , "SRV" , tcp = self .args .dns_tcp )
874+ self .logger .info (f"Found domain controllers for trusted domain { trust_name } via DNS:" )
875+ for srv in srv_records :
876+ dc_hostname = str (srv .target ).rstrip ("." )
877+ self .logger .success (f"Found DC in trusted domain: { colored (dc_hostname , host_info_colors [0 ], attrs = ['bold' ])} " )
878+ self .logger .highlight (f"{ trust_name } -> { direction_text } -> { trust_attributes_text } " )
879+ resolve_and_display_hostname (dc_hostname )
880+ except Exception as e :
881+ self .logger .fail (f"Failed to resolve DCs for { trust_name } via DNS: { e } " )
882+ else :
883+ self .logger .display (f"Skipping non-Active Directory trust '{ trust_name } ' with type: { trust_type_text } and direction: { direction_text } " )
884+ self .logger .info ("Domain Controller enumeration complete." )
810885
811886 def active_users (self ):
812887 if len (self .args .active_users ) > 0 :
0 commit comments