turborex 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +38 -0
  4. data/README.rdoc +19 -0
  5. data/examples/alpc_client.rb +15 -0
  6. data/examples/alpc_server.rb +14 -0
  7. data/examples/com_client.rb +19 -0
  8. data/examples/com_finder.rb +39 -0
  9. data/examples/create_instance.rb +15 -0
  10. data/examples/cstruct.rb +19 -0
  11. data/examples/find_com_client_calls.rb +16 -0
  12. data/examples/find_rpc_security_callback.rb +12 -0
  13. data/examples/rpc_finder.rb +117 -0
  14. data/examples/scan_exports.rb +5 -0
  15. data/examples/scan_imports.rb +5 -0
  16. data/examples/tinysdk.rb +17 -0
  17. data/lib/turborex.rb +21 -0
  18. data/lib/turborex/cstruct.rb +565 -0
  19. data/lib/turborex/cstruct/struct_helper.rb +7 -0
  20. data/lib/turborex/exception.rb +65 -0
  21. data/lib/turborex/fuzzer.rb +204 -0
  22. data/lib/turborex/fuzzer/containers.rb +115 -0
  23. data/lib/turborex/fuzzer/coverage.rb +67 -0
  24. data/lib/turborex/fuzzer/mutators.rb +25 -0
  25. data/lib/turborex/fuzzer/seed.rb +30 -0
  26. data/lib/turborex/monkey.rb +11 -0
  27. data/lib/turborex/msrpc.rb +14 -0
  28. data/lib/turborex/msrpc/decompiler.rb +244 -0
  29. data/lib/turborex/msrpc/midl.rb +747 -0
  30. data/lib/turborex/msrpc/ndrtype.rb +167 -0
  31. data/lib/turborex/msrpc/rpcbase.rb +777 -0
  32. data/lib/turborex/msrpc/rpcfinder.rb +1426 -0
  33. data/lib/turborex/msrpc/utils.rb +70 -0
  34. data/lib/turborex/pefile.rb +8 -0
  35. data/lib/turborex/pefile/pe.rb +61 -0
  36. data/lib/turborex/pefile/scanner.rb +82 -0
  37. data/lib/turborex/utils.rb +321 -0
  38. data/lib/turborex/windows.rb +402 -0
  39. data/lib/turborex/windows/alpc.rb +844 -0
  40. data/lib/turborex/windows/com.rb +266 -0
  41. data/lib/turborex/windows/com/client.rb +84 -0
  42. data/lib/turborex/windows/com/com_finder.rb +330 -0
  43. data/lib/turborex/windows/com/com_registry.rb +100 -0
  44. data/lib/turborex/windows/com/interface.rb +522 -0
  45. data/lib/turborex/windows/com/utils.rb +210 -0
  46. data/lib/turborex/windows/constants.rb +82 -0
  47. data/lib/turborex/windows/process.rb +56 -0
  48. data/lib/turborex/windows/security.rb +12 -0
  49. data/lib/turborex/windows/security/ace.rb +76 -0
  50. data/lib/turborex/windows/security/acl.rb +25 -0
  51. data/lib/turborex/windows/security/security_descriptor.rb +118 -0
  52. data/lib/turborex/windows/tinysdk.rb +89 -0
  53. data/lib/turborex/windows/utils.rb +138 -0
  54. data/resources/headers/alpc/ntdef.h +72 -0
  55. data/resources/headers/alpc/ntlpcapi.h +1014 -0
  56. data/resources/headers/rpc/common.h +162 -0
  57. data/resources/headers/rpc/guiddef.h +191 -0
  58. data/resources/headers/rpc/internal_ndrtypes.h +262 -0
  59. data/resources/headers/rpc/rpc.h +10 -0
  60. data/resources/headers/rpc/rpcdce.h +266 -0
  61. data/resources/headers/rpc/rpcdcep.h +187 -0
  62. data/resources/headers/rpc/rpcndr.h +39 -0
  63. data/resources/headers/rpc/v4_x64/rpcinternals.h +154 -0
  64. data/resources/headers/rpc/wintype.h +517 -0
  65. data/resources/headers/tinysdk/tinysdk.h +5 -0
  66. data/resources/headers/tinysdk/tinysdk/comdef.h +645 -0
  67. data/resources/headers/tinysdk/tinysdk/dbghelp.h +118 -0
  68. data/resources/headers/tinysdk/tinysdk/guiddef.h +194 -0
  69. data/resources/headers/tinysdk/tinysdk/memoryapi.h +12 -0
  70. data/resources/headers/tinysdk/tinysdk/poppack.h +12 -0
  71. data/resources/headers/tinysdk/tinysdk/pshpack4.h +13 -0
  72. data/resources/headers/tinysdk/tinysdk/winnt.h +1059 -0
  73. data/resources/headers/tinysdk/tinysdk/wintype.h +326 -0
  74. metadata +290 -0
