PowerSploit 实战指南:使用 Get-DomainDNSRecord 枚举 Active Directory DNS 记录
PowerSploit 实战指南使用 Get-DomainDNSRecord 枚举 Active Directory DNS 记录【免费下载链接】PowerSploitPowerSploit - A PowerShell Post-Exploitation Framework项目地址: https://gitcode.com/gh_mirrors/po/PowerSploit导读Get-DomainDNSRecord是 PowerSploit 框架 Recon 模块PowerView 工具集中用于枚举 Active Directory DNS 区域Zone内全部 DNS 记录的函数。它不依赖nslookup或 DNS 管理控制台而是直接通过 LDAP 查询 Active Directory 集成的 DNS 分区DomainDnsZones能够以纯 PowerShell 方式获取 A、NS、CNAME、PTR、TXT 等记录及其 TTL、更新时间戳等元数据。读完本文你将掌握该函数的完整参数语义、与Get-DomainDNSZone的管道配合用法以及其底层 LDAP 搜索与二进制记录解码原理从而在域内信息收集与横向侦察中高效提取 DNS 资产信息。一、函数概览与定位Get-DomainDNSRecord位于 Recon/PowerView.ps1由 PowerSploit 作者 Will Schroederharmj0y编写采用 BSD 3-Clause 许可证。它在 Recon 模块中与Get-DomainDNSZone配对使用前者枚举 DNS 区域zone后者枚举某个区域下的所有 DNS 记录。在 Recon/README.md 中该函数的定位被描述为Get-DomainDNSRecord- enumerates the Active Directory DNS records for a given zone在安全测试场景下DNS 记录往往能暴露大量有用的侦察信息A 记录可以揭示服务器 IP 与主机名的对应关系SRV、CNAME记录能揭示服务与别名关系TXT记录偶尔包含验证字符串或敏感配置而较新的记录时间戳则有助于判断资产的活跃度。该函数同时被导出到 Recon 模块清单 Recon/Recon.psd1并注册了别名Get-DNSRecord见 Recon/PowerView.ps1因此两种调用方式等价Get-DomainDNSRecord -ZoneName testlab.local Get-DNSRecord -ZoneName testlab.local二、语法与快速上手2.1 完整语法Get-DomainDNSRecord [-ZoneName] String [-Domain String] [-Server String] [-Properties String[]] [-ResultPageSize Int32] [-ServerTimeLimit Int32] [-FindOne] [-Credential PSCredential]其中仅-ZoneName为必填参数其余均可选。2.2 三个官方示例示例 1枚举单个区域的所有记录Get-DomainDNSRecord -ZoneName testlab.local检索testlab.local区域下的全部 DNS 记录。这是最常用的调用方式直接以区域名作为 LDAP 搜索基点。示例 2枚举当前域的所有区域记录Get-DomainDNSZone | Get-DomainDNSRecord先用Get-DomainDNSZone枚举当前域的所有 DNS 区域再通过管道将每个区域的ZoneName传给Get-DomainDNSRecord从而一次性获得整个域的全部 DNS 记录。这正是两个函数设计的配合方式Get-DomainDNSRecord的-ZoneName参数声明了ValueFromPipeline $True与ValueFromPipelineByPropertyName $True因此能直接消费Get-DomainDNSZone输出的ZoneName属性。示例 3跨域枚举Get-DomainDNSZone -Domain dev.testlab.local | Get-DomainDNSRecord -Domain dev.testlab.local指定子域dev.testlab.local先枚举该域的区域再枚举这些区域下的记录。当当前会话不在目标域内、或目标域存在双向信任时可借助-Domain参数指定目标域。三、参数详解以下参数语义均与文档及 Recon/PowerView.ps1 中的Param声明逐一对齐。3.1 -ZoneName必填类型String位置1必填是管道输入支持ByPropertyName与ByValue说明指定要查询记录的区域名例如testlab.local。该区域名可通过Get-DomainDNSZone枚举获得。源码中带[ValidateNotNullOrEmpty()]校验不允许为空字符串。3.2 -Domain类型String默认值当前域说明指定要查询区域的域默认查询当前域。传参后会被转发给底层的Get-DomainSearcher作为 LDAP 搜索的域上下文。3.3 -Server类型String别名DomainController说明指定要绑定搜索的 Active Directory 服务器域控制器例如-Server DC01.testlab.local。不指定时使用当前登录上下文的默认域控制器。3.4 -Properties类型String[]默认值name,distinguishedname,dnsrecord,whencreated,whenchanged说明指定从服务器检索的输出对象属性。源码Recon/PowerView.ps1中的默认值为这 5 个属性。注意若覆盖该参数输出对象的默认字段会随之变化dnsrecord属性是后续二进制解码的数据来源建议保留。3.5 -ResultPageSize类型Int32默认值200取值范围1 ~ 10000源码中带[ValidateRange(1, 10000)]说明设置 LDAP 搜索器对象的PageSize。当域内 DNS 记录数量庞大时分页机制可避免一次性拉取全部结果导致的内存或网络开销。3.6 -ServerTimeLimit类型Int32默认值0文档注释为“默认 120 秒”取值范围1 ~ 10000说明指定服务器端搜索所花费的最大时间。注意文档元数据与源码注释存在差异参数 YAML 块中默认值为0即由服务器端策略决定而源码的.DESCRIPTION注释写着 Default of 120 seconds。实际生效的默认值以Param声明为准未显式赋值时该参数不传入这一点在使用超大区域时需要留意。3.7 -FindOne类型SwitchParameter别名ReturnOne默认值False说明仅返回一个结果对象。对应源码中的分支指定时调用$DNSSearcher.FindOne()否则调用$DNSSearcher.FindAll()见 Recon/PowerView.ps1。适用于验证区域是否存在或快速取样。3.8 -Credential类型PSCredential默认值[Management.Automation.PSCredential]::Empty说明指定用于连接目标域的备用凭据对象例如$cred Get-Credential TESTLAB\alice Get-DomainDNSRecord -ZoneName testlab.local -Credential $cred当当前用户无权限访问目标域 DNS 分区、或处于跨域侦察场景时可通过该参数提供具备读取权限的账户。四、底层原理LDAP 搜索与二进制解码4.1 LDAP 搜索基点的构造Get-DomainDNSRecord的核心搜索逻辑Recon/PowerView.ps1如下$SearcherArguments { LDAPFilter (objectClassdnsNode) SearchBasePrefix DC$($ZoneName),CNMicrosoftDNS,DCDomainDnsZones } $DNSSearcher Get-DomainSearcher SearcherArguments关键点在于LDAP 过滤器(objectClassdnsNode)即只检索dnsNode对象类。在 Active Directory 集成的 DNS 中每个 DNS 记录节点都以dnsNode对象的形式存储在DomainDnsZones应用程序分区中。搜索基点前缀DCZoneName,CNMicrosoftDNS,DCDomainDnsZones把区域名拼接成 LDAP 专有名称DN作为搜索根。例如区域testlab.local对应的搜索基点为DCtestlab.local,CNMicrosoftDNS,DCDomainDnsZones。这些参数连同-Domain、-Server、-Properties、-ResultPageSize、-ServerTimeLimit、-Credential仅当通过$PSBoundParameters显式传入时一起转发给辅助函数 Get-DomainSearcher由后者构造System.DirectoryServices.DirectorySearcher对象。从源码结构看Get-DomainSearcher是 PowerView 中所有Get-Domain*LDAP 查询函数的公共基础设施负责域解析、服务器绑定与搜索选项组装。4.2 结果处理与输出对象组装搜索结果经过以下流水线处理Recon/PowerView.ps1通过 Convert-LDAPProperty 将 LDAP 原始属性集合转换为 PowerShell 对象并挑选name,distinguishedname,dnsrecord,whencreated,whenchanged五个默认字段附加ZoneName注记属性标明记录所属区域将dnsrecord二进制属性交给 Convert-DNSRecord 解码若该属性是ResultPropertyValueCollection集合则取第一个元素——源码中注明 “TODO: handle multiple nested records properly?”即多嵌套记录场景暂未完整处理把解码得到的RecordType、Data、TTL、Age、TimeStamp、UpdatedAtSerial等属性逐一合并进输出对象将对象的类型名插入PowerView.DNSRecord供后续Where-Object按类型筛选。搜索结束后代码会显式调用$Results.dispose()与$DNSSearcher.dispose()释放非托管资源Recon/PowerView.ps1。4.3 二进制 DNS 记录解码器 Convert-DNSRecordActive Directory 将 DNS 记录以二进制 blob 形式存储在dnsrecord属性中无法直接阅读。Convert-DNSRecord负责将其解码为可读字段其二进制布局解析逻辑Recon/PowerView.ps1如下$RDataType [BitConverter]::ToUInt16($DNSRecord, 2) # 记录类型 $UpdatedAtSerial [BitConverter]::ToUInt32($DNSRecord, 8) # 更新序列号 $TTLRaw $DNSRecord[12..15] # TTL 原始字节 [array]::Reverse($TTLRaw) # 反转成大端序 $TTL [BitConverter]::ToUInt32($TTLRaw, 0)偏移 2 处的 16 位整数为记录类型RDataType对应 DNS 标准类型码偏移 8 处的 32 位整数为更新序列号偏移 12~15 的 4 字节 TTL 需要反转字节序网络序转主机序后解析偏移 20 处的 32 位整数为“Age”时间戳若为 0 则标记为[static]静态记录否则以 1601-01-01 为基准累加小时数换算成可读时间Recon/PowerView.ps1。该函数对以下记录类型做了可读解码Recon/PowerView.ps1RDataType记录类型解码方式1A从偏移 24 起取 4 字节拼成 IPv4 地址2NS通过Get-Name解析名称5CNAME通过Get-Name解析别名6SOA输出 Base64 编码的原始数据12PTR通过Get-Name解析反向指针13HINFO输出 Base64 编码的原始数据15MX输出 Base64 编码的原始数据16TXT逐段拼接为字符串28AAAA输出 Base64 编码的原始数据33SRV输出 Base64 编码的原始数据其他UNKNOWN输出 Base64 编码的原始数据其中Get-Name是定义在BEGIN块中的内部辅助函数用于按“长度前缀 分段”的格式还原 DNS 名称Recon/PowerView.ps1。从源码注释可以推断SOA、HINFO、MX、AAAA、SRV等类型目前仅保留原始数据的 Base64 表示尚未实现结构化字段解码代码中留有 TODO: how to implement properly? nested object? 标记这是使用时的已知限制。注Convert-DNSRecord改编自 Michael B. Smith 的 PowerShell DNS 导出脚本源码中保留了原始出处链接说明见 Recon/PowerView.ps1。五、输出对象PowerView.DNSRecord函数标注的输出类型为PowerView.DNSRecord见 Recon/PowerView.ps1。每个输出对象包含以下字段字段来源说明nameLDAP 属性记录节点名称distinguishednameLDAP 属性记录节点的完整 DNdnsrecordLDAP 属性原始二进制记录 blobwhencreatedLDAP 属性对象创建时间whenchangedLDAP 属性对象最后修改时间ZoneName函数附加记录所属的区域名RecordTypeConvert-DNSRecord解码后的记录类型A/NS/CNAME/PTR/TXT 等DataConvert-DNSRecord解码后的记录数据IP、名称、文本等TTLConvert-DNSRecord记录 TTL秒AgeConvert-DNSRecord记录存活年龄小时0 表示静态记录TimeStampConvert-DNSRecord可读时间戳静态记录显示[static]UpdatedAtSerialConvert-DNSRecord区域更新序列号实用的后续处理示例# 提取区域内的所有 A 记录得到 主机名 - IP 映射 Get-DomainDNSRecord -ZoneName testlab.local | Where-Object RecordType -eq A | Select-Object name, Data, TTL # 查找最近变更过的记录排除静态记录 Get-DomainDNSRecord -ZoneName testlab.local | Where-Object { $_.RecordType -eq A -and $_.TimeStamp -ne [static] } | Sort-Object TimeStamp -Descending | Select-Object -First 20六、搭配 Get-DomainDNSZone 的完整侦察链路Get-DomainDNSZoneRecon/PowerView.ps1用于枚举给定域内的 DNS 区域。其实现会执行两次LDAP 搜索第一次以(objectClassdnsZone)为过滤器搜索域根第二次将搜索基点切换到CNMicrosoftDNS,DCDomainDnsZonesRecon/PowerView.ps1两次结果合并后附加ZoneName属性并标记为PowerView.DNSZone类型。因此完整的 DNS 侦察链路为# 第 1 步枚举区域 Get-DomainDNSZone # 第 2 步枚举所有区域的全部记录 Get-DomainDNSZone | Get-DomainDNSRecord # 第 3 步聚焦高价值记录 $all Get-DomainDNSZone | Get-DomainDNSRecord $all | Where-Object RecordType -eq A | Group-Object ZoneName | Select-Object Name, Count $all | Where-Object RecordType -eq SRV # 服务定位记录可能暴露服务资产 $all | Where-Object RecordType -eq TXT # 文本记录可能含敏感配置串使用前提与限制该函数依赖 Active Directory 集成的 DNSAD-integrated DNS因为DomainDnsZones分区只在域控上存在若目标环境的 DNS 托管在非 AD 的第三方 DNS 服务器上LDAP 搜索将无法返回结果执行账户需要对DomainDnsZones分区具备读取权限普通域用户通常即可读取但具体取决于域的 ACL 配置当前域必须能通过 LDAP 解析到目标 DNS 分区跨域查询示例 3需要存在适当的信任关系函数仅做枚举不执行任何写入操作属于侦察类工具使用时应遵循目标环境的授权边界。七、相关资源函数完整实现Recon/PowerView.ps1二进制解码器Recon/PowerView.ps1Convert-DNSRecord区域枚举函数Recon/PowerView.ps1Get-DomainDNSZoneLDAP 搜索基础设施Recon/PowerView.ps1Get-DomainSearcher模块导出清单Recon/Recon.psd1工具集说明Recon/README.md官方文档docs/Recon/Get-DomainDNSRecord.md若需在其他环境中复现可加载 PowerView 后直接调用# 加载 Recon 模块PowerSploit 仓库根目录下 Import-Module ./Recon/Recon.psd1 # 或直接点源 PowerView 脚本 . ./Recon/PowerView.ps1 Get-DomainDNSZone | Get-DomainDNSRecord | Format-Table -AutoSize【免费下载链接】PowerSploitPowerSploit - A PowerShell Post-Exploitation Framework项目地址: https://gitcode.com/gh_mirrors/po/PowerSploit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考