metasploit-framework/tools/exploit/pattern_offset.rb

155 lines
4.3 KiB
Ruby
Raw Normal View History

#!/usr/bin/env ruby
2018-03-20 19:33:34 +08:00
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
msfbase = __FILE__
while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
2016-05-14 11:06:18 +08:00
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
$LOAD_PATH.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
gem 'rex-text'
2016-04-15 00:59:36 +08:00
require 'optparse'
module PatternOffset
class OptsConsole
def self.parse(args)
options = {}
parser = OptionParser.new do |opt|
opt.banner = "Usage: #{__FILE__} [options]\nExample: #{__FILE__} -q Aa3A\n[*] Exact match at offset 9"
2016-04-15 00:59:36 +08:00
opt.separator ''
2016-04-15 03:11:02 +08:00
opt.separator 'Options:'
2016-05-14 11:06:18 +08:00
opt.on('-q', '--query Aa0A', String, "Query to Locate") do |query|
options[:query] = query
2016-04-15 03:11:02 +08:00
end
2016-05-14 10:45:41 +08:00
opt.on('-l', '--length <length>', Integer, "The length of the pattern") do |len|
2016-04-15 00:59:36 +08:00
options[:length] = len
2016-05-14 11:06:18 +08:00
end
2016-04-15 00:59:36 +08:00
2016-05-14 10:45:41 +08:00
opt.on('-s', '--sets <ABC,def,123>', Array, "Custom Pattern Sets") do |sets|
2016-05-14 11:06:18 +08:00
options[:sets] = sets
2016-04-15 00:59:36 +08:00
end
2016-05-14 10:45:41 +08:00
2016-04-15 00:59:36 +08:00
opt.on_tail('-h', '--help', 'Show this message') do
$stdout.puts opt
exit
end
end
2012-11-21 02:30:27 +08:00
2016-04-15 00:59:36 +08:00
parser.parse!(args)
2012-11-21 02:30:27 +08:00
2016-04-15 00:59:36 +08:00
if options.empty?
raise OptionParser::MissingArgument, 'No options set, try -h for usage'
elsif options[:query].nil?
2016-04-15 03:29:57 +08:00
raise OptionParser::MissingArgument, '-q <query> is required'
elsif options[:length].nil? && options[:sets]
2016-04-15 00:59:36 +08:00
raise OptionParser::MissingArgument, '-l <length> is required'
end
2012-11-21 02:30:27 +08:00
2016-04-15 00:59:36 +08:00
options[:sets] = nil unless options[:sets]
options[:length] = 8192 unless options[:length]
2016-05-14 10:45:41 +08:00
2016-04-15 00:59:36 +08:00
options
end
end
2012-11-20 12:13:21 +08:00
2016-05-14 11:06:18 +08:00
class Driver
2016-04-15 00:59:36 +08:00
def initialize
begin
@opts = OptsConsole.parse(ARGV)
rescue OptionParser::ParseError => e
$stderr.puts "[x] #{e.message}"
exit
end
end
2016-05-14 10:45:41 +08:00
2016-04-15 00:59:36 +08:00
def run
require 'msfenv'
require 'msf/core'
require 'msf/base'
require 'rex/text'
2016-04-15 03:11:02 +08:00
query = (@opts[:query])
2016-04-15 00:59:36 +08:00
2016-05-14 11:06:18 +08:00
if query.length >= 8 && query.hex > 0
2016-04-15 03:11:02 +08:00
query = query.hex
2016-04-15 00:59:36 +08:00
# However, you can also specify a four-byte string
2016-05-14 11:06:18 +08:00
elsif query.length == 4
2016-04-15 03:11:02 +08:00
query = query.unpack("V").first
2016-04-15 00:59:36 +08:00
else
2016-05-14 11:06:18 +08:00
# Or even a hex query that isn't 8 bytes long
2016-04-15 03:11:02 +08:00
query = query.to_i(16)
2016-04-15 00:59:36 +08:00
end
2016-04-15 00:59:36 +08:00
buffer = Rex::Text.pattern_create(@opts[:length], @opts[:sets])
2016-04-15 03:11:02 +08:00
offset = Rex::Text.pattern_offset(buffer, query)
2016-04-15 00:59:36 +08:00
2016-05-14 11:06:18 +08:00
# Handle cases where there is no match by looking for "close" matches
unless offset
found = false
$stderr.puts "[*] No exact matches, looking for likely candidates..."
# Look for shifts by a single byte
0.upto(3) do |idx|
0.upto(255) do |c|
nvb = [query].pack("V")
nvb[idx, 1] = [c].pack("C")
nvi = nvb.unpack("V").first
off = Rex::Text.pattern_offset(buffer, nvi)
if off
mle = query - buffer[off, 4].unpack("V").first
mbe = query - buffer[off, 4].unpack("N").first
puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] ) byte offset #{idx}"
found = true
end
end
end
exit! if found
# Look for 16-bit offsets
[0, 2].each do |idx|
0.upto(65535) do |c|
nvb = [query].pack("V")
nvb[idx, 2] = [c].pack("v")
nvi = nvb.unpack("V").first
off = Rex::Text.pattern_offset(buffer, nvi)
if off
mle = query - buffer[off, 4].unpack("V").first
mbe = query - buffer[off, 4].unpack("N").first
puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] )"
found = true
end
end
end
end
2016-04-15 00:59:36 +08:00
while offset
2016-05-14 11:06:18 +08:00
puts "[*] Exact match at offset #{offset}"
offset = Rex::Text.pattern_offset(buffer, query, offset + 1)
end
end
end
2012-11-20 09:42:54 +08:00
end
2016-04-15 00:59:36 +08:00
if __FILE__ == $PROGRAM_NAME
driver = PatternOffset::Driver.new
begin
driver.run
2016-05-14 11:06:18 +08:00
rescue ::StandardError => e
2016-04-15 00:59:36 +08:00
$stderr.puts "[x] #{e.class}: #{e.message}"
$stderr.puts "[*] If necessary, please refer to framework.log for more details."
end
end