Skip to content

Commit 057c25e

Browse files
author
Brent Cook
committed
Land rapid7#6446, Cleanup pattern_create/pattern_offset and document options
2 parents a940481 + 7cfc4d4 commit 057c25e

File tree

2 files changed

+190
-115
lines changed

2 files changed

+190
-115
lines changed

tools/exploit/pattern_create.rb

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,79 @@
11
#!/usr/bin/env ruby
2-
#
3-
# $Id$
4-
# $Revision$
5-
#
62

73
msfbase = __FILE__
84
while File.symlink?(msfbase)
95
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
106
end
117

12-
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
8+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
9+
$LOAD_PATH.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
10+
1311
require 'msfenv'
12+
require 'msf/core'
13+
require 'msf/base'
14+
require 'rex/text'
15+
require 'optparse'
1416

15-
$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
17+
module PatternCreate
18+
class OptsConsole
19+
def self.parse(args)
20+
options = {}
21+
parser = OptionParser.new do |opt|
22+
opt.banner = "Usage: #{__FILE__} [options]\nExample: #{__FILE__} -l 50 -s ABC,def,123\nAd1Ad2Ad3Ae1Ae2Ae3Af1Af2Af3Bd1Bd2Bd3Be1Be2Be3Bf1Bf"
23+
opt.separator ''
24+
opt.separator 'Options:'
25+
opt.on('-l', '--length <length>', Integer, "The length of the pattern") do |len|
26+
options[:length] = len
27+
end
1628

17-
require 'rex'
29+
opt.on('-s', '--sets <ABC,def,123>', Array, "Custom Pattern Sets") do |sets|
30+
options[:sets] = sets
31+
end
1832

19-
if (!(length = ARGV.shift))
20-
$stderr.puts("Usage: #{File.basename($0)} length [set a] [set b] [set c]\n")
21-
exit
22-
end
33+
opt.on_tail('-h', '--help', 'Show this message') do
34+
$stdout.puts opt
35+
exit
36+
end
37+
end
38+
39+
parser.parse!(args)
40+
41+
if options.empty?
42+
raise OptionParser::MissingArgument, 'No options set, try -h for usage'
43+
elsif options[:length].blank? && options[:sets]
44+
raise OptionParser::MissingArgument, '-l <length> is required'
45+
end
46+
47+
options[:sets] = nil unless options[:sets]
2348

24-
# If the user supplied custom sets, use those. Otherwise, use the default
25-
# sets.
26-
sets = ARGV.length > 0 ? ARGV : Rex::Text::DefaultPatternSets
49+
options
50+
end
51+
end
2752

28-
puts Rex::Text.pattern_create(length.to_i, sets)
53+
class Driver
54+
def initialize
55+
begin
56+
@opts = OptsConsole.parse(ARGV)
57+
rescue OptionParser::ParseError => e
58+
$stderr.puts "[x] #{e.message}"
59+
exit
60+
end
61+
end
62+
63+
def run
64+
pattern = Rex::Text.pattern_create(@opts[:length], @opts[:sets])
65+
puts pattern
66+
end
67+
end
68+
end
69+
70+
if __FILE__ == $PROGRAM_NAME
71+
driver = PatternCreate::Driver.new
72+
begin
73+
driver.run
74+
rescue ::StandardError => e
75+
elog("#{e.class}: #{e.message}\n#{e.backtrace * "\n"}")
76+
$stderr.puts "[x] #{e.class}: #{e.message}"
77+
$stderr.puts "[*] If necessary, please refer to framework.log for more details."
78+
end
79+
end

tools/exploit/pattern_offset.rb

Lines changed: 124 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,147 @@
11
#!/usr/bin/env ruby
2-
# $Id$
3-
# $Revision$
42

53
msfbase = __FILE__
64
while File.symlink?(msfbase)
75
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
86
end
97

10-
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
11-
require 'msfenv'
12-
13-
$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
14-
15-
require 'rex'
16-
17-
if ARGV.length < 1
18-
$stderr.puts("Usage: #{File.basename($0)} <search item> <length of buffer>")
19-
$stderr.puts("Default length of buffer if none is inserted: 8192")
20-
$stderr.puts("This buffer is generated by pattern_create() in the Rex library automatically")
21-
exit
22-
end
23-
24-
value = ARGV.shift
25-
len = ARGV.shift || 8192
26-
27-
=begin
8+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
9+
$LOAD_PATH.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
2810

