Hunting APT29 Part 4: I Found Hardcoded Credentials in a Single Command Line
Inside The Breach #4
The clearest evidence of lateral movement in the entire APT29 dataset fit in one command line. No obfuscation, no evasion, just a username, password, and remote payload path sitting in plain text where anyone running the right query could see it.
ProcessID 1412 generated 75 events over 5 minutes, but the first event at 23:13:27 showed me everything I needed to know about how APT29 moved from the initial compromised machine to a second target.
If you haven’t been following this series:
This is Part 4 of my APT29 hunting analysis. I’m working through 196,071 Sysmon events from the MITRE ATT&CK Evaluations dataset in Splunk, tracking a full adversary simulation from initial access through lateral movement.
- This is Part 1 of an ongoing series where I work through the MITRE ATT&CK Evaluations APT29 dataset in Splunk. No vendor tools. No shortcuts. Just logs, SPL, and a methodology built over two years of self-teaching.
Part 4 is about what happened after the credentials were stolen.
The Parent Process That Started Everything
I was already tracking the Python implant from Part 2 when I noticed something in the process tree. PowerShell (ProcessID 3852) spawned a new process at 23:13:27. That parent PowerShell was the same one that had accessed lsass with full permissions 8 minutes earlier.
The credentials were dumped at 23:05:16. The lateral movement started at 23:13:27. That’s an 8-minute gap between stealing credentials and using them.
The query I ran:
index=apt29 ProcessId=1412 | stats count by EventID | sort -countResult: 75 events across 10 different EventIDs.
- EventID 7: 54 events (image loads, mostly DLLs)
- EventID 5158: 3 events (Windows Filtering Platform permitted bind to local port)
- EventID 5156: 3 events (Windows Filtering Platform permitted connection)
- EventID 18: 2 events (named pipe connected)
- EventID 3: 1 event (network connection detected)
- EventID 5: 1 event (process terminated)
- EventID 13: 1 event (registry value set)
- EventID 12: 1 event (registry key created)
- EventID 22: 1 event (DNS query)
- EventID 1: 1 event (process create)
EventID 1 is always where I start. Process creation logs show what was executed, by whom, and with what parameters.
The Command Line That Gave Everything Away
Here’s what I found in the CommandLine field:
"C:\Program Files\SysinternalsSuite\PsExec64.exe" -accepteula \\NASHUA -u dmevals\pbeesly -p Fl0nk3rt0n!T0by -i 2 C:\Windows\Temp\python.exeLet me break down every part of this because each piece tells you something about attacker behavior.
The binary: PsExec64.exe from Sysinternals Suite. This is a legitimate Microsoft tool used for remote process execution. IT admins use it every day for legitimate remote administration. Attackers use it for lateral movement because it’s already trusted and often whitelisted in security tools.
The flag: -accepteula automatically accepts the End User License Agreement so the tool runs without user interaction. This is standard practice for scripted or automated use.
The target: \\NASHUA is the remote machine hostname. I’ll come back to this in a minute when I show you the DNS query.
The credentials: -u dmevals\pbeesly -p Fl0nk3rt0n!T0by provides explicit username and password. The domain is dmevals, the user is pbeesly, and the password is right there in cleartext on the command line.
No legitimate administrator puts passwords directly in command line arguments. It’s a massive security violation. Every command line parameter is logged in Windows Event Logs, Sysmon, EDR tools, and SIEM platforms. The password sits there permanently in every log that captured this event.
But attackers do it because they already compromised the credentials through credential dumping (Part 2 of this series showed PowerShell accessing lsass). They don’t care about password security at this point. They care about speed and reliability. Hardcoding credentials in the command line guarantees the authentication works without dealing with Kerberos tickets, NTLM relays, or pass-the-hash complexity.
The session flag: -i 2 specifies interactive session ID 2. This means the remote process runs in the context of a specific user session on the target machine, not as a background service. Session 2 is typically a logged-in user desktop session.
The payload: C:\Windows\Temp\python.exe is the executable PsExec will run on the remote target (NASHUA). Notice the path. This isn’t a standard Python installation location. Legitimate Python installs go in Program Files or user AppData directories. C:\Windows\Temp is a staging location, which means the attackers already dropped the Python interpreter on the target machine before executing this PsExec command.
This single command line documents the entire lateral movement technique: tool used (PsExec), target system (NASHUA), authentication method (explicit credentials), and payload (Python interpreter in Temp directory).
The Network Evidence (Ports, DNS, and Two Connections)
PsExec requires network connectivity to work. Specifically, it needs access to port 135 (RPC Endpoint Mapper) and port 445 (SMB). I found both in the logs.
EventID 22 at 23:13:27 (same timestamp as process creation):
- Query Name: NASHUA
- Query Result: 10.0.1.6
- Query Status: 0 (success)
The attacker’s machine (10.0.1.4) resolved the hostname NASHUA to IP address 10.0.1.6 before initiating the PsExec connection. This DNS query happened in the same second as the PsExec process creation, which means it was part of the automated execution flow.
EventID 3 shows the outbound network connection:
- SourceIP: 10.0.1.4
- SourcePort: 60022
- DestinationIP: 10.0.1.6
- DestinationPort: 135
- Protocol: tcp
- Image:
C:\Program Files\SysinternalsSuite\PsExec64.exe - Initiated: true
Port 135 is the RPC Endpoint Mapper. PsExec connects to this port first to negotiate which dynamic high port will be used for the actual remote service creation and process execution. After the initial handshake on 135, the communication moves to SMB on port 445.
EventID 5156 (Windows Filtering Platform) shows three permitted connections. WFP is the Windows firewall logging. The firewall explicitly allowed the outbound connection from PsExec64.exe to 10.0.1.6 on port 135. If there were firewall rules blocking lateral movement tools, this connection would have been denied and logged as EventID 5157 instead.
According to my notes, there were two separate network connections:
- First connection at 11:14:44 PM (23:14:44 in 24-hour format)
- Second connection at 11:18:00 PM (23:18:00)
That’s roughly 3 minutes and 16 seconds between the first connection and the second. The first connection establishes the remote service and executes the payload. The second connection could be a keepalive, a status check, or a cleanup operation. PsExec often makes multiple connections during a single execution.
Get Manish Rawat’s stories in your inbox
Join Medium for free to get updates from this writer.
The process terminated cleanly at 11:18:20 PM (23:18:20), which is 20 seconds after the second network connection. Clean termination with no errors means the lateral movement succeeded.
The Registry Artifacts PsExec Leaves Behind
[Screenshot 5: EventID 12 and 13 showing registry operations]
EventID 12 (registry key created):
- TargetObject:
HKLM\...\Software\sysinternals\PsExec
EventID 13 (registry value set):
- Details: DWORD value or binary data
PsExec writes to the registry for two reasons:
- The
-accepteulaflag writes a registry key to record that the user accepted the license agreement. This prevents the EULA prompt from appearing on subsequent runs. - PsExec may write configuration settings or execution history to registry keys under the Sysinternals registry path.
These registry artifacts persist after the process terminates. Even if the attacker deletes PsExec64.exe from disk, the registry keys remain as evidence of execution. In forensic investigations, these registry artifacts can be used to establish a timeline of when PsExec was first used on a system and how many times it ran.
For detection engineering, you can write a rule that alerts on registry writes to the Sysinternals PsExec path combined with network connections to port 135 or 445. That correlation gives you high confidence that PsExec lateral movement occurred, even if you don’t have visibility into the command line arguments.
The Named Pipe That Blends In
EventID 18 (pipe connected):
- PipeName:
\wkssvc - Image:
C:\Program Files\SysinternalsSuite\PsExec64.exe
Named pipes are inter-process communication mechanisms in Windows. Legitimate Windows services use named pipes constantly. The Workstation service uses \wkssvc. The Server service uses \srvcsvc. LSASS uses \lsass. These are well-known pipe names that appear thousands of times per day in normal Windows operations.
PsExec uses named pipes to communicate with the remote service it creates on the target machine. By connecting to \wkssvc, PsExec blends its network traffic with legitimate Workstation service traffic. If you’re monitoring named pipe connections and you see \wkssvc, you can’t immediately assume it’s malicious because that pipe is used by normal Windows operations.
But here’s the difference: the pipe connection from PsExec64.exe is outbound to a remote machine (10.0.1.6), whereas legitimate Workstation service pipe usage is typically local or server-initiated. The combination of PsExec64.exe + outbound connection + \wkssvc pipe + port 135 network traffic is a stronger indicator than any single data point alone.
This is why detection engineering requires multiple telemetry sources. If you only log process creation, you see PsExec running but you don’t know if it connected anywhere. If you only log network traffic, you see port 135 connections but you don’t know which process initiated them. If you only log named pipes, you see \wkssvc and assume it’s legitimate Windows activity.
Put them all together and the picture becomes clear: PsExec initiated an outbound connection to 10.0.1.6 on port 135, authenticated with hardcoded credentials, executed a Python payload in a remote Temp directory, used named pipes mimicking legitimate Windows services, and left registry artifacts documenting the execution.
The 54 DLL Loads That Tell You What PsExec Does
The majority of ProcessID 1412’s activity was EventID 7 (image loaded), which logged every DLL that PsExec loaded during execution. 54 DLL loads in total.
I didn’t include a screenshot of all 54 because it’s not particularly interesting to look at, but the first DLL load was dnsapi.dll at 23:13:28, which makes sense given that PsExec needed to resolve the NASHUA hostname to an IP address.
Other notable DLLs in the list:
ntdll.dll(core Windows API)kernel32.dll(process and thread management)advapi32.dll(Windows advanced API, includes registry and service functions)ws2_32.dll(Winsock, needed for network communication)secur32.dll(security functions, authentication)
These are all standard Windows system libraries that any legitimate network tool would load. There’s nothing inherently suspicious about the DLL load pattern. The suspicion comes from context: who executed PsExec (PowerShell that previously dumped credentials), what command line arguments were used (hardcoded password), and where it connected (lateral movement to a second machine).
This is why threat hunting isn’t just pattern matching. You can’t write a detection rule that says “alert if PsExec loads ws2_32.dll” because every network application loads that DLL. You have to look at the full behavior chain.
What the Logs Don’t Show (And Why That Matters)
Here’s what I didn’t find in the logs for ProcessID 1412:
No EventID 11 (file created). PsExec didn’t create any files on the attacking machine. The payload (python.exe) was already staged on the target before PsExec ran.
No EventID 23 (file deleted). No cleanup operations on the attacking side. PsExec64.exe stayed on disk in Program Files\SysinternalsSuite after execution completed.
No errors or failed connections. Every network connection succeeded. Every authentication attempt worked. The operation was clean from start to finish.
The absence of certain events is just as important as the presence of others. If I had seen file creation in C:\Windows\Temp followed by immediate deletion, that would indicate the attacker was cleaning up after themselves. If I had seen failed authentication attempts before the successful one, that would indicate credential guessing or brute force.
Instead, I saw a surgical operation: resolve DNS, connect to port 135, authenticate with stolen credentials, execute remote payload, maintain connection for 3 minutes, disconnect cleanly, terminate process. No wasted actions, no failed attempts, no cleanup because nothing needed cleaning.
This is what a practiced adversary looks like in the logs.
Why This Matters for Detection Engineering
The APT29 simulation used PsExec because it’s effective and difficult to distinguish from legitimate administrative activity. Every element of this lateral movement technique has a legitimate use case:
- PsExec is a trusted Microsoft Sysinternals tool
- Port 135 and 445 are required for Windows remote administration
- Named pipes are standard Windows IPC mechanisms
- Registry writes to Sysinternals paths happen during normal tool usage
- DNS queries for hostnames are constant background noise in any network
The only clearly malicious indicator is the hardcoded password in the command line, and even that could theoretically be explained as “bad but not malicious” if an administrator scripted PsExec poorly.
This is why context matters. You need to know:
- What process spawned PsExec? (In this case: PowerShell that previously dumped credentials)
- What is the parent process chain? (Traced back to the initial screensaver execution in Part 3)
- What happened before this? (Credential access 8 minutes earlier)
- What happened after this? (Remote Python payload execution)
Without that context, ProcessID 1412 looks like routine IT administration. With context, it’s confirmed lateral movement as part of a multi-stage attack.
That’s the entire point of threat hunting. You’re not looking for isolated suspicious events. You’re looking for chains of individually-benign events that form a malicious pattern when connected.
What’s Next
This is Part 4 of the APT29 series. I’ve now documented:
- Initial access and persistence (Part 1)
- Credential dumping and collection (Part 2)
- Full execution chain and evasion techniques (Part 3)
- Lateral movement (Part 4)
Follow me on Github to get a SIGMA Detection Rule.
I have data on 4 more techniques in this dataset that I’m still analyzing. But here’s what I’m building alongside the research: Sigma detection rules for every technique I’ve covered.
Each rule will be tested against the actual APT29 data, converted to Splunk SPL, and validated for false positives. The goal is a complete detection pack that covers the entire attack chain with production-ready rules that you can drop into your SIEM today.
If you’re interested in that work, I’ll be releasing more details soon.
Final Thoughts
Lateral movement is the phase where attackers transition from “we compromised one machine” to “we own the network.” It’s the most critical detection point in the kill chain because stopping lateral movement contains the breach.
PsExec is one tool. There are dozens of others: WMI, DCOM, scheduled tasks, WinRM, RDP, SMBExec, and custom tools written by specific threat groups. Each one leaves slightly different artifacts in the logs.
But they all share common elements: authentication, network connections, remote process execution, and often registry or file system artifacts. If you understand how to hunt for one lateral movement technique, you have a framework for hunting all of them.
The work I’m doing in this series is building that framework. Take the investigation methodology from one technique and apply it to others. Track process trees. Correlate network connections with parent processes. Look for authentication patterns that don’t match normal user behavior. Build detection rules that fire on behavior chains, not individual events.
That’s how you find adversaries in 196,071 logs.
About this series: I’m analyzing the MITRE ATT&CK Evaluations APT29 dataset in Splunk, documenting every technique I find, and writing detection rules for each one. This is independent research. No vendor affiliations, no marketing, just threat hunting and detection engineering.
If you want to follow along, all previous parts are available:
- Part 1: Hunting APT29 in 196,071 Logs
- Part 2: One Log Entry, 1,129 Events
- Part 3: I Traced the Process Tree Back to the Beginning
I’m currently looking for remote SOC analyst or detection engineering opportunities. If your team needs someone who knows how to hunt in Splunk, write detection rules, and investigate security incidents, let’s talk. You can reach me at rawatmanish21@outlook.com or connect on LinkedIn.
Share this post if you found it useful. More people should understand how lateral movement actually looks in SIEM logs.










