Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/encoders/x86/avoid_utf8_tolower.rb
19567 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
#
7
# NOTE: Read this if you plan on using this encoder:
8
#
9
# This encoder has some limitations that must be considered. First, this
10
# encoder cannot be used with all of the payloads included in the framework.
11
# Most notably, this includes windows/shell_reverse_tcp. The reason for this
12
# is that some payloads are of a size that leads to a bad character (uppercase
13
# character) being generated in the decoder stub header.
14
#
15
# A second thing to consider is that some IP addresses used in payloads are
16
# incompatible with this encoder depending on their alignment within the
17
# payload. For example, the use of 127.0.0.1 may not work due to the fact
18
# that it's impossible to reach the bytes 127, 0, and 1 in a single add or sub
19
# due to the algorithm that this encoder uses.
20
#
21
# Here's a description of how it works:
22
#
23
# This encoder is pretty lame. It has a huge size overhead. Alas, it
24
# does produce tolower safe and UTF8 safe payloads. The decoder itself is
25
# split into three distinct chunks. The first chunk is the header, the second
26
# chunk is the inline-decoding, and the third chunk is where the decoded data
27
# is persisted. Unlike most encoders, this encoder does not use any branch
28
# instructions and instead runs into the decoded data after it completes due
29
# to the fact that it is decoding inline.
30
#
31
# The basic approach taken to implement the encoder is this. First, the
32
# decoder header assumes that a register (ecx) points to the first byte
33
# in the decoder stub. It then proceeds to calculate the offset to the
34
# third chunk of the decoder (the persisted data) and updates the context
35
# register (ecx) to point to the first byte of the third chunk of the decoder
36
# stub. Following that, the second chunk of the decoder begins executing
37
# which uses a series of add or subtract operations on the third chunk of the
38
# decoder to produce the actual opcodes of the encoded payload. For each four
39
# bytes of encoded data, a sub or add instruction is used in combination with
40
# complementary information stored in the third chunk of the decoder.
41
#
42
# For example, in order to produce 0x01fdfeff one could do the following:
43
#
44
# 0x5e096f7c
45
# - 0x5c0b707d
46
# ------------
47
# 0x01fdfeff
48
#
49
# After all of the inline decoding operations complete, the payload should
50
# simply fall through into the now-decoded payload that was stored in the
51
# third chunk of the decoder.
52
#
53
# The following is an example encoding of:
54
#
55
# "\xcc\x41\xcc\x41\xcc\x41\xcc\x41\xff\xfe\xfd\x01\xff\x02\x82\x4c"
56
#
57
# 00000000 6A04 push byte +0x4
58
# 00000002 6B3C240B imul edi,[esp],byte +0xb
59
# 00000006 60 pusha
60
# 00000007 030C24 add ecx,[esp]
61
# 0000000A 6A11 push byte +0x11
62
# 0000000C 030C24 add ecx,[esp]
63
# 0000000F 6A04 push byte +0x4
64
# 00000011 68640F5F31 push dword 0x315f0f64
65
# 00000016 5F pop edi
66
# 00000017 0139 add [ecx],edi
67
# 00000019 030C24 add ecx,[esp]
68
# 0000001C 6870326B32 push dword 0x326b3270
69
# 00000021 5F pop edi
70
# 00000022 0139 add [ecx],edi
71
# 00000024 030C24 add ecx,[esp]
72
# 00000027 687D700B5C push dword 0x5c0b707d
73
# 0000002C 5F pop edi
74
# 0000002D 2939 sub [ecx],edi
75
# 0000002F 030C24 add ecx,[esp]
76
# 00000032 6804317F32 push dword 0x327f3104
77
# 00000037 5F pop edi
78
# 00000038 2939 sub [ecx],edi
79
# 0000003A 030C24 add ecx,[esp]
80
# 0000003D 68326D105C push dword 0x5c106d32
81
# 00000042 0F610F punpcklwd mm1,[edi]
82
# 00000045 7C6F jl 0xb6
83
# 00000047 095E03 or [esi+0x3],ebx
84
# 0000004A 3401 xor al,0x1
85
# 0000004C 7F db 0x7F
86
#
87
class MetasploitModule < Msf::Encoder
88
89
# This encoder has a manual ranking because it should only be used in cases
90
# where information has been explicitly supplied, like the BufferOffset.
91
Rank = ManualRanking
92
93
def initialize
94
super(
95
'Name' => 'Avoid UTF8/tolower',
96
'Description' => 'UTF8 Safe, tolower Safe Encoder',
97
'Author' => 'skape',
98
'Arch' => ARCH_X86,
99
'License' => MSF_LICENSE,
100
'EncoderType' => Msf::Encoder::Type::NonUpperUtf8Safe,
101
'Decoder' => {
102
'KeySize' => 4,
103
'BlockSize' => 4
104
})
105
end
106
107
#
108
# Returns the decoder stub that is adjusted for the size of
109
# the buffer being encoded
110
#
111
def decoder_stub(state)
112
len = ((state.buf.length + 3) & (~0x3)) / 4
113
114
# Grab the number of additional bytes that we need to adjust by in order
115
# to get the context register to point immediately after the stub header
116
off = (datastore['BufferOffset'] || 0).to_i
117
118
# Check to make sure that the length is a valid size
119
if is_badchar(state, len)
120
raise EncodingError, "The payload being encoded is of an incompatible size (#{len} bytes)"
121
end
122
123
decoder =
124
"\x6a" + [len].pack('C') + # push len
125
"\x6b\x3c\x24\x0b" + # imul 0xb
126
"\x60" + # pusha
127
"\x03\x0c\x24" + # add ecx, [esp]
128
"\x6a" + [0x11 + off].pack('C') + # push byte 0x11 + off
129
"\x03\x0c\x24" + # add ecx, [esp]
130
"\x6a\x04" # push byte 0x4
131
132
# encoded sled
133
state.context = ''
134
135
return decoder
136
end
137
138
def encode_block(state, block)
139
buf = try_add(state, block)
140
141
if buf.nil?
142
buf = try_sub(state, block)
143
end
144
145
if buf.nil?
146
raise BadcharError.new(state.encoded, 0, 0, 0)
147
end
148
149
buf
150
end
151
152
#
153
# Appends the encoded context portion.
154
#
155
def encode_end(state)
156
state.encoded += state.context
157
end
158
159
#
160
# Generate the instructions that will be used to produce a valid
161
# block after decoding using the sub instruction in conjunction with
162
# two UTF8/tolower safe values.
163
#
164
def try_sub(state, block)
165
buf = "\x68"
166
vbuf = ''
167
ctx = ''
168
carry = 0
169
170
block.each_byte do |b|
171
# It's impossible to reach 0x7f, 0x80, 0x81 with two subs
172
# of a value that is < 0x80 without NULLs.
173
return nil if (b == 0x80) || (b == 0x81) || (b == 0x7f)
174
175
x = 0
176
y = 0
177
attempts = 0
178
prev_carry = carry
179
180
loop do
181
carry = prev_carry
182
183
if (b > 0x80)
184
diff = 0x100 - b
185
y = rand(0x80 - diff - 1).to_i + 1
186
x = (0x100 - (b - y + carry))
187
carry = 1
188
else
189
diff = 0x7f - b
190
x = rand(diff - 1) + 1
191
y = (b + x + carry) & 0xff
192
carry = 0
193
end
194
195
attempts += 1
196
197
# Lame.
198
return nil if (attempts > 512)
199
break unless is_badchar(state, x) || is_badchar(state, y)
200
end
201
202
vbuf += [x].pack('C')
203
ctx += [y].pack('C')
204
end
205
206
buf += vbuf + "\x5f\x29\x39\x03\x0c\x24"
207
208
state.context += ctx
209
210
return buf
211
end
212
213
#
214
# Generate instructions that will be used to produce a valid block after
215
# decoding using the add instruction in conjunction with two UTF8/tolower
216
# safe values.
217
#
218
def try_add(state, block)
219
buf = "\x68"
220
vbuf = ''
221
ctx = ''
222
223
block.each_byte do |b|
224
# It's impossible to produce 0xff and 0x01 using two non-NULL,
225
# tolower safe, and UTF8 safe values.
226
return nil if (b == 0xff) || (b == 0x01) || (b == 0x00)
227
228
attempts = 0
229
230
loop do
231
xv = rand(b - 1) + 1
232
233
attempts += 1
234
235
# Lame.
236
return nil if (attempts > 512)
237
break unless is_badchar(state, xv) || is_badchar(state, b - xv)
238
end
239
240
vbuf += [xv].pack('C')
241
ctx += [b - xv].pack('C')
242
end
243
244
buf += vbuf + "\x5f\x01\x39\x03\x0c\x24"
245
246
state.context += ctx
247
248
return buf
249
end
250
251
def is_badchar(state, val)
252
((val >= 0x41 and val <= 0x5a) or val >= 0x80) or Rex::Text.badchar_index([val].pack('C'), state.badchars)
253
end
254
end
255
256