librex 0.0.35 → 0.0.36
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +1 -1
- data/lib/rex/exploitation/javascriptosdetect.rb +3 -22
- data/lib/rex/exploitation/jsobfu.rb +489 -0
- data/lib/rex/io/stream_abstraction.rb +4 -3
- data/lib/rex/parser/acunetix_nokogiri.rb +394 -0
- data/lib/rex/parser/appscan_nokogiri.rb +366 -0
- data/lib/rex/parser/burp_session_nokogiri.rb +290 -0
- data/lib/rex/parser/nokogiri_doc_mixin.rb +5 -3
- data/lib/rex/pescan/scanner.rb +2 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/constants.rb +2 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb +5 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb +30 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry_subsystem/remote_registry_key.rb +188 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/tlv.rb +1 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb +84 -14
- data/lib/rex/proto/http/header.rb +3 -3
- data/lib/rex/ropbuilder.rb +7 -0
- data/lib/rex/ropbuilder/rop.rb +257 -0
- data/lib/rex/ui/text/table.rb +9 -0
- metadata +10 -3
@@ -89,6 +89,7 @@ TLV_TYPE_KEY_NAME = TLV_META_TYPE_STRING | 1003
|
|
89
89
|
TLV_TYPE_VALUE_NAME = TLV_META_TYPE_STRING | 1010
|
90
90
|
TLV_TYPE_VALUE_TYPE = TLV_META_TYPE_UINT | 1011
|
91
91
|
TLV_TYPE_VALUE_DATA = TLV_META_TYPE_RAW | 1012
|
92
|
+
TLV_TYPE_TARGET_HOST = TLV_META_TYPE_STRING | 1013
|
92
93
|
|
93
94
|
# Config
|
94
95
|
TLV_TYPE_COMPUTER_NAME = TLV_META_TYPE_STRING | 1040
|
@@ -37,10 +37,12 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|
37
37
|
#
|
38
38
|
@@reg_opts = Rex::Parser::Arguments.new(
|
39
39
|
"-d" => [ true, "The data to store in the registry value." ],
|
40
|
-
"-h" => [
|
40
|
+
"-h" => [ false, "Help menu." ],
|
41
41
|
"-k" => [ true, "The registry key path (E.g. HKLM\\Software\\Foo)." ],
|
42
42
|
"-t" => [ true, "The registry value type (E.g. REG_SZ)." ],
|
43
|
-
"-v" => [ true, "The registry value name (E.g. Stuff)." ]
|
43
|
+
"-v" => [ true, "The registry value name (E.g. Stuff)." ],
|
44
|
+
"-r" => [ true, "The remote machine name to connect to (with current process credentials" ],
|
45
|
+
"-w" => [ false, "Set KEY_WOW64 flag, valid values [32|64]." ])
|
44
46
|
|
45
47
|
#
|
46
48
|
# List of supported commands.
|
@@ -271,10 +273,12 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|
271
273
|
end
|
272
274
|
|
273
275
|
# Initiailze vars
|
274
|
-
key
|
275
|
-
value
|
276
|
-
data
|
277
|
-
type
|
276
|
+
key = nil
|
277
|
+
value = nil
|
278
|
+
data = nil
|
279
|
+
type = nil
|
280
|
+
wowflag = 0x0000
|
281
|
+
rem = nil
|
278
282
|
|
279
283
|
@@reg_opts.parse(args) { |opt, idx, val|
|
280
284
|
case opt
|
@@ -290,7 +294,7 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|
290
294
|
" queryclass Queries the class of the supplied key [-k <key>]\n" +
|
291
295
|
" setval Set a registry value [-k <key> -v <val> -d <data>]\n" +
|
292
296
|
" deleteval Delete the supplied registry value [-k <key> -v <val>]\n" +
|
293
|
-
" queryval Queries the data contents of a value [-k <key> -v <val>]\n\n")
|
297
|
+
" queryval Queries the data contents of a value [-k <key> -v <val>]\n\n")
|
294
298
|
return false
|
295
299
|
when "-k"
|
296
300
|
key = val
|
@@ -300,6 +304,14 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|
300
304
|
type = val
|
301
305
|
when "-d"
|
302
306
|
data = val
|
307
|
+
when "-r"
|
308
|
+
rem = val
|
309
|
+
when "-w"
|
310
|
+
if val == '64'
|
311
|
+
wowflag = KEY_WOW64_64KEY
|
312
|
+
elsif val == '32'
|
313
|
+
wowflag = KEY_WOW64_32KEY
|
314
|
+
end
|
303
315
|
end
|
304
316
|
}
|
305
317
|
|
@@ -316,7 +328,16 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|
316
328
|
# Rock it
|
317
329
|
case cmd
|
318
330
|
when "enumkey"
|
319
|
-
|
331
|
+
|
332
|
+
open_key = nil
|
333
|
+
if not rem
|
334
|
+
open_key = client.sys.registry.open_key(root_key, base_key, KEY_READ + wowflag)
|
335
|
+
else
|
336
|
+
remote_key = client.sys.registry.open_remote_key(rem, root_key)
|
337
|
+
if remote_key
|
338
|
+
open_key = remote_key.open_key(base_key, KEY_READ + wowflag)
|
339
|
+
end
|
340
|
+
end
|
320
341
|
|
321
342
|
print_line(
|
322
343
|
"Enumerating: #{key}\n")
|
@@ -349,12 +370,29 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|
349
370
|
end
|
350
371
|
|
351
372
|
when "createkey"
|
352
|
-
open_key =
|
373
|
+
open_key = nil
|
374
|
+
if not rem
|
375
|
+
open_key = client.sys.registry.create_key(root_key, base_key, KEY_WRITE + wowflag)
|
376
|
+
else
|
377
|
+
remote_key = client.sys.registry.open_remote_key(rem, root_key)
|
378
|
+
if remote_key
|
379
|
+
open_key = remote_key.create_key(base_key, KEY_WRITE + wowflag)
|
380
|
+
end
|
381
|
+
end
|
353
382
|
|
354
383
|
print_line("Successfully created key: #{key}")
|
355
384
|
|
356
385
|
when "deletekey"
|
357
|
-
|
386
|
+
open_key = nil
|
387
|
+
if not rem
|
388
|
+
open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE + wowflag)
|
389
|
+
else
|
390
|
+
remote_key = client.sys.registry.open_remote_key(rem, root_key)
|
391
|
+
if remote_key
|
392
|
+
open_key = remote_key.open_key(base_key, KEY_WRITE + wowflag)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
open_key.delete_key(base_key)
|
358
396
|
|
359
397
|
print_line("Successfully deleted key: #{key}")
|
360
398
|
|
@@ -366,7 +404,15 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|
366
404
|
|
367
405
|
type = "REG_SZ" if (type == nil)
|
368
406
|
|
369
|
-
open_key =
|
407
|
+
open_key = nil
|
408
|
+
if not rem
|
409
|
+
open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE + wowflag)
|
410
|
+
else
|
411
|
+
remote_key = client.sys.registry.open_remote_key(rem, root_key)
|
412
|
+
if remote_key
|
413
|
+
open_key = remote_key.open_key(base_key, KEY_WRITE + wowflag)
|
414
|
+
end
|
415
|
+
end
|
370
416
|
|
371
417
|
open_key.set_value(value, client.sys.registry.type2str(type), data)
|
372
418
|
|
@@ -378,7 +424,15 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|
378
424
|
return false
|
379
425
|
end
|
380
426
|
|
381
|
-
open_key =
|
427
|
+
open_key = nil
|
428
|
+
if not rem
|
429
|
+
open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE + wowflag)
|
430
|
+
else
|
431
|
+
remote_key = client.sys.registry.open_remote_key(rem, root_key)
|
432
|
+
if remote_key
|
433
|
+
open_key = remote_key.open_key(base_key, KEY_WRITE + wowflag)
|
434
|
+
end
|
435
|
+
end
|
382
436
|
|
383
437
|
open_key.delete_value(value)
|
384
438
|
|
@@ -390,7 +444,15 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|
390
444
|
return false
|
391
445
|
end
|
392
446
|
|
393
|
-
open_key =
|
447
|
+
open_key = nil
|
448
|
+
if not rem
|
449
|
+
open_key = client.sys.registry.open_key(root_key, base_key, KEY_READ + wowflag)
|
450
|
+
else
|
451
|
+
remote_key = client.sys.registry.open_remote_key(rem, root_key)
|
452
|
+
if remote_key
|
453
|
+
open_key = remote_key.open_key(base_key, KEY_READ + wowflag)
|
454
|
+
end
|
455
|
+
end
|
394
456
|
|
395
457
|
v = open_key.query_value(value)
|
396
458
|
|
@@ -401,7 +463,15 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|
401
463
|
"Data: #{v.data}\n")
|
402
464
|
|
403
465
|
when "queryclass"
|
404
|
-
open_key =
|
466
|
+
open_key = nil
|
467
|
+
if not rem
|
468
|
+
open_key = client.sys.registry.open_key(root_key, base_key, KEY_READ + wowflag)
|
469
|
+
else
|
470
|
+
remote_key = client.sys.registry.open_remote_key(rem, root_key)
|
471
|
+
if remote_key
|
472
|
+
open_key = remote_key.open_key(base_key, KEY_READ + wowflag)
|
473
|
+
end
|
474
|
+
end
|
405
475
|
|
406
476
|
data = open_key.query_class
|
407
477
|
|
@@ -37,16 +37,16 @@ class Packet::Header < Hash
|
|
37
37
|
|
38
38
|
# put the non-standard line terminations back to normal
|
39
39
|
# gah. not having look behinds suck,
|
40
|
-
header.gsub!(/([^\r])\n
|
40
|
+
header.gsub!(/([^\r])\n/n,'\1' + "\r\n")
|
41
41
|
|
42
42
|
# undo folding, kinda ugly but works for now.
|
43
|
-
header.gsub!(/:\s*\r\n\s+/
|
43
|
+
header.gsub!(/:\s*\r\n\s+/smni,': ')
|
44
44
|
|
45
45
|
# Extract the command string
|
46
46
|
self.cmd_string = header.slice!(/.+\r\n/)
|
47
47
|
|
48
48
|
# Extract each header value pair
|
49
|
-
header.split(/\r\n/
|
49
|
+
header.split(/\r\n/mn).each { |str|
|
50
50
|
if (md = str.match(/^(.+?): (.+?)$/))
|
51
51
|
if (self[md[1]])
|
52
52
|
self[md[1]] << ", " + md[2]
|
@@ -0,0 +1,257 @@
|
|
1
|
+
require 'metasm'
|
2
|
+
require 'rex/compat'
|
3
|
+
require 'rex/ui/text/table'
|
4
|
+
require 'rex/ui/text/output/stdio'
|
5
|
+
require 'rex/ui/text/color'
|
6
|
+
|
7
|
+
module Rex
|
8
|
+
module RopBuilder
|
9
|
+
|
10
|
+
class RopBase
|
11
|
+
def initialize()
|
12
|
+
@stdio = Rex::Ui::Text::Output::Stdio.new
|
13
|
+
@gadgets = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_csv(gadgets = [])
|
17
|
+
if gadgets.empty? and @gadgets.nil? or @gadgets.empty?
|
18
|
+
print_error("No gadgets collected to convert to CSV format.")
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
# allow the users to import gadget collections from multiple files
|
23
|
+
if @gadgets.empty? or @gadgets.nil?
|
24
|
+
@gadgets = gadgets
|
25
|
+
end
|
26
|
+
|
27
|
+
table = Rex::Ui::Text::Table.new(
|
28
|
+
'Header' => "#{@file} ROP Gadgets",
|
29
|
+
'Indent' => 1,
|
30
|
+
'Columns' =>
|
31
|
+
[
|
32
|
+
"Address",
|
33
|
+
"Raw",
|
34
|
+
"Disassembly",
|
35
|
+
])
|
36
|
+
|
37
|
+
@gadgets.each do |gadget|
|
38
|
+
table << [gadget[:address], gadget[:raw].unpack('H*')[0], gadget[:disasm].gsub(/\n/, ' | ')]
|
39
|
+
end
|
40
|
+
|
41
|
+
return table.to_csv
|
42
|
+
end
|
43
|
+
|
44
|
+
def import(file)
|
45
|
+
begin
|
46
|
+
data = File.new(file, 'r').read
|
47
|
+
rescue
|
48
|
+
print_error("Error reading #{file}")
|
49
|
+
end
|
50
|
+
|
51
|
+
data.gsub!(/\"/, '')
|
52
|
+
data.gsub!("Address,Raw,Disassembly\n", '')
|
53
|
+
@gadgets = []
|
54
|
+
data.each_line do |line|
|
55
|
+
addr, raw, disasm = line.split(',', 3)
|
56
|
+
disasm.gsub!(/: /, ":\t")
|
57
|
+
disasm.gsub!(' | ', "\n")
|
58
|
+
raw = [raw].pack('H*')
|
59
|
+
@gadgets << {:file => file, :address => addr, :raw => raw, :disasm => disasm.chomp!}
|
60
|
+
end
|
61
|
+
@gadgets
|
62
|
+
end
|
63
|
+
|
64
|
+
def print_msg(msg, color=true)
|
65
|
+
if not @stdio
|
66
|
+
@stdio = Rex::Ui::Text::Output::Stdio.new
|
67
|
+
end
|
68
|
+
|
69
|
+
if color == true
|
70
|
+
@stdio.auto_color
|
71
|
+
else
|
72
|
+
@stdio.disable_color
|
73
|
+
end
|
74
|
+
@stdio.print_raw(@stdio.substitute_colors(msg))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class RopCollect < RopBase
|
79
|
+
def initialize(file="")
|
80
|
+
@file = file if not file.empty?
|
81
|
+
@bin = Metasm::AutoExe.decode_file(file) if not file.empty?
|
82
|
+
@disassembler = @bin.disassembler if not @bin.nil?
|
83
|
+
if @disassembler
|
84
|
+
@disassembler.cpu = Metasm::Ia32.new('386_common')
|
85
|
+
end
|
86
|
+
super()
|
87
|
+
end
|
88
|
+
|
89
|
+
def collect(depth, pattern)
|
90
|
+
matches = []
|
91
|
+
gadgets = []
|
92
|
+
|
93
|
+
# find matches by scanning for the pattern
|
94
|
+
matches = @disassembler.pattern_scan(pattern)
|
95
|
+
if @bin.kind_of?(Metasm::PE)
|
96
|
+
@bin.sections.each do |section|
|
97
|
+
next if section.characteristics.include? 'MEM_EXECUTE'
|
98
|
+
# delete matches if the address is outside the virtual address space
|
99
|
+
matches.delete_if do |ea|
|
100
|
+
va = section.virtaddr + @bin.optheader.image_base
|
101
|
+
ea >= va and ea < va + section.virtsize
|
102
|
+
end
|
103
|
+
end
|
104
|
+
elsif @bin.kind_of?(Metasm::ELF)
|
105
|
+
@bin.segments.each do |seg|
|
106
|
+
next if seg.flags.include? 'X'
|
107
|
+
matches.delete_if do |ea|
|
108
|
+
ea >= seg.vaddr and ea < seg.vaddr + seg.memsz
|
109
|
+
end
|
110
|
+
end
|
111
|
+
elsif @bin.kind_of?(Metasm::MachO)
|
112
|
+
@bin.segments.each do |seg|
|
113
|
+
next if seg.initprot.include? 'EXECUTE'
|
114
|
+
matches.delete_if do |ea|
|
115
|
+
ea >= seg.virtaddr and ea < seg.virtaddr + seg.filesize
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
gadgets = process_gadgets(matches, depth)
|
121
|
+
gadgets.each do |gadget|
|
122
|
+
@gadgets << gadget
|
123
|
+
end
|
124
|
+
gadgets
|
125
|
+
end
|
126
|
+
|
127
|
+
def pattern_search(pattern)
|
128
|
+
p = Regexp.new("(" + pattern + ")")
|
129
|
+
matches = []
|
130
|
+
|
131
|
+
@gadgets.each do |gadget|
|
132
|
+
disasm = ""
|
133
|
+
addrs = []
|
134
|
+
|
135
|
+
gadget[:disasm].each_line do |line|
|
136
|
+
addr, asm = line.split("\t", 2)
|
137
|
+
addrs << addr
|
138
|
+
disasm << asm
|
139
|
+
end
|
140
|
+
|
141
|
+
if gadget[:raw] =~ p or gadget[:disasm] =~ p or disasm =~ p
|
142
|
+
matches << {:gadget => gadget, :disasm => disasm, :addrs => addrs}
|
143
|
+
end
|
144
|
+
end
|
145
|
+
matches.each do |match|
|
146
|
+
@stdio.print_status("gadget with address: %bld%cya#{match[:gadget][:address]}%clr matched")
|
147
|
+
color_pattern(match[:gadget], match[:disasm], match[:addrs], p)
|
148
|
+
end
|
149
|
+
matches
|
150
|
+
end
|
151
|
+
|
152
|
+
def color_pattern(gadget, disasm, addrs, p)
|
153
|
+
idx = disasm.index(p)
|
154
|
+
if idx.nil?
|
155
|
+
print_msg(gadget[:disasm])
|
156
|
+
return
|
157
|
+
end
|
158
|
+
|
159
|
+
disasm = disasm.insert(idx, "%bld%grn")
|
160
|
+
|
161
|
+
asm = ""
|
162
|
+
cnt = 0
|
163
|
+
colors = false
|
164
|
+
disasm.each_line do |line|
|
165
|
+
# if we find this then we are in the matching area
|
166
|
+
if line.index(/\%bld\%grn/)
|
167
|
+
colors = true
|
168
|
+
end
|
169
|
+
asm << "%clr" + addrs[cnt] + "\t"
|
170
|
+
|
171
|
+
# color the remaining parts of the gadget
|
172
|
+
if colors and line.index("%bld%grn").nil?
|
173
|
+
asm << "%bld%grn" + line
|
174
|
+
else
|
175
|
+
asm << line
|
176
|
+
end
|
177
|
+
|
178
|
+
cnt += 1
|
179
|
+
end
|
180
|
+
asm << "%clr\n"
|
181
|
+
print_msg(asm)
|
182
|
+
end
|
183
|
+
|
184
|
+
def process_gadgets(rets, num)
|
185
|
+
ret = {}
|
186
|
+
gadgets = []
|
187
|
+
tmp = []
|
188
|
+
rets.each do |ea|
|
189
|
+
insn = @disassembler.disassemble_instruction(ea)
|
190
|
+
next if not insn
|
191
|
+
|
192
|
+
xtra = insn.bin_length
|
193
|
+
|
194
|
+
1.upto(num) do |x|
|
195
|
+
addr = ea - x
|
196
|
+
|
197
|
+
# get the disassembled instruction at this address
|
198
|
+
di = @disassembler.disassemble_instruction(addr)
|
199
|
+
|
200
|
+
# skip invalid instructions
|
201
|
+
next if not di
|
202
|
+
next if di.opcode.props[:setip]
|
203
|
+
next if di.opcode.props[:stopexec]
|
204
|
+
|
205
|
+
# get raw bytes
|
206
|
+
buf = @disassembler.read_raw_data(addr, x + xtra)
|
207
|
+
|
208
|
+
|
209
|
+
# make sure disassembling forward leads to our instruction
|
210
|
+
next if not ends_with_addr(buf, addr, ea)
|
211
|
+
|
212
|
+
dasm = ""
|
213
|
+
while addr <= ea
|
214
|
+
di = @disassembler.disassemble_instruction(addr)
|
215
|
+
dasm << ("0x%08x:\t" % addr) + di.instruction.to_s + "\n"
|
216
|
+
addr = addr + di.bin_length
|
217
|
+
end
|
218
|
+
|
219
|
+
if not tmp.include?(ea)
|
220
|
+
tmp << ea
|
221
|
+
else
|
222
|
+
next
|
223
|
+
end
|
224
|
+
# otherwise, we create a new tailchunk and add it to the list
|
225
|
+
ret = {:file => @file, :address => ("0x%08x" % (ea - x)), :raw => buf, :disasm => dasm}
|
226
|
+
gadgets << ret
|
227
|
+
end
|
228
|
+
end
|
229
|
+
gadgets
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
def ends_with_addr(raw, base, addr)
|
234
|
+
dasm2 = Metasm::Shellcode.decode(raw, @disassembler.cpu).disassembler
|
235
|
+
offset = 0
|
236
|
+
while ((di = dasm2.disassemble_instruction(offset)))
|
237
|
+
return true if (base + offset) == addr
|
238
|
+
return false if di.opcode.props[:setip]
|
239
|
+
return false if di.opcode.props[:stopexec]
|
240
|
+
offset = di.next_addr
|
241
|
+
end
|
242
|
+
false
|
243
|
+
end
|
244
|
+
|
245
|
+
def raw_instructions(raw)
|
246
|
+
insns = []
|
247
|
+
d2 = Metasm::Shellcode.decode(raw, @disassembler.cpu).disassembler
|
248
|
+
addr = 0
|
249
|
+
while ((di = d2.disassemble_instruction(addr)))
|
250
|
+
insns << di.instruction
|
251
|
+
addr = di.next_addr
|
252
|
+
end
|
253
|
+
insns
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|