udfx.git

udfx

~
git clone https://github.com/debuman/udfx.git

Get-ADDnsRecords.ps1 (12486B)

      1 <#
      2 .SYNOPSIS
      3     UDFX - Ultimate Domain Finder X
      4     AD 통합 DNS 레코드를 LDAP으로 직접 조회하는 에어갭 환경용 도구
      5 .DESCRIPTION
      6     도메인 사용자 권한으로 Active Directory에 저장된 DNS 레코드를 조회합니다.
      7     인터넷 연결 없이 에어갭 환경에서 사용 가능합니다.
      8 .EXAMPLE
      9     .\Get-ADDnsRecords.ps1 -Domain corp.example.com
     10     .\Get-ADDnsRecords.ps1 -Domain corp.example.com -Output results
     11 .NOTES
     12     Version: 1.0.0
     13     Author: UDFX Project
     14 #>
     15 
     16 param(
     17     [Parameter(Mandatory=$true)]
     18     [string]$Domain,
     19 
     20     [string]$Output = "dns_records",
     21 
     22     [switch]$ShowDebug
     23 )
     24 
     25 $banner = @"
     26 
     27     __  ______  _________  __
     28    / / / / __ \/ ____/ |/ /
     29   / / / / / / / /_   |   /
     30  / /_/ / /_/ / __/  /   |
     31  \____/_____/_/    /_/|_|
     32 
     33  ╔═══════════════════════════════════════════════════╗
     34  ║  Ultimate Domain Finder X                         ║
     35  ║  Air-Gap AD DNS Enumeration Tool v1.0             ║
     36  ╠═══════════════════════════════════════════════════╣
     37  ║  [*] LDAP-based AD DNS Record Extraction          ║
     38  ║  [*] No Internet Connection Required              ║
     39  ║  [*] Domain User Privileges Only                  ║
     40  ╚═══════════════════════════════════════════════════╝
     41 
     42 "@
     43 
     44 Write-Host $banner -ForegroundColor Cyan
     45 
     46 # 루트 도메인 추출 함수
     47 function Get-RootDomain {
     48     param([string]$domain)
     49 
     50     $parts = $domain.Split(".")
     51     if ($parts.Count -lt 2) { return $domain }
     52 
     53     # .co.kr, .or.kr, .go.kr, .ac.kr 등 3단계 TLD 처리
     54     if ($parts.Count -ge 3) {
     55         $sld = $parts[-2]
     56         $tld = $parts[-1]
     57         if ($tld -eq "kr" -and $sld -match "^(co|or|go|ac|ne|re|pe|hs|ms|es|sc|kg)$") {
     58             return "$($parts[-3]).$($parts[-2]).$($parts[-1])"
     59         }
     60         if ($tld -eq "uk" -and $sld -match "^(co|org|ac|gov)$") {
     61             return "$($parts[-3]).$($parts[-2]).$($parts[-1])"
     62         }
     63         if ($tld -eq "jp" -and $sld -match "^(co|or|ac|go|ne)$") {
     64             return "$($parts[-3]).$($parts[-2]).$($parts[-1])"
     65         }
     66     }
     67 
     68     # 일반 도메인: example.com, example.io
     69     return "$($parts[-2]).$($parts[-1])"
     70 }
     71 
     72 # DNS 이름 파싱 함수
     73 function Get-DnsNameFromRecord {
     74     param(
     75         [byte[]]$data,
     76         [int]$offset
     77     )
     78 
     79     $nameParts = @()
     80     $pos = $offset
     81 
     82     try {
     83         while ($pos -lt $data.Length) {
     84             $len = $data[$pos]
     85             if ($len -eq 0) { break }
     86             if ($len -gt 63) { break }
     87             if (($pos + 1 + $len) -gt $data.Length) { break }
     88 
     89             $label = [System.Text.Encoding]::ASCII.GetString($data, $pos + 1, $len)
     90             $nameParts += $label
     91             $pos += $len + 1
     92         }
     93     } catch {
     94         return "unknown"
     95     }
     96 
     97     if ($nameParts.Count -eq 0) { return "" }
     98     return ($nameParts -join ".")
     99 }
    100 
    101 # DNS 레코드 변환 함수
    102 function Convert-DNSRecord {
    103     param([byte[]]$RecordData)
    104 
    105     if ($RecordData.Length -lt 24) { return $null }
    106 
    107     $recordType = [BitConverter]::ToUInt16($RecordData, 2)
    108 
    109     $typeName = switch ($recordType) {
    110         1 { "A" }
    111         2 { "NS" }
    112         5 { "CNAME" }
    113         6 { "SOA" }
    114         12 { "PTR" }
    115         15 { "MX" }
    116         16 { "TXT" }
    117         28 { "AAAA" }
    118         33 { "SRV" }
    119         default { "TYPE$recordType" }
    120     }
    121 
    122     $result = @{
    123         Type = $typeName
    124         TypeId = $recordType
    125         Data = ""
    126     }
    127 
    128     try {
    129         switch ($recordType) {
    130             1 { # A Record
    131                 if ($RecordData.Length -ge 28) {
    132                     $ip = "{0}.{1}.{2}.{3}" -f $RecordData[24], $RecordData[25], $RecordData[26], $RecordData[27]
    133                     $result.Data = $ip
    134                 }
    135             }
    136             28 { # AAAA Record
    137                 if ($RecordData.Length -ge 40) {
    138                     $ipv6Bytes = $RecordData[24..39]
    139                     $ip = [System.Net.IPAddress]::new($ipv6Bytes).ToString()
    140                     $result.Data = $ip
    141                 }
    142             }
    143             2 { # NS Record
    144                 $result.Data = Get-DnsNameFromRecord $RecordData 24
    145             }
    146             5 { # CNAME Record
    147                 $result.Data = Get-DnsNameFromRecord $RecordData 24
    148             }
    149             12 { # PTR Record
    150                 $result.Data = Get-DnsNameFromRecord $RecordData 24
    151             }
    152             15 { # MX Record
    153                 if ($RecordData.Length -ge 28) {
    154                     $priority = [BitConverter]::ToUInt16($RecordData, 24)
    155                     $exchange = Get-DnsNameFromRecord $RecordData 26
    156                     $result.Data = "[$priority] $exchange"
    157                 }
    158             }
    159             6 { # SOA Record
    160                 $result.Data = Get-DnsNameFromRecord $RecordData 24
    161             }
    162             16 { # TXT Record
    163                 if ($RecordData.Length -gt 24) {
    164                     $txtLen = $RecordData[24]
    165                     if ($txtLen -gt 0 -and $RecordData.Length -ge (25 + $txtLen)) {
    166                         $result.Data = [System.Text.Encoding]::ASCII.GetString($RecordData, 25, $txtLen)
    167                     }
    168                 }
    169             }
    170             33 { # SRV Record
    171                 if ($RecordData.Length -ge 32) {
    172                     $priority = [BitConverter]::ToUInt16($RecordData, 24)
    173                     $weight = [BitConverter]::ToUInt16($RecordData, 26)
    174                     $port = [BitConverter]::ToUInt16($RecordData, 28)
    175                     $target = Get-DnsNameFromRecord $RecordData 30
    176                     $result.Data = "${target}:$port (pri:$priority wt:$weight)"
    177                 }
    178             }
    179             default {
    180                 $result.Data = "Binary (type:$recordType)"
    181             }
    182         }
    183     } catch {
    184         $result.Data = "Parse error"
    185     }
    186 
    187     return $result
    188 }
    189 
    190 # 메인 실행
    191 Write-Host "[*] Target Domain: $Domain" -ForegroundColor White
    192 Write-Host ""
    193 
    194 # 도메인 DN 생성
    195 $domainParts = $Domain.Split(".")
    196 $domainDN = ($domainParts | ForEach-Object { "DC=$_" }) -join ","
    197 
    198 Write-Host "[*] Searching for DNS Zones in Active Directory..." -ForegroundColor Cyan
    199 
    200 # DNS 영역 검색
    201 $zones = @()
    202 $searchBase = "LDAP://DC=DomainDnsZones,$domainDN"
    203 
    204 try {
    205     $searcher = New-Object DirectoryServices.DirectorySearcher
    206     $searcher.SearchRoot = [ADSI]$searchBase
    207     $searcher.Filter = "(objectClass=dnsZone)"
    208     $searcher.PageSize = 1000
    209 
    210     $results = $searcher.FindAll()
    211     foreach ($result in $results) {
    212         $zoneName = $result.Properties["name"][0]
    213         if ($zoneName -and $zoneName -ne "RootDNSServers") {
    214             $zones += @{
    215                 Name = $zoneName
    216                 Path = $result.Path
    217             }
    218         }
    219     }
    220 } catch {
    221     Write-Host "[!] Error accessing DNS zones: $_" -ForegroundColor Red
    222     exit 1
    223 }
    224 
    225 if ($zones.Count -eq 0) {
    226     Write-Host "[!] No DNS zones found." -ForegroundColor Red
    227     exit 1
    228 }
    229 
    230 Write-Host "[+] Found $($zones.Count) DNS zone(s)" -ForegroundColor Green
    231 Write-Host ""
    232 
    233 # 모든 레코드 수집
    234 $allRecords = @()
    235 
    236 foreach ($zone in $zones) {
    237     Write-Host "[*] Dumping: $($zone.Name)" -ForegroundColor Cyan
    238 
    239     try {
    240         $zoneSearcher = New-Object DirectoryServices.DirectorySearcher
    241         $zoneSearcher.SearchRoot = [ADSI]$zone.Path
    242         $zoneSearcher.Filter = "(objectClass=dnsNode)"
    243         $zoneSearcher.PageSize = 1000
    244         $zoneSearcher.PropertiesToLoad.AddRange(@("name", "dnsRecord"))
    245 
    246         $nodeResults = $zoneSearcher.FindAll()
    247         $zoneRecordCount = 0
    248 
    249         foreach ($node in $nodeResults) {
    250             $nodeName = $node.Properties["name"][0]
    251             $dnsRecords = $node.Properties["dnsRecord"]
    252 
    253             if ($nodeName -eq "@") {
    254                 $fqdn = $zone.Name
    255             } else {
    256                 $fqdn = "$nodeName.$($zone.Name)"
    257             }
    258 
    259             foreach ($recordData in $dnsRecords) {
    260                 $parsed = Convert-DNSRecord -RecordData $recordData
    261                 if ($parsed -and $parsed.Data -and $parsed.Type -ne "SOA") {
    262                     # Zone 유형 분류
    263                     $zoneType = "External"
    264                     if ($zone.Name -eq $Domain) {
    265                         $zoneType = "Primary"
    266                     } elseif ($zone.Name.EndsWith(".$Domain")) {
    267                         $zoneType = "Subdomain"
    268                     }
    269 
    270                     # 루트 도메인 추출
    271                     $rootDomain = Get-RootDomain $zone.Name
    272 
    273                     $record = [PSCustomObject]@{
    274                         Domain = $fqdn
    275                         RecordType = $parsed.Type
    276                         Value = $parsed.Data
    277                         Zone = $zone.Name
    278                         ZoneType = $zoneType
    279                         RootDomain = $rootDomain
    280                     }
    281                     $allRecords += $record
    282                     $zoneRecordCount++
    283 
    284                     if ($ShowDebug) {
    285                         Write-Host "    [$($parsed.Type)] $fqdn -> $($parsed.Data)" -ForegroundColor Gray
    286                     }
    287                 }
    288             }
    289         }
    290 
    291         Write-Host "    Found $zoneRecordCount records" -ForegroundColor Gray
    292 
    293     } catch {
    294         Write-Host "    [!] Error: $_" -ForegroundColor Red
    295     }
    296 }
    297 
    298 Write-Host ""
    299 Write-Host "[*] Saving results..." -ForegroundColor Cyan
    300 
    301 # JSON 출력
    302 $jsonOutput = @{
    303     Target = $Domain
    304     ScanTime = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
    305     TotalRecords = $allRecords.Count
    306     ZoneCount = $zones.Count
    307     Zones = $zones | ForEach-Object { $_.Name }
    308     Records = $allRecords
    309 } | ConvertTo-Json -Depth 10
    310 $jsonOutput | Out-File -FilePath "$Output.json" -Encoding UTF8
    311 
    312 # CSV 출력
    313 $allRecords | Export-Csv -Path "$Output.csv" -NoTypeInformation -Encoding UTF8
    314 
    315 # Root Domain CSV 출력 (중복 제거)
    316 $uniqueRootDomains = $allRecords | Select-Object -ExpandProperty RootDomain -Unique | Sort-Object | ForEach-Object {
    317     [PSCustomObject]@{
    318         RootDomain = $_
    319     }
    320 }
    321 $uniqueRootDomains | Export-Csv -Path "${Output}_root_domains.csv" -NoTypeInformation -Encoding UTF8
    322 
    323 # TXT 출력
    324 $txtContent = @"
    325 # AD DNS Record Dump
    326 # Target: $Domain
    327 # Time: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
    328 # Total Records: $($allRecords.Count)
    329 # Zones: $($zones.Count)
    330 #
    331 # Format: RootDomain | ZoneType | Type | Domain | Value | Zone
    332 # =============================================================
    333 
    334 "@
    335 foreach ($record in $allRecords) {
    336     $txtContent += "$($record.RootDomain) | $($record.ZoneType) | $($record.RecordType) | $($record.Domain) | $($record.Value) | $($record.Zone)`n"
    337 }
    338 $txtContent | Out-File -FilePath "$Output.txt" -Encoding UTF8
    339 
    340 Write-Host ""
    341 Write-Host "========== SCAN COMPLETE ==========" -ForegroundColor Green
    342 Write-Host "Total Zones: $($zones.Count)" -ForegroundColor White
    343 Write-Host "Total Records: $($allRecords.Count)" -ForegroundColor White
    344 Write-Host ""
    345 Write-Host "Results saved to:" -ForegroundColor White
    346 Write-Host "    - $Output.json" -ForegroundColor Cyan
    347 Write-Host "    - $Output.csv" -ForegroundColor Cyan
    348 Write-Host "    - ${Output}_root_domains.csv (unique root domains)" -ForegroundColor Cyan
    349 Write-Host "    - $Output.txt" -ForegroundColor Cyan
    350 Write-Host "===================================" -ForegroundColor Green
    351 
    352 # 루트 도메인 목록
    353 Write-Host ""
    354 Write-Host "Root Domains Found:" -ForegroundColor White
    355 $allRecords | Select-Object -ExpandProperty RootDomain -Unique | Sort-Object | ForEach-Object {
    356     Write-Host "    $_" -ForegroundColor Gray
    357 }
    358 
    359 # Zone 유형별 통계
    360 Write-Host ""
    361 Write-Host "Records by Zone Type:" -ForegroundColor White
    362 $allRecords | Group-Object ZoneType | Sort-Object Count -Descending | ForEach-Object {
    363     Write-Host "    $($_.Name): $($_.Count)" -ForegroundColor Gray
    364 }
    365 
    366 # 레코드 타입별 통계
    367 Write-Host ""
    368 Write-Host "Records by Record Type:" -ForegroundColor White
    369 $allRecords | Group-Object RecordType | Sort-Object Count -Descending | ForEach-Object {
    370     Write-Host "    $($_.Name): $($_.Count)" -ForegroundColor Gray
    371 }
    372 
    373 # 상위 도메인 출력
    374 Write-Host ""
    375 Write-Host "Sample Records (first 20):" -ForegroundColor White
    376 $allRecords | Select-Object -First 20 | ForEach-Object {
    377     Write-Host "    [$($_.RecordType)] $($_.Domain) -> $($_.Value)" -ForegroundColor Gray
    378 }