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