Skip to content

Commit 55f6fe7

Browse files
author
Brent Cook
committed
Land rapid7#5510, update x86/alpha* encoders to be SaveRegister aware
2 parents d551f42 + 101c5a2 commit 55f6fe7

File tree

8 files changed

+641
-27
lines changed

8 files changed

+641
-27
lines changed

lib/rex/arch/x86.rb

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,7 @@ def self.fpu_instructions
421421
# This method returns an array containing a geteip stub, a register, and an offset
422422
# This method will return nil if the getip generation fails
423423
#
424-
def self.geteip_fpu(badchars)
425-
424+
def self.geteip_fpu(badchars, modified_registers = [])
426425
#
427426
# Default badchars to an empty string
428427
#
@@ -470,18 +469,29 @@ def self.geteip_fpu(badchars)
470469
#
471470
while(dsts.length > 0)
472471
buf = ''
472+
mod_registers = [ESP]
473473
dst = dsts[ rand(dsts.length) ]
474474
dsts.delete(dst)
475475

476476
# If the register is not ESP, copy ESP
477477
if (dst != ESP)
478-
next if badchars.index( (0x70 + dst).chr )
478+
mod_registers.push(dst)
479+
if badchars.index( (0x70 + dst).chr )
480+
mod_registers.pop(dst)
481+
next
482+
end
479483

480484
if !(badchars.index("\x89") or badchars.index( (0xE0+dst).chr ))
481485
buf << "\x89" + (0xE0 + dst).chr
482486
else
483-
next if badchars.index("\x54")
484-
next if badchars.index( (0x58+dst).chr )
487+
if badchars.index("\x54")
488+
mod_registers.pop(dst)
489+
next
490+
end
491+
if badchars.index( (0x58+dst).chr )
492+
mod_registers.pop(dst)
493+
next
494+
end
485495
buf << "\x54" + (0x58 + dst).chr
486496
end
487497
end
@@ -506,15 +516,19 @@ def self.geteip_fpu(badchars)
506516
regs.delete(reg)
507517
next if reg == ESP
508518
next if badchars.index( (0x58 + reg).chr )
519+
mod_registers.push(reg)
509520

510521
# Pop the value back out
511522
0.upto(pad / 4) { |c| out << (0x58 + reg).chr }
512523

513524
# Fix the value to point to self
514525
gap = out.length - buf.length
515526

527+
mod_registers.uniq!
528+
modified_registers.concat(mod_registers)
516529
return [out, REG_NAMES32[reg].upcase, gap]
517530
end
531+
mod_registers.pop(dst)
518532
end
519533

520534
return nil

lib/rex/encoder/alpha2/alpha_mixed.rb

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,49 @@ module Alpha2
88

99
class AlphaMixed < Generic
1010

11-
def self.gen_decoder_prefix(reg, offset)
12-
if (offset > 32)
13-
raise "Critical: Offset is greater than 32"
11+
# Generates the decoder stub prefix
12+
#
13+
# @param [String] reg the register pointing to the encoded payload
14+
# @param [Fixnum] offset the offset to reach the encoded payload
15+
# @param [Array] modified_registers accounts the registers modified by the stub
16+
# @return [String] the alpha mixed decoder stub prefix
17+
def self.gen_decoder_prefix(reg, offset, modified_registers = [])
18+
if offset > 32
19+
raise 'Critical: Offset is greater than 32'
1420
end
1521

22+
mod_registers = []
23+
nop_regs = []
24+
mod_regs = []
25+
edx_regs = []
26+
1627
# use inc ebx as a nop here so we still pad correctly
17-
if (offset <= 16)
28+
if offset <= 16
1829
nop = 'C' * offset
30+
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
31+
1932
mod = 'I' * (16 - offset) + nop + '7QZ' # dec ecx,,, push ecx, pop edx
33+
mod_regs.push(Rex::Arch::X86::ECX) unless offset == 16
34+
mod_regs.concat(nop_regs)
35+
mod_regs.push(Rex::Arch::X86::EDX)
36+
2037
edxmod = 'J' * (17 - offset)
38+
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
2139
else
2240
mod = 'A' * (offset - 16)
41+
mod_regs.push(Rex::Arch::X86::ECX) unless mod.empty?
42+
2343
nop = 'C' * (16 - mod.length)
44+
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
45+
2446
mod << nop + '7QZ'
47+
mod_regs.concat(nop_regs)
48+
mod_regs.push(Rex::Arch::X86::EDX)
49+
2550
edxmod = 'B' * (17 - (offset - 16))
51+
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
2652
end
53+
2754
regprefix = {
2855
'EAX' => 'PY' + mod, # push eax, pop ecx
2956
'ECX' => 'I' + mod, # dec ecx
@@ -36,15 +63,38 @@ def self.gen_decoder_prefix(reg, offset)
3663
}
3764

