1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <windows.h>
6
7 #include <memory>
8
9 #include <stdlib.h>
10
11 #include "base/process/process_metrics.h"
12 #include "base/stl_util.h"
13 #include "base/win/win_util.h"
14 #include "sandbox/win/src/crosscall_client.h"
15 #include "sandbox/win/src/filesystem_interception.h"
16 #include "sandbox/win/src/ipc_tags.h"
17 #include "sandbox/win/src/named_pipe_interception.h"
18 #include "sandbox/win/src/policy_engine_processor.h"
19 #include "sandbox/win/src/policy_low_level.h"
20 #include "sandbox/win/src/policy_params.h"
21 #include "sandbox/win/src/process_thread_interception.h"
22 #include "sandbox/win/src/registry_interception.h"
23 #include "sandbox/win/src/sandbox.h"
24 #include "sandbox/win/src/sandbox_nt_util.h"
25 #include "sandbox/win/src/sharedmem_ipc_client.h"
26 #include "sandbox/win/tests/common/controller.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 #define BINDNTDLL(name) \
30 name##Function name = reinterpret_cast<name##Function>( \
31 ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name));
32
33 namespace sandbox {
34
35 namespace {
36
37 enum TestId {
38 TESTIPC_NTOPENFILE,
39 TESTIPC_NTCREATEFILE,
40 TESTIPC_CREATETHREAD,
41 TESTIPC_CREATENAMEDPIPEW,
42 TESTIPC_NTOPENKEY,
43 TESTIPC_NTCREATEKEY,
44 TESTIPC_LAST
45 };
46
47 // Helper function to allocate space (on the heap) for policy.
MakePolicyMemory()48 PolicyGlobal* MakePolicyMemory() {
49 const size_t kTotalPolicySz = 4096 * 8;
50 char* mem = new char[kTotalPolicySz];
51 memset(mem, 0, kTotalPolicySz);
52 PolicyGlobal* policy = reinterpret_cast<PolicyGlobal*>(mem);
53 policy->data_size = kTotalPolicySz - sizeof(PolicyGlobal);
54 return policy;
55 }
56
57 // NtOpenKey
DummyNtOpenKey(PHANDLE key,ACCESS_MASK desired_access,POBJECT_ATTRIBUTES object_attributes)58 NTSTATUS WINAPI DummyNtOpenKey(PHANDLE key,
59 ACCESS_MASK desired_access,
60 POBJECT_ATTRIBUTES object_attributes) {
61 return STATUS_ACCESS_DENIED;
62 }
63
TestNtOpenKey()64 void TestNtOpenKey() {
65 NTSTATUS status;
66 UNICODE_STRING path_str;
67 HANDLE handle = INVALID_HANDLE_VALUE;
68 OBJECT_ATTRIBUTES attr;
69 BINDNTDLL(RtlInitUnicodeString);
70
71 RtlInitUnicodeString(&path_str, L"\\??\\leak");
72 InitializeObjectAttributes(&attr, &path_str, OBJ_CASE_INSENSITIVE, nullptr,
73 nullptr);
74
75 status = TargetNtOpenKey(reinterpret_cast<NtOpenKeyFunction>(DummyNtOpenKey),
76 &handle, KEY_READ, &attr);
77 if (NT_SUCCESS(status))
78 CloseHandle(handle);
79 }
80
81 // NtCreateKey
DummyNtCreateKey(PHANDLE key,ACCESS_MASK desired_access,POBJECT_ATTRIBUTES object_attributes,ULONG title_index,PUNICODE_STRING class_name,ULONG create_options,PULONG disposition)82 NTSTATUS WINAPI DummyNtCreateKey(PHANDLE key,
83 ACCESS_MASK desired_access,
84 POBJECT_ATTRIBUTES object_attributes,
85 ULONG title_index,
86 PUNICODE_STRING class_name,
87 ULONG create_options,
88 PULONG disposition) {
89 return STATUS_ACCESS_DENIED;
90 }
91
TestNtCreateKey()92 void TestNtCreateKey() {
93 NTSTATUS status;
94 UNICODE_STRING path_str;
95 HANDLE handle = INVALID_HANDLE_VALUE;
96 OBJECT_ATTRIBUTES attr;
97 BINDNTDLL(RtlInitUnicodeString);
98
99 RtlInitUnicodeString(&path_str, L"\\Registry\\Machine\\BADBAD");
100 InitializeObjectAttributes(&attr, &path_str, OBJ_CASE_INSENSITIVE, nullptr,
101 nullptr);
102
103 ULONG disposition;
104 status =
105 TargetNtCreateKey(reinterpret_cast<NtCreateKeyFunction>(DummyNtCreateKey),
106 &handle, KEY_READ, &attr, 0, nullptr, 0, &disposition);
107 if (NT_SUCCESS(status))
108 CloseHandle(handle);
109 }
110
111 // CreateNamedPipeW
DummyCreateNamedPipeW(LPCWSTR pipe_name,DWORD open_mode,DWORD pipe_mode,DWORD max_instance,DWORD out_buffer_size,DWORD in_buffer_size,DWORD default_timeout,LPSECURITY_ATTRIBUTES security_attributes)112 HANDLE WINAPI DummyCreateNamedPipeW(LPCWSTR pipe_name,
113 DWORD open_mode,
114 DWORD pipe_mode,
115 DWORD max_instance,
116 DWORD out_buffer_size,
117 DWORD in_buffer_size,
118 DWORD default_timeout,
119 LPSECURITY_ATTRIBUTES security_attributes) {
120 return INVALID_HANDLE_VALUE;
121 }
122
TestCreateNamedPipeW()123 void TestCreateNamedPipeW() {
124 HANDLE handle;
125
126 handle = TargetCreateNamedPipeW(
127 reinterpret_cast<CreateNamedPipeWFunction>(DummyCreateNamedPipeW),
128 L"\\??\\leak", PIPE_ACCESS_DUPLEX,
129 PIPE_TYPE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, 1, 0x1000,
130 0x1000, 100, nullptr);
131 if (handle != INVALID_HANDLE_VALUE)
132 CloseHandle(handle);
133 }
134
135 // NtCreateFile
DummyNtCreateFile(PHANDLE file,ACCESS_MASK desired_access,POBJECT_ATTRIBUTES object_attributes,PIO_STATUS_BLOCK io_status,PLARGE_INTEGER allocation_size,ULONG file_attributes,ULONG sharing,ULONG disposition,ULONG options,PVOID ea_buffer,ULONG ea_length)136 NTSTATUS WINAPI DummyNtCreateFile(PHANDLE file,
137 ACCESS_MASK desired_access,
138 POBJECT_ATTRIBUTES object_attributes,
139 PIO_STATUS_BLOCK io_status,
140 PLARGE_INTEGER allocation_size,
141 ULONG file_attributes,
142 ULONG sharing,
143 ULONG disposition,
144 ULONG options,
145 PVOID ea_buffer,
146 ULONG ea_length) {
147 return STATUS_ACCESS_DENIED;
148 }
149
TestNtCreateFile()150 void TestNtCreateFile() {
151 UNICODE_STRING path_str;
152 OBJECT_ATTRIBUTES attr;
153 IO_STATUS_BLOCK iosb;
154 HANDLE handle = INVALID_HANDLE_VALUE;
155 BINDNTDLL(RtlInitUnicodeString);
156 RtlInitUnicodeString(&path_str, L"\\??\\leak");
157 InitializeObjectAttributes(&attr, &path_str, OBJ_CASE_INSENSITIVE, nullptr,
158 nullptr);
159 NTSTATUS ret = TargetNtCreateFile(
160 reinterpret_cast<NtCreateFileFunction>(DummyNtCreateFile), &handle,
161 FILE_READ_DATA, &attr, &iosb, 0, 0,
162 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_OPEN, 0,
163 nullptr, 0);
164 if (NT_SUCCESS(ret))
165 CloseHandle(handle);
166 }
167
168 // NtOpenFile
DummyNtOpenFile(PHANDLE file,ACCESS_MASK desired_access,POBJECT_ATTRIBUTES object_attributes,PIO_STATUS_BLOCK io_status,ULONG sharing,ULONG options)169 NTSTATUS WINAPI DummyNtOpenFile(PHANDLE file,
170 ACCESS_MASK desired_access,
171 POBJECT_ATTRIBUTES object_attributes,
172 PIO_STATUS_BLOCK io_status,
173 ULONG sharing,
174 ULONG options) {
175 return STATUS_ACCESS_DENIED;
176 }
177
TestNtOpenFile()178 void TestNtOpenFile() {
179 UNICODE_STRING path_str;
180 OBJECT_ATTRIBUTES attr;
181 IO_STATUS_BLOCK iosb;
182 HANDLE handle = INVALID_HANDLE_VALUE;
183 BINDNTDLL(RtlInitUnicodeString);
184 RtlInitUnicodeString(&path_str, L"\\??\\leak");
185 InitializeObjectAttributes(&attr, &path_str, OBJ_CASE_INSENSITIVE, nullptr,
186 nullptr);
187 NTSTATUS ret = TargetNtOpenFile(
188 reinterpret_cast<NtOpenFileFunction>(DummyNtOpenFile), &handle,
189 FILE_READ_DATA | SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ, FILE_OPEN);
190 if (NT_SUCCESS(ret))
191 CloseHandle(handle);
192 }
193
194 // CreateThread
DummyCreateThread(LPSECURITY_ATTRIBUTES thread_attributes,SIZE_T stack_size,LPTHREAD_START_ROUTINE start_address,LPVOID parameter,DWORD creation_flags,LPDWORD thread_id)195 HANDLE WINAPI DummyCreateThread(LPSECURITY_ATTRIBUTES thread_attributes,
196 SIZE_T stack_size,
197 LPTHREAD_START_ROUTINE start_address,
198 LPVOID parameter,
199 DWORD creation_flags,
200 LPDWORD thread_id) {
201 return nullptr;
202 }
203
ThreadEntry(LPVOID)204 DWORD WINAPI ThreadEntry(LPVOID) {
205 return 0;
206 }
207
TestCreateThread()208 void TestCreateThread() {
209 HANDLE handle = TargetCreateThread(
210 reinterpret_cast<CreateThreadFunction>(DummyCreateThread), nullptr,
211 SIZE_MAX, ThreadEntry, nullptr, 0, nullptr);
212 if (handle) {
213 WaitForSingleObject(handle, INFINITE);
214 CloseHandle(handle);
215 }
216 }
217
218 // Generates a blank policy where all the rules are ASK_BROKER.
GenerateBlankPolicy()219 PolicyGlobal* GenerateBlankPolicy() {
220 PolicyGlobal* policy = MakePolicyMemory();
221
222 LowLevelPolicy policy_maker(policy);
223
224 for (int i = static_cast<int>(IpcTag::UNUSED);
225 i < static_cast<int>(IpcTag::LAST); i++) {
226 IpcTag service = static_cast<IpcTag>(i);
227 PolicyRule ask_broker(ASK_BROKER);
228 ask_broker.Done();
229 policy_maker.AddRule(service, &ask_broker);
230 }
231
232 policy_maker.Done();
233 return policy;
234 }
235
236 // The Policy structure must be flattened before placing into the policy buffer.
237 // This code is taken from target_process.cc
CopyPolicyToTarget(const void * source,size_t size,void * dest)238 void CopyPolicyToTarget(const void* source, size_t size, void* dest) {
239 if (!source || !size)
240 return;
241 memcpy(dest, source, size);
242 sandbox::PolicyGlobal* policy =
243 reinterpret_cast<sandbox::PolicyGlobal*>(dest);
244
245 size_t offset = reinterpret_cast<size_t>(source);
246
247 for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) {
248 size_t buffer = reinterpret_cast<size_t>(policy->entry[i]);
249 if (buffer) {
250 buffer -= offset;
251 policy->entry[i] = reinterpret_cast<sandbox::PolicyBuffer*>(buffer);
252 }
253 }
254 }
255
256 } // namespace
257
IPC_Leak(int argc,wchar_t ** argv)258 SBOX_TESTS_COMMAND int IPC_Leak(int argc, wchar_t** argv) {
259 if (argc != 1)
260 return SBOX_TEST_FAILED;
261
262 // Replace current target policy with one that forwards all interceptions to
263 // broker.
264 PolicyGlobal* policy = GenerateBlankPolicy();
265 PolicyGlobal* current_policy =
266 (PolicyGlobal*)sandbox::GetGlobalPolicyMemory();
267 CopyPolicyToTarget(policy, policy->data_size + sizeof(PolicyGlobal),
268 current_policy);
269
270 int test = wcstol(argv[0], nullptr, 10);
271
272 static_assert(TESTIPC_NTOPENFILE == 0,
273 "TESTIPC_NTOPENFILE must be first in enum.");
274 if (test < TESTIPC_NTOPENFILE || test >= TESTIPC_LAST)
275 return SBOX_TEST_INVALID_PARAMETER;
276
277 auto test_id = TestId(test);
278
279 switch (test_id) {
280 case TESTIPC_NTOPENFILE:
281 TestNtOpenFile();
282 break;
283 case TESTIPC_NTCREATEFILE:
284 TestNtCreateFile();
285 break;
286 case TESTIPC_CREATETHREAD:
287 TestCreateThread();
288 break;
289 case TESTIPC_CREATENAMEDPIPEW:
290 TestCreateNamedPipeW();
291 break;
292 case TESTIPC_NTOPENKEY:
293 TestNtOpenKey();
294 break;
295 case TESTIPC_NTCREATEKEY:
296 TestNtCreateKey();
297 break;
298 case TESTIPC_LAST:
299 NOTREACHED_NT();
300 break;
301 }
302 // Taken from sandbox_policy_base.cc
303 size_t shared_size = base::GetPageSize() * 2;
304 const size_t channel_size = sandbox::kIPCChannelSize;
305 // Calculate how many channels can fit in the shared memory.
306 shared_size -= offsetof(IPCControl, channels);
307 size_t channel_count = shared_size / (sizeof(ChannelControl) + channel_size);
308 size_t base_start =
309 (sizeof(ChannelControl) * channel_count) + offsetof(IPCControl, channels);
310
311 void* memory = GetGlobalIPCMemory();
312 if (!memory)
313 return SBOX_TEST_FAILED;
314
315 // structure taken from crosscall_params.h
316 struct ipc_internal {
317 uint32_t tag;
318 uint32_t is_in_out;
319 CrossCallReturn answer;
320 };
321
322 auto* ipc_data = reinterpret_cast<ipc_internal*>(
323 reinterpret_cast<char*>(memory) + base_start);
324
325 return base::win::HandleToUint32(ipc_data->answer.handle);
326 }
327
TEST(IPCTest,IPCLeak)328 TEST(IPCTest, IPCLeak) {
329 struct TestData {
330 TestId test_id;
331 const char* test_name;
332 HANDLE expected_result;
333 } test_data[] = {{TESTIPC_NTOPENFILE, "TESTIPC_NTOPENFILE", nullptr},
334 {TESTIPC_NTCREATEFILE, "TESTIPC_NTCREATEFILE", nullptr},
335 {TESTIPC_CREATETHREAD, "TESTIPC_CREATETHREAD", nullptr},
336 {TESTIPC_CREATENAMEDPIPEW, "TESTIPC_CREATENAMEDPIPEW",
337 INVALID_HANDLE_VALUE},
338 {TESTIPC_NTOPENKEY, "TESTIPC_NTOPENKEY", nullptr},
339 {TESTIPC_NTCREATEKEY, "TESTIPC_NTCREATEEY", nullptr}};
340
341 static_assert(base::size(test_data) == TESTIPC_LAST, "Not enough tests.");
342 for (auto test : test_data) {
343 TestRunner runner;
344 EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
345 TargetPolicy::REG_ALLOW_READONLY,
346 L"HKEY_LOCAL_MACHINE\\Software\\*"));
347 std::wstring command = std::wstring(L"IPC_Leak ");
348 command += std::to_wstring(test.test_id);
349 EXPECT_EQ(test.expected_result,
350 base::win::Uint32ToHandle(runner.RunTest(command.c_str())))
351 << test.test_name;
352 }
353 }
354
355 } // namespace sandbox
356