Players will use Threat Intelligence services to link together a series of cyber crime activities.
An attacker probed a honeypot web application with a custom, branded User-Agent, got past the WAF and planted a PHP web shell, then pulled a SQL database dump out over HTTP a few days later. A short marker string recurred across the whole intrusion: in the User-Agent, in the names of the files they created and in the beacon identifier. Searching the CTI platforms for that tag tied the activity to a cluster of campaigns attributed to one actor, all using the same malware. Pivoting on that sample’s hash led to its C2 address, the file it drops for persistence, and a scanner profile of the C2 host that exposed its ports, its owner and a taunting service banner.
cat and grepcat and grepcat and grepcurl plus grep or Python reads it as easily as the browser doesTimestamp note: The logs carry dates in 2025-05; none of the questions need a timestamp answer, but the order (first contact, web shell, dump) is what makes the story.
Read access.log from the top. The earliest requests are the attacker’s first contact (2025-05-01 08:23:12) and their User-Agent is hand-built, not a browser or scanner default: a tool name, a slash, a short token, a hyphen and a second name. That fits the format mask exactly, and the short token is the marker that returns in Q4.
cat access.log
application.log and waf.log agree on the moment of the bypass: a new PHP file is created at 2025-05-15 11:25. Its name carries the same token as the User-Agent from Q1, which ties the file to the same actor.
cat application.log waf.log
Tip:
waf.loghas anERRORline with a garbled field. The bytes are already replaced with U+FFFD in the file, so there is nothing to recover with a hex dump.application.loghas the filename in clear.
On 2025-05-18 access.log shows the attacker downloading a SQL dump over plain HTTP. The filename follows the same naming convention as the web shell, with the format mask ********_****_****.*** fitting a database_dump_<token>.sql name.
cat access.log
Compare Q1 to Q3: the same four-character token sits in the User-Agent, the web shell name and the dump name, and it is also the beacon identifier in the logs. A string that shows up in a header, in filenames and in a beacon is a deliberate actor tag, and it is the search term for everything that follows.
cat access.log application.log waf.log
OmniYard-3 is a single-page app, so fetch the page and then its script bundles. The Intel Graph data is a STIX-style set of entities and relationships hardcoded in the bundle. Filter the relationships for campaigns with an attributed-to link to the actor identity (identity--threat-actor-jm): five campaigns come back (bio-breach, civic-disruption, neural-storm, quantum-heist, transport-chaos).
curl -s http://<omniyard-host>:<port>/ | grep -o 'src="[^"]*\.js"'
curl -s http://<omniyard-host>:<port>/<bundle>.js | grep -o 'attributed-to[^}]*'
Hint: There is no API to query. If the page looks empty to
curl, the data is in the bundle, not behind an endpoint.
From Q5 take the five campaign IDs and list their uses relationships. They resolve to five malware entities and four tools, because transport-chaos has no tool attached, for nine in total.
curl -s http://<omniyard-host>:<port>/<bundle>.js | grep -o '"relationship_type":"uses"[^}]*'
Of the malware entities from Q6, one is linked to every campaign. Its SHA-256 indicator appears five times in the graph data, once per campaign, which is the signal that it is the shared implant. Pull the 64-hex string out of the bundle with grep -o.
curl -s http://<omniyard-host>:<port>/<bundle>.js | grep -oE '[0-9a-f]{64}' | sort | uniq -c | sort -rn
CogWork is the same kind of SPA. Search its bundles for the Q7 hash and read the scan entry for it. The network activity section lists the C2 address the sample calls out to.
curl -s http://<cogwork-host>:<port>/<bundle>.js | grep -o '7477c4f5e6d7c8b9[^}]*'
The same scan entry holds the behaviorAnalysis block. Under fileOperations, the create action names the script the implant drops to survive reboots. The 4a4d in the filename is the actor tag from Q4 again.
curl -s http://<cogwork-host>:<port>/<bundle>.js | grep -o 'fileOperations[^]]*'
On CogNet, find the host entry for the C2 address from Q8 and count the elements of its ports array. The bundle is one minified line, so a regex with a bounded wildcard hangs; slice it in Python with str.index from the IP to the end of the array instead.
python3 -I -c "import sys;s=open(sys.argv[1]).read();i=s.index('74.77.74.77');print(s[i:i+1500])" cognet_bundle.js
The same host entry carries an organization field. It is the hosting provider that owns the address, not the actor’s own name.
python3 -I -c "import sys,re;s=open(sys.argv[1]).read();i=s.index('74.77.74.77');print(re.findall(r'organization[^,]*',s[i:i+3000]))" cognet_bundle.js
Step through the services in the Q10 ports array. Most banners are product strings; the one on port 7477 is a sentence. The long format mask is the hint that you are looking for a quote with an attribution after the hyphen.
python3 -I -c "import sys;s=open(sys.argv[1]).read();i=s.index('7477',s.index('74.77.74.77'));print(s[i:i+400])" cognet_bundle.js
Gotcha: Copy the banner exactly, including the apostrophe in “He’s” and the spacing around the final hyphen. The attribution after the hyphen is part of the banner.
| Phase | Action |
|---|---|
| Initial Access | First requests to the honeypot with a branded User-Agent (2025-05-01) |
| Initial Access | WAF bypassed |
| Persistence | PHP web shell created on the server (2025-05-15) |
| Exfiltration | SQL database dump downloaded over HTTP (2025-05-18) |
| Command and Control | Marker string reused in the User-Agent, filenames and beacon identifier |
| Command and Control | Shared implant calls out to a single C2 host and drops a persistence script on victims |
| Type | Value |
|---|---|
| User-Agent | Lilnunc/4A4D - SpecterEye |
| File (web shell) | temp_4A4D.php |
| File (exfiltrated) | database_dump_4A4D.sql |
| Marker string | 4A4D |
| SHA-256 (implant) | 7477c4f5e6d7c8b9a0f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9e0d17477 |
| IP (C2) | 74.77.74[.]77 |
| File (persistence) | /opt/lilnunc/implant/4a4d_persistence.sh |
| Technique | ID | Description |
|---|---|---|
| Exploit Public-Facing Application | T1190 | Attack on the honeypot web app that got past the WAF |
| Server Software Component: Web Shell | T1505.003 | PHP web shell dropped on the server |
| Exfiltration Over C2 Channel | T1041 | Database dump pulled back over HTTP |
| Application Layer Protocol: Web Protocols | T1071.001 | Implant network activity to the C2 address reported by the sandbox |
A WAF bypass was followed by a file write, and that write was visible. The web shell creation shows in both application.log and waf.log. Alert on new executable files appearing in web-served directories, especially names such as temp_*.php.
Custom User-Agents are cheap, high-signal detections. The attacker’s first request already carried a branded string. Unusual or hand-built User-Agents are worth a watchlist and a pivot into the other logs.
Database dumps should never be web-reachable. The exfiltrated file was fetched with a plain HTTP request. Keep dumps out of the document root and alert on downloads of .sql files.
Reused marker strings link an intrusion to a wider actor. The same token in the User-Agent, filenames and beacon is what let the CTI platforms connect this honeypot hit to five campaigns and one shared implant. Extract such strings as indicators early, then pivot from them to hashes, C2 addresses and persistence paths for hunting.
The attacker went from first probe to web shell to database dump over about two and a half weeks, and tagged everything with one marker string. That tag is the weakness: it ties every artifact to five campaigns, one implant and one C2 host, and it is the thread that turns a honeypot log into an attribution.