3865
reg.upcase!
39-
if (not regprefix.keys.include? reg)
40-
raise ArgumentError.new("Invalid register name")
66+
67+
unless regprefix.keys.include?(reg)
68+
raise ArgumentError.new('Invalid register name')
4169
end
70+
71+
case reg
72+
when 'EDX'
73+
mod_registers.concat(edx_regs)
74+
mod_registers.concat(nop_regs)
75+
mod_registers.push(Rex::Arch::X86::ECX)
76+
else
77+
mod_registers.push(Rex::Arch::X86::ECX)
78+
mod_registers.concat(mod_regs)
79+
end
80+
81+
mod_registers.uniq!
82+
modified_registers.concat(mod_registers)
83+
4284
return regprefix[reg]
4385
end
4486

45-
def self.gen_decoder(reg, offset)
87+
# Generates the decoder stub
88+
#
89+
# @param [String] reg the register pointing to the encoded payload
90+
# @param [Fixnum] offset the offset to reach the encoded payload
91+
# @param [Array] modified_registers accounts the registers modified by the stub
92+
# @return [String] the alpha mixed decoder stub
93+
def self.gen_decoder(reg, offset, modified_registers = [])
94+
mod_registers = []
95+
4696
decoder =
47-
gen_decoder_prefix(reg, offset) +
97+
gen_decoder_prefix(reg, offset, mod_registers) +
4898
"jA" + # push 0x41
4999
"X" + # pop eax
50100
"P" + # push eax
@@ -62,7 +112,18 @@ def self.gen_decoder(reg, offset)
62112
"uJ" + # jnz short -------------------------
63113
"I" # first encoded char, fixes the above J
64114

65-
return decoder
115+
mod_registers.concat(
116+
[
117+
Rex::Arch::X86::ESP,
118+
Rex::Arch::X86::EAX,
119+
Rex::Arch::X86::ECX,
120+
Rex::Arch::X86::EDX
121+
])
122+
123+
mod_registers.uniq!
124+
modified_registers.concat(mod_registers)
125+
126+
decoder
66127
end
67128

68129
end end end end

lib/rex/encoder/alpha2/alpha_upper.rb

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,47 @@ module Alpha2
99
class AlphaUpper < Generic
1010
def self.default_accepted_chars ; ('B' .. 'Z').to_a + ('0' .. '9').to_a ; end
1111

12-
def self.gen_decoder_prefix(reg, offset)
13-
if (offset > 20)
14-
raise "Critical: Offset is greater than 20"
12+
# Generates the decoder stub prefix
13+
#
14+
# @param [String] reg the register pointing to the encoded payload
15+
# @param [Fixnum] offset the offset to reach the encoded payload
16+
# @param [Array] modified_registers accounts the registers modified by the stub
17+
# @return [String] the alpha upper decoder stub prefix
18+
def self.gen_decoder_prefix(reg, offset, modified_registers = [])
19+
if offset > 20
20+
raise 'Critical: Offset is greater than 20'
1521
end
1622

23+
mod_registers = []
24+
nop_regs = []
25+
mod_regs = []
26+
edx_regs = []
27+
1728
# use inc ebx as a nop here so we still pad correctly
1829
if (offset <= 10)
1930
nop = 'C' * offset
31+
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
32+
2033
mod = 'I' * (10 - offset) + nop + 'QZ' # dec ecx,,, push ecx, pop edx
34+
mod_regs.push(Rex::Arch::X86::ECX) unless offset == 10
35+
mod_regs.concat(nop_regs)
36+
mod_regs.push(Rex::Arch::X86::EDX)
37+
2138
edxmod = 'J' * (11 - offset)
39+
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
2240
else
2341
mod = 'A' * (offset - 10)
42+
mod_regs.push(Rex::Arch::X86::ECX) unless mod.empty?
43+
2444
nop = 'C' * (10 - mod.length)
45+
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
46+
2547
mod << nop + 'QZ'
48+
mod_regs.concat(nop_regs)
49+
mod_regs.push(Rex::Arch::X86::EDX)
50+
2651
edxmod = 'B' * (11 - (offset - 10))
52+
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
2753
end
2854
regprefix = {
2955
'EAX' => 'PY' + mod, # push eax, pop ecx
@@ -33,20 +59,41 @@ def self.gen_decoder_prefix(reg, offset)
3359
'ESP' => 'TY' + mod, # push esp, pop ecx
3460
'EBP' => 'UY' + mod, # push ebp, pop ecx
3561
'ESI' => 'VY' + mod, # push esi, pop ecx
36-
'EDI' => 'WY' + mod, # push edi, pop edi
62+
'EDI' => 'WY' + mod, # push edi, pop ecx
3763
}
3864

