导语:操作系统的正常运行都离不开环境变量,而关于环境变量的安全却很少引人关注。本文就将讨论如何在 Windows 中利用环境变量完成不同的攻击。
操作系统的正常运行都离不开环境变量,而关于环境变量的安全却很少引人关注。本文就将讨论如何在 Windows 中利用环境变量完成不同的攻击。
进程环境变量
什么是环境变量?简言之,环境变量就是一组可以被用户或进程直接读写的变量,这个变量可以由用户、程序或者操作系统进行设置,用于保障进程的正常运行。一个典型的例子就是文件系统中的路劲。
Windows 中的环境变量
Windows 中的环境变量可以通过使用 DOS 命令 set 进行读写。
读取环境变量:set <变量>
写入环境变量:set <变量>=<值>
在命令行下单独输入 set 会显示当前可用的所有环境变量。而对于程序来说,则可以直接调用 Kernel32 库中的SetEnvironmentVariable 函数来完成同样的操作。当然,程序设置的环境变量只会对当前进程及其子进程有效。
系统在允许的情况下,会直接将 %变量% 直接替换为变量的值,这种替换称为 “扩展(expansion)”。例如,如果我们在命令行中输入 echo %username%,输出会是扩展后变量的值。另外在使用中,我们调用环境变量和直接使用环境变量的值,得到的效果是一样的。
临时环境变量
就像前面说的,由程序设置的环境变量只对当前进程及其子进程有效。这类环境变量在 Windows 中被称为“临时环境变量(volatile environment)”,一旦进程终止就会从系统中消失的无影无踪。
相对临时环境变量,Windows 中还有另一种环境变量,可以在系统重启后作用于全系统。这种环境变量可以由管理员使用命令 setx 进行设置,或是通过修改以下的注册表键值:
HKEY_CURRENT_USER/Environment
扩展注册表值
Windows 注册表中支持一种类型名为 "REG_EXPAND_SZ" 长度可变的数据字符串,这种数据类型包括程序或服务使用该数据时解析的变量。使用这个扩展的程序会在该值被程序调用前被执行,而这个过程只依赖于注册表中的值无需开发人员跟踪环境变量的值。
扩展 Windows 程序
通过搜索 Windows 注册表,我们会发现大量的程序、库和对象依赖环境变量来指定路径,其中最常用的变量是 "SystemRoot",正常状况下它应当指向 Windows 安装路径 C:Windows。
基于环境变量的攻击场景
场景 1:无注入的 DLL 注入
猜想:
如果某一环境变量控制加载一个 DLL 文件,那么攻击者可能通过修改环境变量来加载其他的 DLL 文件。也就是说,攻击者控制进程下的子进程,都会使用攻击者指定的环境变量。
可行性:
进程加载的 DLL 文件,无论是经过复制、修改或者完全替换都会具有相同的权限。这是一种非常有效的无需使用任何注入技术的 DLL 注入方法。
应用:
实现这一种注入最简单的方法就是使用命令行,一般来说流程类似如下方式:
1. 将 C:/Windows 复制到另一个地方 C:/Wherever 2. 设置环境变量: set SystemRoot=C://Wherever 3. 重启 explorer.exe 进程: taskkill /F /IM explorer.exe C:Windowsexplorer.exe 4. Explorer 重启后会从我们指定的目录中加载少量的 DLL,而攻击者则可以替换这些 DLL 文件并修改执行流程。
注:尽管只有很少几个文件会被加载,但这却是最简单的方法。
场景 2:无注入远程加载 DLL
这个同场景 1 基本相同,除了是加载远程 DLL。在 Windows 中,调用 API 需要指定一个文件或目录的路径,通常也会接受指向远程机器的 UNC 路径。当给定 UNC 路径后,进程会尝试使用 SMB 协议来访问指定的路径。
猜想:
如果攻击者能将 %SystemRoot% 扩展为一个 UNC 网络路径,那么 Windows 则会尝试使用 SMB 协议加载远程的资源。
可行性:
首先需要一台远程加载 DLL 文件的 SMB 服务器,而通常使用 SMB 协议进行访问还需要用户凭证,这些都需要攻击者之前能够解决。
应用:
步骤基本类似场景 1,除了将路径替换为远程 UNC 路径,例如如下路径:
set SystemRoot=127.0.0.1c$Windows
场景 3:启动时加载 DLL
前两个场景的攻击都不是持久的,意味着一旦重启系统甚至重启进程,攻击都将失效。
猜想:
攻击者能够设置一个持久性环境变量,无论系统是否重启都能一直影响系统控制流。
可行性:
在系统启动时加载 DLL 文件,或者满足其他特定条。
应用:
还是基本类似场景 1 的情况,仅仅只是把命令 set 换成 setx:
setx SystemRoot C:Wherever
这样当系统重启后还是会加载攻击者指定目录下的 DLL。
场景 4:提权#1
到目前为止,我们谈论的都是攻击者能够掌控影响当前用户进程运行的环境变量。而如果用户想要执行需要更高权限被 UAC 控制的进程,那么这个进程会被创建为 svchost.exe 的子进程并具有不受用户控制的预定义环境变量。但这里却有个问题,尽管这个子进程是由 svchost.exe 创建的,但它依旧会有一份之前用户控制环境变量的拷贝。
这里绕过 UAC 能否称之为提权,取决于你对提权的看法,但微软官方却不认为这是一个漏洞,尽管他们也确实采取了一系列行动解决这一问题。
猜想:
默认情况下,部分进程无需用户同意即会被赋予较高的权限。一般来说,存在一个微软定义的允许可执行文件直接静默执行的文件列表,这个列表就是微软为了同时兼顾安全与用户体验所采取的一种折衷方法。而攻击者却可以利用这种折衷方法,使操作系统在运行时加载不受信任的 DLL 文件并绕过 UAC 赋予其较高的权限。
可行性:
在默认配置下,是可以在无需用户同意或者不告知用户的情况下实现提权的。
而安全管理员则可能通过更改默认的 UAC 配置来提高安全性,强制 Windows 显示任何请求提升权限的通知。不过,攻击者依旧可以在用户同意后加载恶意 DLL 文件。当然,正常用户是不会同意的,那么就需要掩盖在一些合法的进程之下,Windows 任务管理器(taskmgr.exe)就是一个常见的例子。
应用:
1. 设置同场景 3 一样的环境变量;
2. 以管理员权限执行提权进程,本例中我们使用 lpksetup.exe:
ShellExecute(0,”runas”,”C:WindowsSystem32lpksetup.exe”,NULL,NULL,0);
场景 5:提权#2
在上一个场景中,我们使用的是 ShellExecute 来完成提权操作的,而 Windows 还支持用户调用 COM 对象。关于 COM 对象的概念不在本文讨论范围之列,如果不清楚,你可以把它当作常规的程序、可执行文件或是类库。
猜想:
如果存在一个 COM 对象可以在无需用户同意的情况下实现提权,那么攻击者就可以利用这种机制,而非自己执行命令或者创建进程。
可行性:
存在在无需用户同意的情况下实现提权。
应用:
对于本例,我们选择的对象是 Windows 提供用于设置防火墙的 COM 接口。对象的 CLSID 为 {752438CB-E941-433F-BCB4-8B7D2329F0C8}:
1. 同场景 4/1; 2. CoInitialize();
CoCreateInstance() 给定一个 CLSID, 会返回一个指向 IFwlCpl 接口的指针。IFwlCpl->LaunchAdvancedUI()
以上会让具有高权限的 Windows 管理控制台在进程 svchost.exe 中加载 mmc.exe,这就意味着,会从攻击者控制的目录 C:Wherever 中加载 DLL。
场景 6:命令注入
猜想:
如果要执行的命令包含环境变量参数,那么就可以被扩展成执行多条命令。
可行性:
攻击者可以在环境变量中拼接进要执行的其他命令。
应用:
假设我们使用 notepad.exe 打开一个正常的文本文件(.txt),那么打开命令如下所示:
%SystemRoot%System32NOTEPAD.EXE %1
实际上执行的命令如下:
C:WindowsSystem32NOTEPAD.EXE <filename.txt>
那么,现在让我们先使用以下命令:
setx SystemRoot “C:WindowsSystem32cmd.exe && C:Windows”
此时,我们最初执行的命令就变成了:
C:WindowsSystem32cmd.exe && C:WindowsSystem32NOTEPAD.EXE <filename.txt>
这就意味着,在打开记事本前还打开了CMD窗口,当然这里使用的 && 也可以替换成其他连接符。
场景 7:参数操作
Windows 注册表中可以使用多个百分号('%')来扩展多个参数。
猜想:
两个百分号之间的内容通常被认为是一个环境变量。
可行性:
攻击者可以设置环境变量来将参数内容改变成其他攻击者定义的内容。
应用:
首先新定义一个名为 1" 的环境变量指向其他文件,注意引号需要被转义:
setx "1"," "C:Tempevil.dll","
这样做的结果就是,以后当我们在要运行 .cpl 文件时,都会运行 evil.dll。
场景 8:在说提权
右击 "我的电脑" 选择菜单中的 "管理",此时打开的 "计算机管理" 会具有高权限,但这个过程却不会收到任何 UAC 的提示。
这一操作过程是在注册表中 "Manage" 表项中定义的,注册表路径如下:
HKCRCLSID{20D04FE0-3AEA-1069-A2D8-08002B30309D}shellManagecommand
具体的 "计算机管理" 键值为:
%SystemRoot%system32CompMgmtLauncher.exe
猜想:
使用 CompMgmtLauncher.exe 来实现提权。
可行性:
攻击者可以通过设置 SystemRoot 环境变量来实现提权。
结果:
失败!我们的假设是有问题的,更改路径确实会运行其他的可执行文件,但仍然是以当前权限运行的而没有实现提权操作。
深入研究
那么 CompMgmtLauncher.exe 这个程序在启动时到底是怎么完成提权的呢?
CompMgmtLauncher.exe 实际上执行的是另一个 .lnk 链接文件,可以在开始菜单中的 "Windows管理工具" 中找到:
C:ProgramDataMicrosoftWindowsStart MenuProgramsAdministrative ToolsComputer Management.lnk
这个链接指向了位于 Windows32 下的 mmc.exe,并给定了一个 .msc 文件作为参数,这里是 compmgmt.msc,看上去单独运行 mmc.exe 会收到 UAC 的提示,而当在给定一个特定的 .msc 文件当参数后就不会受到 UAC 的提示了。
猜想:
运行 CompMgmtLauncher.exe 时给定一个 .lnk 文件指针来实现提权。
可行性:
攻击者控制目标 .lnk` 文件来绕过 UAC。
结果:
失败!但有进步,写入目标和 .lnk` 文件时需要更高的权限。
继续研究:
目标文件夹引用了两个环境变量:
ALLUSERSPROFILE=C:ProgramData ProgramData=C:ProgramData
猜想:
CompMgmtLauncher.exe 使用了其中一个环境变量来访问 .lnk` 文件。
可行性:
攻击者可以通过控制其中一个或两个环境变量来操控 .lnk` 文件。
应用:
1. 设置 ProgramData 指向其他 C:ProgramData 以外的文件夹; 2. 创建目录树: MicrosoftWindowsStart MenuProgramsAdministrative Tools 3. 创建一个链接文件(.lnk),其指向的路径前面包含我们要执行的命令; 4. 运行 CompMgmtLauncher.exe 或是点击 "管理"。
结果:
终于成功了!
总结
Windows 中的环境变量不仅可以帮助攻击者在攻击之前进行信息收集,更可以帮助到后续的各种攻击,本文只讨论了 Windows 环境变量的部分利用,更多方法还需要大家不停的探索。
本文使用的 POC Python 脚本可以在这里找到:https://github.com/BreakingMalwareResearch/eleven