CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/tools/exploit/pattern_offset.rb
Views: 11766
1
#!/usr/bin/env ruby
2
3
##
4
# This module requires Metasploit: https://metasploit.com/download
5
# Current source: https://github.com/rapid7/metasploit-framework
6
##
7
begin
8
msfbase = __FILE__
9
while File.symlink?(msfbase)
10
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
11
end
12
13
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
14
$LOAD_PATH.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
15
16
gem 'rex-text'
17
18
require 'optparse'
19
20
module PatternOffset
21
class OptsConsole
22
def self.parse(args)
23
options = {}
24
parser = OptionParser.new do |opt|
25
opt.banner = "Usage: #{__FILE__} [options]\nExample: #{__FILE__} -q Aa3A\n[*] Exact match at offset 9"
26
opt.separator ''
27
opt.separator 'Options:'
28
29
opt.on('-q', '--query Aa0A', String, "Query to Locate") do |query|
30
options[:query] = query
31
end
32
33
opt.on('-l', '--length <length>', Integer, "The length of the pattern") do |len|
34
options[:length] = len
35
end
36
37
opt.on('-s', '--sets <ABC,def,123>', Array, "Custom Pattern Sets") do |sets|
38
options[:sets] = sets
39
end
40
41
opt.on_tail('-h', '--help', 'Show this message') do
42
$stdout.puts opt
43
exit
44
end
45
end
46
47
parser.parse!(args)
48
49
if options.empty?
50
raise OptionParser::MissingArgument, 'No options set, try -h for usage'
51
elsif options[:query].nil?
52
raise OptionParser::MissingArgument, '-q <query> is required'
53
elsif options[:length].nil? && options[:sets]
54
raise OptionParser::MissingArgument, '-l <length> is required'
55
end
56
57
options[:sets] = nil unless options[:sets]
58
options[:length] = 8192 unless options[:length]
59
60
options
61
end
62
end
63
64
class Driver
65
def initialize
66
begin
67
@opts = OptsConsole.parse(ARGV)
68
rescue OptionParser::ParseError => e
69
$stderr.puts "[x] #{e.message}"
70
exit
71
end
72
end
73
74
def run
75
require 'rex/text'
76
77
query = (@opts[:query])
78
79
if query.length >= 8 && query.hex > 0
80
query = query.hex
81
# However, you can also specify a four-byte string
82
elsif query.length == 4
83
query = query.unpack("V").first
84
else
85
# Or even a hex query that isn't 8 bytes long
86
query = query.to_i(16)
87
end
88
89
buffer = Rex::Text.pattern_create(@opts[:length], @opts[:sets])
90
offset = Rex::Text.pattern_offset(buffer, query)
91
92
# Handle cases where there is no match by looking for "close" matches
93
unless offset
94
found = false
95
$stderr.puts "[*] No exact matches, looking for likely candidates..."
96
97
# Look for shifts by a single byte
98
0.upto(3) do |idx|
99
0.upto(255) do |c|
100
nvb = [query].pack("V")
101
nvb[idx, 1] = [c].pack("C")
102
nvi = nvb.unpack("V").first
103
104
off = Rex::Text.pattern_offset(buffer, nvi)
105
if off
106
mle = query - buffer[off, 4].unpack("V").first
107
mbe = query - buffer[off, 4].unpack("N").first
108
puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] ) byte offset #{idx}"
109
found = true
110
end
111
end
112
end
113
114
exit! if found
115
116
# Look for 16-bit offsets
117
[0, 2].each do |idx|
118
0.upto(65535) do |c|
119
nvb = [query].pack("V")
120
nvb[idx, 2] = [c].pack("v")
121
nvi = nvb.unpack("V").first
122
123
off = Rex::Text.pattern_offset(buffer, nvi)
124
if off
125
mle = query - buffer[off, 4].unpack("V").first
126
mbe = query - buffer[off, 4].unpack("N").first
127
puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] )"
128
found = true
129
end
130
end
131
end
132
end
133
134
while offset
135
puts "[*] Exact match at offset #{offset}"
136
offset = Rex::Text.pattern_offset(buffer, query, offset + 1)
137
end
138
end
139
end
140
end
141
142
if __FILE__ == $PROGRAM_NAME
143
driver = PatternOffset::Driver.new
144
begin
145
driver.run
146
rescue ::StandardError => e
147
$stderr.puts "[x] #{e.class}: #{e.message}"
148
$stderr.puts "[*] If necessary, please refer to framework.log for more details."
149
150
end
151
end
152
rescue SignalException => e
153
puts("Aborted!")
154
end
155
156