3965
reg.upcase!
40-
if (not regprefix.keys.include? reg)
66+
unless regprefix.keys.include?(reg)
4167
raise ArgumentError.new("Invalid register name")
4268
end
43-
return regprefix[reg]
4469

70+
case reg
71+
when 'EDX'
72+
mod_registers.concat(edx_regs)
73+
mod_registers.concat(nop_regs)
74+
mod_registers.push(Rex::Arch::X86::ECX)
75+
else
76+
mod_registers.push(Rex::Arch::X86::ECX)
77+
mod_registers.concat(mod_regs)
78+
end
79+
80+
mod_registers.uniq!
81+
modified_registers.concat(mod_registers)
82+
83+
return regprefix[reg]
4584
end
4685

47-
def self.gen_decoder(reg, offset)
86+
# Generates the decoder stub
87+
#
88+
# @param [String] reg the register pointing to the encoded payload
89+
# @param [Fixnum] offset the offset to reach the encoded payload
90+
# @param [Array] modified_registers accounts the registers modified by the stub
91+
# @return [String] the alpha upper decoder stub
92+
def self.gen_decoder(reg, offset, modified_registers = [])
93+
mod_registers = []
94+
4895
decoder =
49-
gen_decoder_prefix(reg, offset) +
96+
gen_decoder_prefix(reg, offset, mod_registers) +
5097
"V" + # push esi
5198
"T" + # push esp
5299
"X" + # pop eax
@@ -73,6 +120,18 @@ def self.gen_decoder(reg, offset)
73120
"JJ" + # jnz * --------------------
74121
"I" # first encoded char, fixes the above J
75122

123+
mod_registers.concat(
124+
[
125+
Rex::Arch::X86::ESP,
126+
Rex::Arch::X86::EAX,
127+
Rex::Arch::X86::ESI,
128+
Rex::Arch::X86::ECX,
129+
Rex::Arch::X86::EDX
130+
])
131+
132+
mod_registers.uniq!
133+
modified_registers.concat(mod_registers)
134+
76135
return decoder
77136
end
78137

modules/encoders/x86/alpha_mixed.rb

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def initialize
3131
# being encoded.
3232
#
3333
def decoder_stub(state)
34+
modified_registers = []
3435
reg = datastore['BufferRegister']
3536
off = (datastore['BufferOffset'] || 0).to_i
3637
buf = ''
@@ -41,8 +42,19 @@ def decoder_stub(state)
4142
buf = 'VTX630VXH49HHHPhYAAQhZYYYYAAQQDDDd36FFFFTXVj0PPTUPPa301089'
4243
reg = 'ECX'
4344
off = 0
45+
modified_registers.concat (
46+
[
47+
Rex::Arch::X86::ESP,
48+
Rex::Arch::X86::EDI,
49+
Rex::Arch::X86::ESI,
50+
Rex::Arch::X86::EBP,
51+
Rex::Arch::X86::EBX,
52+
Rex::Arch::X86::EDX,
53+
Rex::Arch::X86::ECX,
54+
Rex::Arch::X86::EAX
55+
])
4456
else
45-
res = Rex::Arch::X86.geteip_fpu(state.badchars)
57+
res = Rex::Arch::X86.geteip_fpu(state.badchars, modified_registers)
4658
if (not res)
4759
raise EncodingError, "Unable to generate geteip code"
4860
end
@@ -52,7 +64,15 @@ def decoder_stub(state)
5264
reg.upcase!
5365
end
5466

55-
buf + Rex::Encoder::Alpha2::AlphaMixed::gen_decoder(reg, off)
67+
stub = buf + Rex::Encoder::Alpha2::AlphaMixed::gen_decoder(reg, off, modified_registers)
68+
69+
# Sanity check that saved_registers doesn't overlap with modified_registers
70+
modified_registers.uniq!
71+
if (modified_registers & saved_registers).length > 0
72+
raise BadGenerateError
73+
end
74+
75+
stub
5676
end
5777

5878
#
@@ -69,4 +89,14 @@ def encode_block(state, block)
6989
def encode_end(state)
7090
state.encoded += Rex::Encoder::Alpha2::AlphaMixed::add_terminator()
7191
end
92+
93+
# Indicate that this module can preserve some registers
94+
def can_preserve_registers?
95+
true
96+
end
97+
98+
# Convert the SaveRegisters to an array of x86 register constants
99+
def saved_registers
100+
Rex::Arch::X86.register_names_to_ids(datastore['SaveRegisters'])
101+
end
72102
end

0 commit comments

Comments
 (0)