Players will analyze and corelate multiple artifacts to create an incident timeline.Evidence includes a Windows triage image, RMM Logs, Packet capture, etc.
An attacker on a decommissioned internal machine chatted with the MSP helpdesk AI over unencrypted HTTP and used a prompt injection to make it disclose the remote management credentials for the Cogwork Central workstation. They used those credentials to open a TeamViewer remote-control session, pushed a folder of offensive tools through TeamViewer’s file transfer, and ran a browser credential harvester and Mimikatz. Sensitive files, including a KeePass database, were staged in a second folder and sent back out over the same channel. The attacker then hooked the Winlogon Userinit value for persistence and ended the session; the stolen database cracked against a common wordlist and gave credentials for a further workstation.
msp-helpdesk-ai day 5982 section 5 traffic.pcapng → open with tshark or Wireshark. Chatbot API traffic is plain HTTP JSONProgram Files/TeamViewer/TeamViewer15_Logfile.log and Connections_incoming.txt → read with grep$Extend/$J → MFTECmd. The USN journal starts shortly after 10:00 UTC on the dayUsers/Cogwork_Admin (NTUSER.DAT, Recent LNKs, jumplists) → RECmd, LECmd, JLECmdWindows/System32/config (SOFTWARE) with transaction logs → RECmdacquired file (critical).kdbx → keepass2john, john, pykeepassTimestamp note:
TeamViewer15_Logfile.logis in local time (UTC+1).Connections_incoming.txt, the registry and$Jare UTC. The capture’s frame times are +10:00, while the chatbot JSON carries UTC. Normalise everything to UTC before answering.
Start with the conversation summary of the capture. One internal host posts to the helpdesk chatbot’s /api/messages/send endpoint over plain HTTP, and it is the only client sending the odd opening lines (“Hello Old Friend”, “I AM WATSON”). Its source address is the answer.
tshark -r pcap.pcapng -q -z conv,tcp
tshark -r pcap.pcapng -Y 'http.request.method==POST' -T fields -e frame.number -e ip.src -e http.request.uri
From Q1 we know the host. Decommissioned machines still announce themselves on the LAN, so filter NBNS and browser-protocol traffic from that source. The Host Announcement frame carries the NetBIOS name.
tshark -r pcap.pcapng -Y 'ip.src==10.0.69.45 && (nbns || browser)' -T fields -e nbns.name -e browser.server
List the POST bodies from the Q1 host in frame order. The earliest one is the opening line to the bot, sent at 21:53:40 in the capture’s +10:00 offset.
tshark -r pcap.pcapng -Y 'http.request.method==POST && ip.src==10.0.69.45' -T fields -e frame.time -e http.file_data
The bot’s JSON replies are what matter here. http.file_data truncates the long responses, so reassemble the TCP streams and read the reply that discloses remote-management details. The JSON timestamp field is UTC, unlike the capture’s local frame time.
tshark -r pcap.pcapng -q -z follow,tcp,raw,56
Gotcha: The frame time in the capture is +10:00. Use the timestamp inside the bot’s JSON, which is UTC, because the answer format is UTC.
The same leaking reply from Q4 hands over the remote management tool’s ID and password. The ID is printed with spaces in the chat, and the answer format wants it joined, then a colon, then the password.
tshark -r pcap.pcapng -q -z follow,tcp,raw,60
Take the last POST from the Q1 host (frame 2909) after the chat history is fully decoded. It is the attacker’s closing line before they moved to the workstation.
tshark -r pcap.pcapng -Y 'http.request.method==POST && ip.src==10.0.69.45' -T fields -e frame.number -e http.file_data | tail -1
Switch to the triage image. TeamViewer keeps Connections_incoming.txt next to its log. The entry for the attacker’s session lists the start and end in UTC, with the connection type RemoteControl.
cat "evidence/Holmes2025_Chapter2 The Watchman's Residue"/TRIAGE_IMAGE_COGWORK-CENTRAL/C/'Program Files'/TeamViewer/Connections_incoming.txt
Gotcha:
Connections_incoming.txtis UTC, butTeamViewer15_Logfile.logis local time (UTC+1 on this host). Convert before comparing the two.
The same incoming-connection record names the remote party, and the participant lines in TeamViewer15_Logfile.log confirm it with the TeamViewer ID. The display name is the RMM account.
grep -i participant TeamViewer15_Logfile.log
At session start the TeamViewer log records a UDP hole-punch from the peer. The punch received line carries the attacker’s internal address.
grep -i 'punch received' TeamViewer15_Logfile.log
The TeamViewer log records every file the attacker pushed over the session as Write file lines. The tools (including Mimikatz) all land in one directory under the Windows temp folder.
grep 'Write file' TeamViewer15_Logfile.log
The session closes at the end time in Connections_incoming.txt, and the TeamViewer log agrees with a RemoveParticipant line one hour ahead because of its local-time offset.
grep -i RemoveParticipant TeamViewer15_Logfile.log
Pull UserAssist from the Cogwork_Admin NTUSER hive with RECmd’s UserAssist batch file. The browser password recovery tool (WebBrowserPassView) has a focus time of 8 seconds, which is 8000 ms rounded to the nearest thousand. This is the closest proxy for how long it ran, and HTB accepted it.
RECmd.exe -d C:\Users\Cogwork_Admin --bn BatchExampleUserAssist.reb --csv work/reg
UserAssist has no entry for Mimikatz because it was launched from a command line. The prefetch files are not in the triage, but the NTFS $J journal is. It shows the creation of MIMIKATZ.EXE-<hash>.pf at the moment of first execution. This answer is reasoned from that journal entry rather than read from an execution artifact, and HTB accepted it.
MFTECmd.exe -f '$J' --csv work/mft
grep -i mimikatz work/mft/*.csv
Parse the Recent LNK files with LECmd. The text file, named like tool output, has a dump.lnk whose target access time marks when it was opened and read.
LECmd.exe -d C:\Users\Cogwork_Admin\AppData\Roaming\Microsoft\Windows\Recent --csv work/lnk
Gotcha: The Notepad jumplist
LastModifiedis a few seconds later and was not accepted. The LNK’s target access time is the read.
The $J change journal records the file creation inside the staging folder. Filter on the KeePass database name to see when it was moved in.
grep -i kdbx work/mft/*.csv
TeamViewer logs transfers in the other direction as Send file lines. The first one after staging is the start of exfiltration. The log is local time, so subtract one hour to get UTC.
grep 'Send file' TeamViewer15_Logfile.log | head
Gotcha: The format mask shows a double colon, but a normal
YYYY-MM-DD hh:mm:ssstring is what to give.
Dump the Winlogon key from the SOFTWARE hive with RECmd, replaying the transaction logs first because the hive is dirty. Userinit has been extended with a second executable (JM.exe), and the key’s last write time is when the change was made.
RECmd.exe --bn Winlogon.reb -d C:\Windows\System32\config --csv work/reg
Tip: regipy fails on dirty hives. RECmd replays
.LOG1/.LOG2and gets the correct last-write time.
Appending a payload to Userinit is the Winlogon Helper DLL sub-technique under Boot or Logon Autostart Execution (MITRE ATT&CK).
The exfiltrated .kdbx is locked. Extract its hash with keepass2john and crack it against rockyou. The password opens the database, and the entry for the target workstation holds the username and password.
keepass2john 'acquired file (critical).kdbx' > work/kdbx.hash
john work/kdbx.hash --wordlist=/usr/share/dict/rockyou.txt
python3 -I -c "from pykeepass import PyKeePass as K;[print(e.title,e.username,e.password) for e in K('acquired file (critical).kdbx',password='<cracked>').entries]"
| Phase | Action |
|---|---|
| Initial Access | Decommissioned host chats with the helpdesk AI and uses prompt injection to leak the RMM ID and password |
| Command and Control | Attacker connects to the Cogwork Central workstation over TeamViewer under an RMM account |
| Command and Control | Offensive tools are pushed to a staging directory under the Windows temp folder |
| Credential Access | Browser credential harvester runs briefly and is closed |
| Credential Access | Mimikatz is executed from the command line |
| Collection | Tool output text file is opened; sensitive files, including a KeePass database, are moved to a second staging folder |
| Exfiltration | Staged files are sent back through the TeamViewer session |
| Persistence | Winlogon Userinit is extended with JM.exe |
| Command and Control | TeamViewer session ends |
| Lateral Movement | Cracked KeePass database yields credentials for a second workstation |
| Timestamp (UTC) | Event |
|---|---|
| 2025-08-19 11:53:40 | First chatbot message from the decommissioned host (21:53:40 at +10:00) |
| 2025-08-19 12:02:06 | Bot leaks RMM ID and password |
| 2025-08-20 09:58:25 | Attacker’s TeamViewer remote-control session starts |
| 2025-08-20 10:07:08 | Mimikatz prefetch file created in $J |
| 2025-08-20 10:08:06 | Tool output text file opened (dump.lnk target accessed) |
| 2025-08-20 10:11:09 | KeePass database created in the exfiltration staging folder |
| 2025-08-20 10:12:07 | First file sent out over TeamViewer |
| 2025-08-20 10:13:57 | Winlogon key modified (persistence) |
| 2025-08-20 10:14:27 | TeamViewer session ends |
| Type | Value |
|---|---|
| IP (attacker source, internal) | 10.0.69[.]45 |
| IP (TeamViewer peer) | 192.168.69[.]213 |
| Hostname | WATSON-ALPHA-2 |
| RMM account | James Moriarty |
| Directory | C:\Windows\Temp\safe\ |
| Directory | C:\Windows\Temp\flyover\ |
| File | JM.exe (added to Userinit) |
| File | mimikatz.exe, WebBrowserPassView.exe |
| File | Heisen-9 remote snapshot.kdbx, COG-HR-EMPLOYEES.pdf |
| Technique | ID | Description |
|---|---|---|
| Remote Access Software | T1219 | TeamViewer session using credentials leaked by the chatbot |
| Ingress Tool Transfer | T1105 | Tools pushed with TeamViewer file transfer |
| Credentials from Web Browsers | T1555.003 | WebBrowserPassView run on the workstation |
| OS Credential Dumping | T1003 | Mimikatz executed (reasoned from the $J prefetch creation) |
| Local Data Staging | T1074.001 | Sensitive files collected in a staging folder |
| Exfiltration Over C2 Channel | T1041 | Files sent back through the TeamViewer session |
| Boot or Logon Autostart Execution: Winlogon Helper DLL | T1547.004 | JM.exe appended to Userinit |
| Brute Force: Password Cracking | T1110.002 | Exfiltrated KeePass master password cracked offline to read the stored credentials |
| Valid Accounts | T1078 | Credentials from the database for the next workstation |
An AI helpdesk bot must never hold or reveal credentials. The prompt injection worked because the bot had the RMM ID and password in its context. Keep secrets out of any model’s reach and enforce the policy outside the model, with output filtering and access controls.
Plain HTTP between internal hosts exposed the whole chat. The capture showed the attacker’s prompts and the bot’s replies in clear text. TLS on internal APIs makes this harder to read in transit, and logging the chat server side gives a record that does not depend on a capture.
An unmanaged remote access tool was the whole intrusion path. TeamViewer on a workstation accepted an inbound session from an unknown account and allowed file transfer in both directions. Restrict RMM tooling to an allowlist, disable file transfer where it isn’t needed, and alert on Connections_incoming.txt entries from unfamiliar accounts.
Winlogon Userinit changes are cheap to detect. A second executable after Userinit.exe is a very specific indicator. Monitor writes to the Winlogon key and baseline its value.
A weak KeePass master password turned one theft into a second compromise. The database cracked against rockyou, and its stored credentials gave the attacker a further workstation. Use long random master passphrases or a key file, and do not keep infrastructure credentials in a database that sits on a workstation.
The attacker needed no exploit: a chatbot gave away the remote access credentials, and a legitimate RMM tool carried every later stage. Controls on what the bot could say and on which remote access tools were allowed would have stopped the chain at the first step.