Incident Investigation Walkthrough: Splunk Boss of the SOC v1

2026-08

Context

Boss of the SOC (BOTS) is Splunk's blue-team CTF dataset: real log data (or realistic recreations of it) from a simulated breach, released for anyone to practise investigation on. Version 1, from 2016, centers on two linked stories: an external attacker ("Po1s0n1vy") defacing a Wayne Enterprises property site, imreallynotbatman.com, and a separate ransomware incident (Cerber) that hits an internal workstation at Wayne Enterprises weeks later.

The goal of this exercise was to set up my own Splunk instance, load the BOTS v1 dataset into it, and work the investigation myself using SPL, treating it exactly like a live incident: form a hypothesis, write a query, see what the data says, and follow wherever it leads, including the dead ends.

Setup

Splunk Enterprise was already running locally. BOTS v1 ships in a few formats; the officially recommended one is a pre-indexed archive that has to be extracted directly into Splunk's app directory and loaded via a restart, not something a browser can do on its own, so that one step (extracting botsv1-attack-only.tgz, the 135MB attack-only subset, and restarting Splunk) was run manually in a terminal. Everything else, the download, the verification, and the entire investigation, was done through the Splunk web UI.

Once the restart finished and indexing settled, index=botsv1 earliest=0 confirmed the load: 955,807 events across 22 sourcetypes.

Sourcetype breakdown:

SourcetypeEvents
XmlWinEventLog:Sysmon270,597
stream:smb151,568
suricata125,584
WinEventLog:Security87,430
WinRegistry74,720
stream:ip62,083
fgt_traffic55,279
stream:tcp28,291
fgt_utm25,586
stream:http23,936
iis22,615

The heavy weighting toward Sysmon, WinRegistry and stream:smb told me upfront that the attack-only subset would have much richer coverage of the endpoint/ransomware side of the story than the initial web-application compromise, which turned out to be exactly right, and shaped where the investigation could and couldn't go.

The investigation

1. First indicator of compromise: who's scanning the website?

index=botsv1 sourcetype=stream:http imreallynotbatman.com
| stats count by src_ip, http_user_agent | sort -count

One source IP, 40.80.148.42, accounts for 1,928 of the 10,010 matching HTTP-stream events, using a Chrome-spoofed user agent string. A follow-up search for the literal string "acunetix" scoped to that IP returns 17,469 hits, and a search for "joomla" against the site returns 19,751. So before anything else happens, someone is running the Acunetix web vulnerability scanner against a Joomla site. That's the first indicator of compromise: automated reconnaissance, well before any exploitation.

2. Initial access: how did they get in?

I tried to find the actual defacement (the modified homepage / uploaded image) first, since that's usually the most visible artifact of this kind of attack:

index=botsv1 sourcetype=stream:http dest_ip=192.168.250.70 (url=*batman* OR url=*poison*)
| table _time, url, http_method, src_ip

Zero results. So was sourcetype=iis (poison OR batman), and a plain keyword search for "poison" scoped to the web server. This is one of the real dead ends of working from the attack-only (135MB) package instead of the full 6.1GB dataset: that particular artifact doesn't appear to have made the cut into the smaller subset. Rather than keep guessing at field names, I went looking for the exploitation itself instead:

index=botsv1 dest_ip=192.168.250.70 sourcetype=stream:http "multipart/form-data"

Four events. One of them is the payload: a POST to /joomla/administrator/index.php?option=com_extplorer (Joomla's file-manager component) with a multipart body containing userfile[0] = 3791.exe and userfile[1] = agent.php, complete with the raw MZ/PE header ("This program cannot be run in DOS mode") sitting in the capture, and a server response of 'success':true,'message':'Upload successful!'. Timestamp: 2016-08-10 23:52:47 UTC, same source IP as the recon traffic.

I went looking for an MD5 hash of that executable next, since that's usually asked in these exercises. file_hash and md5 came back empty on that event. Splunk Stream wasn't configured to compute file hashes in this environment, so that particular fact isn't recoverable from the network capture alone; it would need an endpoint-side artifact (which, per the timeline below, lands on a different host two weeks later).

3. A second, separate compromise: the ransomware side

The web-server attack is dated August 10. Searching more broadly for ransomware indicators turns up a cluster of activity from August 24, two weeks later, and on a different machine entirely:

index=botsv1 we8105desk | stats count by host, sourcetype | sort -count

we8105desk is a literal hostname in the data: a Windows workstation generating 117,611 Sysmon events and 34,289 Security events, logged in as WAYNECORPINC\bob.smith. This is clearly a separate intrusion chain, not a continuation of the web app compromise.

Pulling Sysmon process-creation events on that host around the same timeframe:

index=botsv1 host=we8105desk sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" (.tmp OR .vbs OR cerber)

The Microsoft Sysmon Splunk add-on wasn't installed (it's one of seven prerequisite TAs listed for the full dataset), so these came back as raw, unparsed XML instead of clean CommandLine/Image fields, a second honest limitation of a minimal setup. Reading the raw events anyway: at 18:17:31 UTC, csc.exe (the C# compiler) spawns cvtres.exe, compiling code staged into %TEMP% (RES958E.tmp / CSC958D.tmp). That's a living-off-the-land pattern, using a trusted, signed Microsoft binary to compile and run a payload in memory, consistent with a VBScript or macro-based dropper that's trying to avoid writing an obviously malicious executable to disk.

4. Ransomware detonation

index=botsv1 cerber | stats count by sourcetype

21,596 events reference "cerber" across fgt_traffic, fgt_utm, WinEventLog:Security, stream:smb, and suricata. Narrowing to the firewall's IPS signatures:

index=botsv1 cerber sourcetype=fgt_utm | table _time, msg, srcip, dstip

At 18:49:41 UTC, 32 minutes after the in-memory compile, the FortiGate fires 9,231 "Botnet: Cerber.Botnet" alerts in the same second, all from 192.168.250.100 (we8105desk's IP), each one to a different address in the 85.93.63.0/24 block. That's a known Cerber technique: instead of one static C2 domain that's easy to blocklist, the payload sprays connection attempts across an entire /24 of decoy addresses.

5. Impact: does it stay on one machine?

index=botsv1 sourcetype=stream:smb (cerber OR readme OR ransom) | stats count by src_ip, dest_ip

152 SMB sessions from 192.168.250.100 go to 192.168.250.20, a file server, not the infected workstation, carrying ransom-note-related keywords. The ransomware reached at least one shared network drive, which is the difference between "one user's laptop is down" and "we need to check what else was mapped to that share."

Timeline

View the interactive timeline →

Time (UTC)PhaseEvent
2016-08-10Recon40.80.148.42 scans imreallynotbatman.com with Acunetix
2016-08-10 23:52:47Initial access3791.exe + agent.php uploaded via Joomla com_extplorer
2016-08-24 18:17:31Endpoint compromisecsc.exe → cvtres.exe in-memory compile on we8105desk (bob.smith)
2016-08-24 18:49:41Ransomware detonationCerber C2 burst, 192.168.250.100 → 85.93.63.0/24
2016-08-24 (following)Lateral impactRansomware reaches file server 192.168.250.20 over SMB

What I'd check next

With the full 6.1GB dataset instead of the attack-only subset, the obvious next steps would be: recover the actual defacement artifact and the brute-force login attempts against the Joomla admin panel (both absent from this smaller package), pull a proper MD5/SHA256 on 3791.exe from an endpoint-side Sysmon file-creation event once the Sysmon TA is installed, and count how many files on the file server were actually touched or renamed by Cerber.

Lessons from doing this "for real" instead of just reading a walkthrough

← back to all posts