29-
Examples:
30-
31-
$ ./tools/pattern_create.rb 128
32-
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae
33-
34-
$ ./tools/pattern_offset.rb 8Ac9
35-
[*] Exact match at offset 86
36-
37-
$ ./tools/pattern_offset.rb 39634138
38-
[*] Exact match at offset 86
39-
40-
$ ./tools/pattern_offset.rb 0x39634138
41-
[*] Exact match at offset 86
42-
43-
$ ./tools/pattern_offset.rb 0x396341FF
44-
[*] No exact matches, looking for likely candidates...
45-
[+] Possible match at offset 86 (adjusted [ little-endian: 199 | big-endian: 18996934 ] ) byte offset 0
11+
require 'msfenv'
12+
require 'msf/core'
13+
require 'msf/base'
14+
require 'rex/text'
15+
require 'optparse'
16+
17+
module PatternOffset
18+
class OptsConsole
19+
def self.parse(args)
20+
options = {}
21+
parser = OptionParser.new do |opt|
22+
opt.banner = "Usage: #{__FILE__} [options]\nExample: #{__FILE__} -q Aa3A|0x39634138|0xFFFF4138\n[*] Exact match at offset 9"
23+
opt.separator ''
24+
opt.separator 'Options:'
25+
26+
opt.on('-q', '--query Aa0A', String, "Query to Locate") do |query|
27+
options[:query] = query
28+
end
29+
30+
opt.on('-l', '--length <length>', Integer, "The length of the pattern") do |len|
31+
options[:length] = len
32+
end
33+
34+
opt.on('-s', '--sets <ABC,def,123>', Array, "Custom Pattern Sets") do |sets|
35+
options[:sets] = sets
36+
end
37+
38+
opt.on_tail('-h', '--help', 'Show this message') do
39+
$stdout.puts opt
40+
exit
41+
end
42+
end
4643

47-
$ ./tools/pattern_offset.rb 0x3963FFFF
48-
[*] No exact matches, looking for likely candidates...
49-
[+] Possible match at offset 86 (adjusted [ little-endian: 48839 | big-endian: 19045574 ] )
50-
[ snip ]
44+
parser.parse!(args)
5145

52-
$ ./tools/pattern_offset.rb 0xFFFF4138
53-
[*] No exact matches, looking for likely candidates...
54-
[+] Possible match at offset 26 (adjusted [ little-endian: 3332243456 | big-endian: 3351109631 ] )
55-
[+] Possible match at offset 56 (adjusted [ little-endian: 3332177920 | big-endian: 3351109375 ] )
56-
[+] Possible match at offset 86 (adjusted [ little-endian: 3332112384 | big-endian: 3351109119 ] )
57-
[ snip ]
46+
if options.empty?
47+
raise OptionParser::MissingArgument, 'No options set, try -h for usage'
48+
elsif options[:query].blank?
49+
raise OptionParser::MissingArgument, '-q <query> is required'
50+
elsif options[:length].blank? && options[:sets]
51+
raise OptionParser::MissingArgument, '-l <length> is required'
52+
end
5853

59-
=end
54+
options[:sets] = nil unless options[:sets]
55+
options[:length] = 1024 unless options[:length]
6056

57+
options
58+
end
59+
end
6160

61+
class Driver
62+
def initialize
63+
begin
64+
@opts = OptsConsole.parse(ARGV)
65+
rescue OptionParser::ParseError => e
66+
$stderr.puts "[x] #{e.message}"
67+
exit
68+
end
69+
end
6270

63-
# The normal format is a full hexadecimal value: 0x41424344
64-
if (value.length >= 8 and value.hex > 0)
65-
value = value.hex
66-
# However, you can also specify a four-byte string
67-
elsif (value.length == 4)
68-
value = value.unpack("V").first
69-
else
70-
# Or even a hex value that isn't 8 bytes long
71-
value = value.to_i(16)
72-
end
71+
def run
72+
query = (@opts[:query])
73+
74+
if query.length >= 8 && query.hex > 0
75+
query = query.hex
76+
# However, you can also specify a four-byte string
77+
elsif query.length == 4
78+
query = query.unpack("V").first
79+
else
80+
# Or even a hex query that isn't 8 bytes long
81+
query = query.to_i(16)
82+
end
7383