@@ -0,0 +1,70 @@
1
+ module TurboRex
2
+ module MSRPC
3
+ module Utils
4
+ extend TurboRex::MSRPC::RPCBase
5
+
6
+ def get_interface_type
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def gen_script_rpc_client_np(opts = {})
11
+ uuid = opts[:uuid]
12
+ version = opts[:version] || '1.0'
13
+ function = opts[:function]
14
+ data = opts[:data]
15
+ pipe = opts[:pipe]
16
+ output = opts[:output] || 'my_rpc_client.rb'
17
+
18
+ template = <<-EOS
19
+ #usage: ruby your_script.rb RHOST USERNAME PASSWORD
20
+ require 'rex'
21
+ require 'rex/encoder/ndr'
22
+
23
+ Rex::Proto::SMB::SimpleClient.class_eval do
24
+ attr_accessor :read_timeout
25
+ end
26
+
27
+ uuid = #{uuid}
28
+ version = #{version}
29
+ protocol = 'ncacn_np'
30
+ rhost = ARGV[0]
31
+ opts = ['#{pipe}']
32
+ handle = Rex::Proto::DCERPC::Handle.new([uuid, version], protocol, rhost, opts)
33
+ function = #{function}
34
+ data = #{data}
35
+
36
+ sock = Rex::Socket::Tcp.create('PeerHost' => rhost, 'PeerPort' => 445)
37
+ dcerpc = Rex::Proto::DCERPC::Client.new(handle, sock, {'smb_user' => ARGV[1], 'smb_pass' => ARGV[2]})
38
+ res = dcerpc.call(function, data, true)
39
+
40
+ puts res
41
+ EOS
42
+
43
+ file = File.new(output, 'rw')
44
+ file.puts template
45
+ file.close
46
+
47
+ true
48
+ end
49
+
50
+ def self.raw_to_guid_str(raw, upcase = true)
51
+ unpacked = raw.unpack("VvvCCa6")
52
+ mac = unpacked[5].unpack("C*")
53
+ unpacked[-1] = '%02x%02x%02x%02x%02x%02x' % mac
54
+ formatted = ("%08x-%04x-%04x-%02x%02x-%s" % unpacked)
55
+ upcase ? formatted.upcase : formatted
56
+ end
57
+
58
+ def self.read_cstring(isource, base=0)
59
+ len=0
60
+ cstr = ""
61
+ until (data=isource.read(base+len, 1)) == "\x00"
62
+ cstr << data
63
+ len+=1
64
+ end
65
+
66
+ return cstr, len
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,8 @@
1
+ require 'turborex/pefile/scanner'
2
+ require 'turborex/pefile/pe'
3
+
4
+ module TurboRex
5
+ module PEFile
6
+
7
+ end
8
+ end
@@ -0,0 +1,61 @@
1
+ require 'rex/peparsey'
2
+ require 'pathname'
3
+ require 'rgl/adjacency'
4
+
5
+ module TurboRex
6
+ module PEFile
7
+ class PE < Rex::PeParsey::Pe
8
+ attr_accessor :image_path
9
+ attr_reader :data_sections
10
+ attr_reader :executable_sections
11
+
12
+ def initialize(isource)
13
+ super(isource)
14
+
15
+ get_data_sections
16
+ get_executable_sections
17
+ end
18
+
19
+ def data_section_names
20
+ unless @data_sections.empty?
21
+ names = []
22
+ @data_sections.each do |section|
23
+ names << section.name
24
+ end
25
+
26
+ return names
27
+ end
28
+
29
+ nil
30
+ end
31
+
32
+ private
33
+
34
+ def get_data_sections
35
+ @data_sections = []
36
+ self.all_sections.each do |section|
37
+ next if section.flags.nil?
38
+ if section.flags & 0x20000000 != 0 #IMAGE_SCN_MEM_EXECUTE
39
+ next
40
+ end
41
+
42
+ unless section.flags & 0x40000000 != 0 #IMAGE_SCN_MEM_READ
43
+ next
44
+ end
45
+
46
+ @data_sections << section
47
+ end
48
+ end
49
+
50
+ def get_executable_sections
51
+ @executable_sections = []
52
+ self.all_sections.each do |section|
53
+ next if section.flags.nil?
54
+ if section.flags & 0x20000000 != 0 #IMAGE_SCN_MEM_EXECUTE
55
+ @executable_sections << section
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,82 @@
1
+ module TurboRex
2
+ module PEFile
3
+ module Scanner
4
+ require 'rgl/path'
5
+
6
+ def self.scan_section(section, regex)
7
+ index = 0
8
+
9
+ hits = []
10
+
11
+ while index < section.size && (index = section.index(regex, index)) != nil
12
+
13
+ idx = index
14
+ buf = ''
15
+ mat = nil
16
+
17
+ while (!(mat = buf.match(regex)))
18
+ buf << section.read(idx, 1)
19
+ idx += 1
20
+ end
21
+
22
+ rva = section.offset_to_rva(index)
23
+
24
+ hits << [rva, buf.unpack("H*")]
25
+ index += buf.length
26
+ end
27
+
28
+ return hits
29
+ end
30
+
31
+ def self.scan_all_sections(pe, regex)
32
+ result = []
33
+
34
+ pe.all_sections.each do |section|
35
+ Scanner.scan_section(section, regex).each do |r|
36
+ result << r
37
+ end
38
+ end
39
+ end
40
+
41
+ def self.data_section?(section)
42
+ if section.flags & 0x20000000 != 0 #IMAGE_SCN_MEM_EXECUTE
43
+ return false
44
+ end
45
+
46
+ unless section.flags & 0x40000000 != 0 #IMAGE_SCN_MEM_READ
47
+ return false
48
+ end
49
+
50
+ return true
51
+ end
52
+
53
+ def has_path?(dasm, addr1, addr2, dg=nil)
54
+ dg = draw_xrefs_dg(dasm, addr1) unless dg
55
+
56
+ v1 = dasm.get_label_at(addr1) || addr1.to_s
57
+ v2 = dasm.get_label_at(addr2) || addr2.to_s
58
+ dg.path?(v1, v2)
59
+ end
60
+
61
+ def draw_xrefs_dg(dasm, addr1)
62
+ g = dasm.function_graph_from(addr1)
63
+ dg = RGL::DirectedAdjacencyGraph.new
64
+
65
+ (g.keys + g.values).flatten.uniq.each do |e|
66
+ label = dasm.get_label_at(e) || e.to_s
67
+ dg.add_vertex label
68
+ end
69
+
70
+ g.each do |k, v|
71
+ kl = dasm.get_label_at(k) || k.to_s
72
+ v.each do |e|
73
+ el = dasm.get_label_at(e) || e.to_s
74
+ dg.add_edge(kl, el)
75
+ end
76
+ end
77
+
78
+ dg
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,321 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TurboRex
4
+ module Utils
5
+ def self.get_all_subdir(root_path)
6
+ require 'find'
7
+ paths = []
8
+ Find.find(root_path) do |path|
9
+ if FileTest.directory?(path)
10
+ if File.basename(path)[0] == '.'
11
+ Find.prune
12
+ else
13
+ paths << path
14
+ end
15
+ end
16
+ end
17
+
18
+ paths
19
+ end
20
+
21
+ module DisassemblerHelper
22
+ # https://github.com/jjyg/metasm/blob/master/samples/dasm-plugins/imm2off.rb
23
+ def addrtolabel(dasm)
24
+ bp = dasm.prog_binding.invert
25
+ dasm.decoded.each_value do |di|
26
+ next unless di.is_a?(Metasm::DecodedInstruction)
27
+
28
+ di.each_expr do |e|
29
+ next unless e.is_a?(Metasm::Expression)
30
+
31
+ if l = bp[e.lexpr]
32
+ dasm.add_xref(e.lexpr, Metasm::Xref.new(:addr, di.address))
33
+ e.lexpr = Metasm::Expression[l]
34
+ end
35
+ if l = bp[e.rexpr]
36
+ dasm.add_xref(e.rexpr, Metasm::Xref.new(:addr, di.address))
37
+ e.rexpr = (e.lexpr ? Metasm::Expression[l] : l)
38
+ end
39
+ end
40
+ end
41
+ nil
42
+ end
43
+
44
+ def add_dasm_all_method(dasm)
45
+ dasm.instance_eval %(
46
+ def dasm_all(addrstart, length, method=:disassemble_fast_deep)
47
+ s = get_section_at(addrstart)
48
+ return if not s
49
+ s = s[0]
50
+ boff = s.ptr
51
+ off = 0
52
+ while off < length
53
+ if di = di_at(addrstart + off)
54
+ off += di.bin_length
55
+ elsif @decoded[addrstart+off]
56
+ off += 1
57
+ else
58
+ s.ptr = boff+off
59
+ maydi = cpu.decode_instruction(s, 0)
60
+ if not maydi
61
+ off += 1
62
+ elsif maydi.instruction.to_s =~ /nop|lea (.*), \[\1(?:\+0)?\]|mov (.*), \2|int 3/
63
+ off += maydi.bin_length
64
+ else
65
+ send(method, addrstart+off)
66
+ end
67
+ end
68
+ end
69
+
70
+ count = 0
71
+ off = 0
72
+ while off < length
73
+ addr = addrstart+off
74
+ if di = di_at(addr)
75
+ if di.block_head?
76
+ b = di.block
77
+ if not @function[addr] and b.from_subfuncret.to_a.empty? and b.from_normal.to_a.empty?
78
+ l = auto_label_at(addr, 'sub_orph')
79
+ @function[addrstart+off] = Metasm::DecodedFunction.new
80
+ @function[addrstart+off].finalized = true
81
+ detect_function_thunk(addr)
82
+ count += 1
83
+ end
84
+ end
85
+ off += di.bin_length
86
+ else
87
+ off += 1
88
+ end
89
+ end
90
+
91
+ end
92
+ )
93
+
94
+ dasm
95
+ end
96
+
97
+ def _disassemble_executable_sections(pe, method=:disassemble_fast_deep)
98
+ dasm = pe.disassembler
99
+ executable_sections = pe.section_info.select {|s| s.last.to_s.split(',').include?('MEM_EXECUTE')}
100
+ unless executable_sections.empty?
101
+ add_dasm_all_method(dasm)
102
+ executable_sections.each do |name, address, len|
103
+ dasm.dasm_all(address, len, method)
104
+ end
105
+
106
+ addrtolabel(dasm)
107
+ dasm
108
+ end
109
+ end
110
+
111
+ # https://github.com/jjyg/metasm/blob/2a088ff85e5b873570bc284a97ddd9f8b3b0a03a/metasm/gui/dasm_main.rb#L413
112
+ def backtrace(addr, dasm, e, narg={})
113
+ bd = {}
114
+ expr = Metasm::IndExpression.parse_string(e)
115
+ registers = (begin
116
+ dasm.cpu.dbg_register_list.map(&:to_s)
117
+ rescue StandardError
118
+ []
119
+ end)
120
+ expr.externals.grep(String).each do |w|
121
+ bd[w] = w.downcase.to_sym if registers.include? w.downcase
122
+ end
123
+
124
+ expr = expr.bind(bd).reduce do |e_|
125
+ e_.len ||= dasm.cpu.size / 8 if e_.is_a? Metasm::Indirection; nil
126
+ end
127
+
128
+ log = []
129
+ found = []
130
+ bt_opts = { log: log }
131
+ bt_opts.merge!(narg)
132
+ dasm.backtrace(expr, addr, bt_opts)
133
+ if found_log = log.assoc(:found)
134
+ found_log[1].each do |expr|
135
+ found << dasm.resolve(expr)
136
+ end
137
+ end
138
+
139
+ [found, log]
140
+ end
141
+
142
+ def solve_cppobj_call(dasm, di)
143
+ return unless di.opcode.props[:saveip]
144
+ fptr = dasm.get_xrefs_x(di)
145
+ return if fptr.to_a.length != 1
146
+ fptr = ::Metasm::Expression[fptr.first].reduce_rec
147
+ return unless fptr.kind_of? ::Metasm::Indirection
148
+ return unless fptr.pointer.lexpr.kind_of? Symbol
149
+ return unless fptr.pointer.rexpr.kind_of? Integer
150
+ log = []
151
+ vtbl = dasm.backtrace(fptr.pointer.lexpr, di.address, log: log)
152
+ vtbl.delete ::Metasm::Expression::Unknown
153
+
154
+ if vtbl.empty?
155
+ r = log.reverse_each.detect {|l| l[0] != :found && l[2] != ::Metasm::Expression[:unknown]}
156
+ vtbl = r[2].reduce_rec
157
+ else
158
+ vtbl = vtbl.first
159
+ end
160
+
161
+ vtbl = ::Metasm::Expression[vtbl].reduce_rec
162
+ return unless vtbl.kind_of? ::Metasm::Indirection
163
+ obj = vtbl.pointer
164
+ ptr_size = @dasm.program.cpu.size / 8
165
+ [obj, vtbl, fptr.pointer.rexpr / ptr_size]
166
+ end
167
+
168
+ def solve_guard_icall(dasm, di)
169
+ return unless di.opcode.props[:saveip]
170
+ fptr = dasm.get_xrefs_x(di)
171
+ return if fptr.to_a.length != 1
172
+ fptr = ::Metasm::Expression[fptr.first].reduce_rec
173
+ return unless fptr.kind_of? ::Metasm::Indirection
174
+
175
+ v = case dasm.cpu.size
176
+ when 32
177
+ dasm.program.decode_loadconfig.guard_check_icall
178
+ when 64
179
+ dasm.program.decode_loadconfig.guard_dispatch_icall
180
+ end
181
+
182
+ #f = dasm.decode_dword(dasm.normalize(dasm.prog_binding[v.to_s])).to_s
183
+ if v == fptr.pointer
184
+ case dasm.cpu.size
185
+ when 32
186
+ expr = :ecx
187
+ when 64
188
+ expr = :rax
189
+ end
190
+
191
+ log = []
192
+ res = dasm.backtrace(expr, di.address, log: log)
193
+ res.delete ::Metasm::Expression::Unknown
194
+ if res.empty?
195
+ r = log.reverse_each.detect {|l| l[0] != :found && l[2] != ::Metasm::Expression[:unknown]}
196
+ return r[2].reduce_rec
197
+ end
198
+
199
+ return res.first
200
+ end
201
+ end
202
+ end
203
+
204
+ module COMApiBacktraceHelper
205
+ include DisassemblerHelper
206
+
207
+ def bt_cocreateinstance(dasm, addr, filter={})
208
+ case dasm.cpu.size
209
+ when 32
210
+ expr_rclsid = '[esp]'
211
+ expr_context = '[esp+8]'
212
+ expr_riid = '[esp+12]'
213
+ expr_pv = '[[esp+16]]'
214
+ when 64
215
+ expr_rclsid = 'rcx'
216
+ expr_context = 'r8'
217
+ expr_riid = 'r9'
218
+ expr_pv = '[[rsp+32]]'
219
+ end
220
+
221
+ rclsid, context, riid, pv = [:unknown]*4
222
+ # rclsid
223
+ found, _ = backtrace(addr, dasm, expr_rclsid)
224
+ unless found.empty?
225
+ raw_rclsid = dasm.read_raw_data(found.first, 16)
226
+ rclsid = TurboRex::MSRPC::Utils.raw_to_guid_str(raw_rclsid)
227
+ if filter[:rclsid]
228
+ return unless rclsid == filter[:rclsid]
229
+ end
230
+ end
231
+
232
+ #context
233
+ found, _ = backtrace(addr, dasm, expr_context)
234
+ unless found.empty?
235
+ context = found.first
236
+ if filter[:context]
237
+ return unless context == filter[:context]
238
+ end
239
+ end
240
+
241
+ # riid
242
+ found, _ = backtrace(addr, dasm, expr_riid)
243
+ unless found.empty?
244
+ raw_riid = dasm.read_raw_data(found.first, 16)
245
+ riid = TurboRex::MSRPC::Utils.raw_to_guid_str(raw_riid)
246
+ if filter[:riid]
247
+ return unless riid == filter[:riid]
248
+ end
249
+ end
250
+
251
+ # pv
252
+ log = []
253
+ found, _ = backtrace(addr, dasm, expr_pv, log: log)
254
+ found.delete ::Metasm::Expression::Unknown
255
+ if found.empty?
256
+ r = log.reverse_each.detect {|l| l[0] != :found && l[2] != ::Metasm::Expression[:unknown]}
257
+ pv = r[2].reduce_rec
258
+ else
259
+ pv = found.first
260
+ end
261
+
262
+
263
+ {rclsid: rclsid, context: context, riid: riid, pv: pv}
264
+ end
265
+
266
+ # TODO: Backtrace ServerInfo
267
+ def bt_cocreateinstanceex(dasm, addr, filter={})
268
+ case dasm.cpu.size
269
+ when 32
270
+ expr_rclsid = '[esp]'
271
+ expr_context = '[esp+8]'
272
+ expr_count = '[esp+16]'
273
+ expr_results = '[esp+20]'
274
+ when 64
275
+ expr_rclsid = 'rcx'
276
+ expr_context = 'r8'
277
+ expr_count = 'dword ptr [rsp+32]'
278
+ expr_results = '[rsp+40]'
279
+ end
280
+
281
+ rclsid, context, iids = [:unknown]*3
282
+
283
+ # rclsid
284
+ found, _ = backtrace(addr, dasm, expr_rclsid)
285
+ unless found.empty?
286
+ raw_rclsid = dasm.read_raw_data(found.first, 16)
287
+ rclsid = TurboRex::MSRPC::Utils.raw_to_guid_str(raw_rclsid)
288
+ if filter[:rclsid]
289
+ return unless rclsid == filter[:rclsid]
290
+ end
291
+ end
292
+
293
+ #context
294
+ found, _ = backtrace(addr, dasm, expr_context)
295
+ unless found.empty?
296
+ context = found.first
297
+ if filter[:context]
298
+ return unless context == filter[:context]
299
+ end
300
+ end
301
+
302
+ # results and count
303
+ found, _ = backtrace(addr, dasm, expr_count)
304
+ unless found.empty?
305
+ count = found.first
306
+ iids = []
307
+ size = dasm.alloc_c_struct('MULTI_QI').sizeof
308
+ count.times do |i|
309
+ expr_iid = "[#{expr_results}+#{i*size}]"
310
+ found, _ = backtrace(addr, dasm, expr_iid)
311
+ unless found.empty?
312
+ iids << found.first
313
+ end
314
+ end
315
+ end
316
+
317
+ {rclsid: rclsid, context: context, iids: iids}
318
+ end
319
+ end
320
+ end
321
+ end