123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- #!/usr/bin/env ruby
- ##
- # This module requires Metasploit: https://metasploit.com/download
- # Current source: https://github.com/rapid7/metasploit-framework
- ##
- #
- # This script is intended to assist an exploit developer in deducing what
- # "bad characters" exist for a given input path to a program.
- #
- begin
- msfbase = __FILE__
- while File.symlink?(msfbase)
- msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
- end
- gem 'rex-text'
- $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
- require 'msfenv'
- $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
- require 'rex'
- OutStatus = "[*] "
- OutError = "[-] "
- $args = Rex::Parser::Arguments.new(
- "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ],
- "-h" => [ false, "Help banner" ],
- "-i" => [ true, "Read memory contents from the supplied file path" ],
- "-t" => [ true, "The format that the memory contents are in (empty to list)" ])
- def usage
- $stderr.puts("\n" + " Usage: #{File.basename($0)} <options>\n" + $args.usage)
- exit
- end
- def show_format_list
- $stderr.puts("Supported formats:\n")
- $stderr.puts(" raw raw binary data\n")
- $stderr.puts(" windbg output from windbg's \"db\" command\n")
- $stderr.puts(" gdb output from gdb's \"x/bx\" command\n")
- $stderr.puts(" hex hex bytes like \"\\xFF\\x41\" or \"eb fe\"\n")
- end
- def debug_buffer(name, buf)
- str = "\n#{buf.length} bytes of "
- str << name
- str += ":" if buf.length > 0
- str += "\n\n"
- $stderr.puts str
- if buf.length > 0
- $stderr.puts Rex::Text.to_hex_dump(buf)
- end
- end
- # Input defaults
- badchars = ''
- fmt = 'raw'
- input = $stdin
- # Output
- new_badchars = ''
- # Parse the argument and rock it
- $args.parse(ARGV) { |opt, idx, val|
- case opt
- when "-i"
- begin
- input = File.new(val)
- rescue
- $stderr.puts(OutError + "Failed to open file #{val}: #{$!}")
- exit
- end
- when "-b"
- badchars = Rex::Text.dehex(val)
- when "-t"
- if (val =~ /^(raw|windbg|gdb|hex)$/)
- fmt = val
- else
- if val.nil? or val.length < 1
- show_format_list
- else
- $stderr.puts(OutError + "Invalid format: #{val}")
- end
- exit
- end
- when "-h"
- usage
- end
- }
- if input == $stdin
- $stderr.puts(OutStatus + "Please paste the memory contents in \"" + fmt + "\" format below (end with EOF):\n")
- end
- # Working data set
- from_msf = Rex::Text.charset_exclude(badchars)
- from_dbg = ''
- # Process the input
- from_dbg = input.read
- case fmt
- when "raw"
- # this should already be in the correct format :)
- when "windbg"
- translated = ''
- from_dbg.each_line do |ln|
- translated << ln.chomp[10,47].gsub!(/(-| )/, '')
- end
- from_dbg = Rex::Text.hex_to_raw(translated)
- when "gdb"
- translated = ''
- from_dbg.each_line do |ln|
- translated << ln.chomp.split(':')[1].gsub!(/0x/, '\x').gsub!(/ /, '')
- end
- from_dbg = Rex::Text.hex_to_raw(translated)
- when "hex"
- translated = ''
- from_dbg.each_line do |ln|
- translated << ln.chomp.gsub!(/ /,'')
- end
- from_dbg = Rex::Text.hex_to_raw(translated)
- end
- =begin
- # Uncomment these to debug stuff ..
- debug_buffer("BadChars", badchars)
- debug_buffer("memory contents", from_dbg)
- debug_buffer("Rex::Text.charset_exclude() output", from_msf)
- =end
- # Find differences between the two data sets
- from_msf = from_msf.unpack('C*')
- from_dbg = from_dbg.unpack('C*')
- minlen = from_msf.length
- minlen = from_dbg.length if from_dbg.length < minlen
- (0..(minlen-1)).each do |idx|
- ch1 = from_msf[idx]
- ch2 = from_dbg[idx]
- if ch1 != ch2
- str = "Byte at index 0x%04x differs (0x%02x became 0x%02x)" % [idx, ch1, ch2]
- $stderr.puts OutStatus + str
- new_badchars << ch1
- end
- end
- # show the results
- if new_badchars.length < 1
- $stderr.puts(OutStatus + "All characters matched, no new bad characters discovered.")
- else
- $stderr.puts(OutStatus + "Proposed BadChars: \"" + Rex::Text.to_hex(new_badchars) + "\"")
- end
- rescue SignalException => e
- puts("Aborted! #{e}")
- end
|