74-
buffer = Rex::Text.pattern_create(len.to_i)
75-
offset = Rex::Text.pattern_offset(buffer, value)
76-
77-
# Handle cases where there is no match by looking for "close" matches
78-
unless offset
79-
found = false
80-
$stderr.puts "[*] No exact matches, looking for likely candidates..."
81-
82-
# Look for shifts by a single byte
83-
0.upto(3) do |idx|
84-
0.upto(255) do |c|
85-
nvb = [value].pack("V")
86-
nvb[idx, 1] = [c].pack("C")
87-
nvi = nvb.unpack("V").first
88-
89-
off = Rex::Text.pattern_offset(buffer, nvi)
90-
if off
91-
mle = value - buffer[off,4].unpack("V").first
92-
mbe = value - buffer[off,4].unpack("N").first
93-
puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] ) byte offset #{idx}"
94-
found = true
84+
buffer = Rex::Text.pattern_create(@opts[:length], @opts[:sets])
85+
offset = Rex::Text.pattern_offset(buffer, query)
86+
87+
# Handle cases where there is no match by looking for "close" matches
88+
unless offset
89+
found = false
90+
$stderr.puts "[*] No exact matches, looking for likely candidates..."
91+
92+
# Look for shifts by a single byte
93+
0.upto(3) do |idx|
94+
0.upto(255) do |c|
95+
nvb = [query].pack("V")
96+
nvb[idx, 1] = [c].pack("C")
97+
nvi = nvb.unpack("V").first
98+
99+
off = Rex::Text.pattern_offset(buffer, nvi)
100+
if off
101+
mle = query - buffer[off, 4].unpack("V").first
102+
mbe = query - buffer[off, 4].unpack("N").first
103+
puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] ) byte offset #{idx}"
104+
found = true
105+
end
106+
end
107+
end
108+
109+
exit! if found
110+
111+
# Look for 16-bit offsets
112+
[0, 2].each do |idx|
113+
0.upto(65535) do |c|
114+
nvb = [query].pack("V")
115+
nvb[idx, 2] = [c].pack("v")
116+
nvi = nvb.unpack("V").first
117+
118+
off = Rex::Text.pattern_offset(buffer, nvi)
119+
if off
120+
mle = query - buffer[off, 4].unpack("V").first
121+
mbe = query - buffer[off, 4].unpack("N").first
122+
puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] )"
123+
found = true
124+
end
125+
end
126+
end
95127
end
96-
end
97-
end
98128

99-
exit if found
100-
101-
# Look for 16-bit offsets
102-
[0, 2].each do |idx|
103-
0.upto(65535) do |c|
104-
nvb = [value].pack("V")
105-
nvb[idx, 2] = [c].pack("v")
106-
nvi = nvb.unpack("V").first
107-
108-
off = Rex::Text.pattern_offset(buffer, nvi)
109-
if off
110-
mle = value - buffer[off,4].unpack("V").first
111-
mbe = value - buffer[off,4].unpack("N").first
112-
puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] )"
113-
found = true
129+
while offset
130+
puts "[*] Exact match at offset #{offset}"
131+
offset = Rex::Text.pattern_offset(buffer, query, offset + 1)
114132
end
115133
end
116134
end
117-
118135
end
119136

120-
while offset
121-
puts "[*] Exact match at offset #{offset}"
122-
offset = Rex::Text.pattern_offset(buffer, value, offset + 1)
137+
if __FILE__ == $PROGRAM_NAME
138+
driver = PatternOffset::Driver.new
139+
begin
140+
driver.run
141+
rescue ::StandardError => e
142+
elog("#{e.class}: #{e.message}\n#{e.backtrace * "\n"}")
143+
$stderr.puts "[x] #{e.class}: #{e.message}"
144+
$stderr.puts "[*] If necessary, please refer to framework.log for more details."
145+
146+
end
123147
end

0 commit comments

Comments
 (0)