Skip to content

Commit 2790a4b

Browse files
committed
Resolve the CA address via DNS records in LDAP
1 parent 3d20697 commit 2790a4b

File tree

1 file changed

+73
-28
lines changed

1 file changed

+73
-28
lines changed

modules/auxiliary/gather/ldap_esc_vulnerable_cert_finder.rb

Lines changed: 73 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ class MetasploitModule < Msf::Auxiliary
33
include Msf::Auxiliary::Report
44
include Msf::Exploit::Remote::LDAP
55
include Msf::OptionalSession::LDAP
6+
include Rex::Proto::MsDnsp
67
include Rex::Proto::Secauthz
78
include Rex::Proto::LDAP
89

@@ -573,31 +574,37 @@ def find_enrollable_vuln_certificate_templates
573574
allowed_sids = parse_acl(security_descriptor.dacl) if security_descriptor.dacl
574575
next if allowed_sids.empty?
575576

576-
service = report_service({
577-
host: ca_server[:dnshostname][0],
578-
port: 445,
579-
proto: 'tcp',
580-
name: 'AD CS',
581-
info: "AD CS CA name: #{ca_server[:name][0]}"
582-
})
583-
584-
report_note({
585-
data: ca_server[:dn][0].to_s,
586-
service: service,
587-
host: ca_server[:dnshostname][0],
588-
ntype: 'windows.ad.cs.ca.dn'
589-
})
590-
591-
report_host({
592-
host: ca_server[:dnshostname][0],
593-
name: ca_server[:dnshostname][0]
594-
})
595-
596-
ca_server_key = ca_server[:dnshostname][0].to_sym
577+
ca_server_fqdn = ca_server[:dnshostname][0].to_s.downcase
578+
ca_server_ip_address = get_ip_addresses_by_fqdn(ca_server_fqdn)&.first
579+
580+
if ca_server_ip_address
581+
service = report_service({
582+
host: ca_server_ip_address,
583+
port: 445,
584+
proto: 'tcp',
585+
name: 'AD CS',
586+
info: "AD CS CA name: #{ca_server[:name][0]}"
587+
})
588+
589+
report_note({
590+
data: ca_server[:dn][0].to_s,
591+
service: service,
592+
host: ca_server_ip_address,
593+
ntype: 'windows.ad.cs.ca.dn'
594+
})
595+
596+
report_host({
597+
host: ca_server_ip_address,
598+
name: ca_server_fqdn
599+
})
600+
end
601+
602+
ca_server_key = ca_server_fqdn.to_sym
597603
next if @certificate_details[certificate_template][:ca_servers].key?(ca_server_key)
598604

599605
@certificate_details[certificate_template][:ca_servers][ca_server_key] = {
600-
hostname: ca_server[:dnshostname][0].to_s,
606+
fqdn: ca_server_fqdn,
607+
ip_address: ca_server_ip_address,
601608
enrollment_sids: allowed_sids,
602609
name: ca_server[:name][0].to_s,
603610
dn: ca_server[:dn][0].to_s
@@ -644,17 +651,17 @@ def print_vulnerable_cert_info
644651
info = hash[:notes].select { |note| note.start_with?(prefix) }.map { |note| note.delete_prefix(prefix).strip }.join("\n")
645652
info = nil if info.blank?
646653

647-
hash[:ca_servers].each do |dnshostname, ca_server|
654+
hash[:ca_servers].each do |ca_fqdn, ca_server|
648655
service = report_service({
649-
host: dnshostname.to_s,
656+
host: ca_server[:ip_address],
650657
port: 445,
651658
proto: 'tcp',
652659
name: 'AD CS',
653660
info: "AD CS CA name: #{ca_server[:name]}"
654661
})
655662

656663
vuln = report_vuln(
657-
host: dnshostname.to_s,
664+
host: ca_server[:ip_address],
658665
port: 445,
659666
proto: 'tcp',
660667
sname: 'AD CS',
@@ -667,7 +674,7 @@ def print_vulnerable_cert_info
667674
report_note({
668675
data: hash[:dn],
669676
service: service,
670-
host: dnshostname.to_s,
677+
host: ca_fqdn.to_s,
671678
ntype: 'windows.ad.cs.ca.template.dn',
672679
vuln_id: vuln.id
673680
})
@@ -702,8 +709,8 @@ def print_vulnerable_cert_info
702709
end
703710

704711
if hash[:ca_servers].any?
705-
hash[:ca_servers].each do |ca_hostname, ca_hash|
706-
print_good(" Issuing CA: #{ca_hash[:name]} (#{ca_hostname})")
712+
hash[:ca_servers].each do |ca_fqdn, ca_hash|
713+
print_good(" Issuing CA: #{ca_hash[:name]} (#{ca_fqdn})")
707714
print_status(' Enrollment SIDs:')
708715
convert_sids_to_human_readable_name(ca_hash[:enrollment_sids]).each do |sid|
709716
print_status(" * #{highlight_sid(sid)}")
@@ -769,10 +776,48 @@ def get_object_by_sid(object_sid)
769776
object
770777
end
771778

779+
def get_ip_addresses_by_fqdn(host_fqdn)
780+
return @fqdns[host_fqdn] if @fqdns.key?(host_fqdn)
781+
782+
vprint_status("Looking up DNS records for #{host_fqdn} in LDAP.")
783+
hostname, _, domain = host_fqdn.partition('.')
784+
results = query_ldap_server(
785+
"(&(objectClass=dnsNode)(DC=#{ldap_escape_filter(hostname)}))",
786+
%w[dnsRecord],
787+
base_prefix: "DC=#{ldap_escape_filter(domain)},CN=MicrosoftDNS,DC=DomainDnsZones"
788+
)
789+
return nil if results.blank?
790+
791+
ip_addresses = []
792+
results.first[:dnsrecord].each do |packed|
793+
begin
794+
unpacked = MsDnspDnsRecord.read(packed)
795+
rescue ::EOFError
796+
next
797+
rescue ::IOError
798+
next
799+
end
800+
801+
next unless [ DnsRecordType::DNS_TYPE_A, DnsRecordType::DNS_TYPE_AAAA ].include?(unpacked.record_type)
802+
803+
ip_addresses << unpacked.data.to_s
804+
end
805+
806+
@fqdns[host_fqdn] = ip_addresses
807+
if ip_addresses.empty?
808+
print_warning("No A or AAAA DNS records were found for #{host_fqdn} in LDAP.")
809+
else
810+
vprint_status("Found #{ip_addresses.length} IP address#{ip_addresses.length > 1 ? 'es' : ''} via A and AAAA DNS records.")
811+
end
812+
813+
ip_addresses
814+
end
815+
772816
def run
773817
# Define our instance variables real quick.
774818
@base_dn = nil
775819
@ldap_objects = []
820+
@fqdns = {}
776821
@certificate_details = {} # Initialize to empty hash since we want to only keep one copy of each certificate template along with its details.
777822

778823
ldap_connect do |ldap|

0 commit comments

Comments
 (0)