← All posts

Targeted Attacks that Slip by in the Noise

Austin Kauffman ·
#mcp#siem#threat-hunting#fingerprinting#security

I asked an agent a question I used to spend a day doing:

Look at the last week of request logs. What is getting through that should not? What looks like a real exploit, not a scanner? Write the policy.

The agent was not staring at a dashboard. It was talking to two MCP servers behind the coreX MCP gateway: the coreX control plane, and the OpenSearch SIEM that holds HAProxy request logs — including the decoded request fingerprint.

It came back with CVEs, body shapes, four named actors, and two list-backed block rules. I applied config. That is the loop this product is for.

Why MCP on the SIEM changes the work

Threat hunting is not “ask the model about security.” It is: query 2,191,000 documents, group by JA4 and req_fp subfields, subtract the scanners you already know, then look at what is left with the policy context still in the conversation.

Without MCP, that is Kibana, a notepad, and a change ticket. With MCP:

  1. The agent queries corex-log-* for the last 7 days.
  2. It reads the live security rules and JA4 lists from coreX.
  3. It notices the request fingerprint already decoded the JSON body shape even though the body is not in the log.
  4. It proposes lists and rules in the same expression language the GUI uses.
  5. The MCP gateway policy lets it create lists and rules, and refuses apply_config. A human still hits Apply.

That last point is the whole design. Analysts and agents get context. Owners change production. I wrote about that split in What an AI Agent Finds When You Connect It to Your Security Stack and SIEM. This post is what that looks like on a real week of traffic.

2,191,000 requests, most of it noise

The week was loud. Miteflux spraying WordPress batch endpoints with Chrome/120 and a TLS 1.3 + HTTP/1.1 JA4. A secret-file crawler with a truncated Chrome UA and header list ahuxxxx. GCP bots wearing Googlebot and GPTBot. Vite /@fs/proc/self/environ with raw??. CONNECT to icanhazip.com. Classic internet.

The agent’s first useful move was not writing a rule for any of that. It was throwing it out so the rest of the session could look at what was not a spray.

What remained fell into two piles: exploit families (people probing stacks they think you run) and targeted clients (people who already know what you actually run).

The fingerprint is the body you did not log

HAProxy request logs do not keep POST bodies. They do keep req_fp. Seventeen fields. The ones that matter for this hunt are the parameter fields:

FieldWhat it captures
param_keysFirst letter of each JSON/query key, sorted
param_typesType of each value: s string, o object, l list, i int
param_lensLength of each value

That is enough to tell a legitimate password-reset from a SQL injection, without storing the injection.

Metabase unauth SQLi — CVE-2026-72898

This was a 0-day in August. ShinyHunters used it against Metabase Cloud. Entry point: POST /api/session/reset_password. A real reset is two strings (password, token). The exploit puts an object where a string belongs, or adds a third field.

The week’s probes looked like this:

Shapeparam_keysparam_typesparam_lens
Detectorptso14-12 / 14-90
Exploit-shapedptusso14-6-82

Twenty-four POSTs across public hostnames. Nine returned HTTP 200 from an origin — 1,181 bytes in ~1 ms. That is a static HTML catch-all, not Metabase executing SQL. There was no follow-up GET /api/user/current, which is the documented post-exploit check.

So: not a compromise. Still a policy you can write the moment you see so on that path:

http.request.uri.path contains "/api/session/reset_password"
and http.request.fingerprint.param_types contains "o"

A labeled scanner used metabase-cve-2026-72898-detect/1.0. A hosting ASN reused the same body shape with Edge / Firefox / Safari UAs. That second group is not inventory. That is hiding a KEV exploit as a browser.

WordPress “wp2shell” — CVE-2026-63030

Pre-auth REST batch route confusion, chained to SQLi, to RCE. Patched in 6.9.5 / 7.0.2. CISA KEV.

Fingerprint: POST to /wp-json/batch/v1 or /?rest_route=/batch/v1 with JSON types l / ls (a nested list — the malformed batch). One ASN did this at volume. A four-request probe signed cve-2026-63030/1.0 hit the query-string form and was denied.

No author__not_in in the query string, so the SQLi half of the chain did not run here. Origins that returned 200 on batch paths did it with a ~600 byte body — the same catch-all HTML as fake /graphql on those hosts. Again: not evidence WordPress executed the batch. Evidence the internet is treating every hostname as WordPress 7.0 until proven otherwise.

Next.js / Vite disk reads

2,466 GETs. /@fs/proc/self/environ, /@fs/.env, /@fs/root/.aws/credentials, plus RSC filename=file:///proc/self/environ&environmentName=rsc. Almost all 403 or 302. If any origin is actually Vite or Next in a loose config, that is the class of bug these payloads are for. The edge was already dropping them.

Four actors that were not scanners

Volume scanners rotate paths. These did not.

Actor A — an MCP client that knows the protocol

NetworkA Spanish hosting ASN plus Zenlayer
JA4t13d1812h2_85036bcba153_d41ae481755e
UApython-httpx/0.28.1
What they sentOnly POST /mcp and GET /sse

The body fingerprint on /mcp was not a PHP probe. It was JSON-RPC:

FieldValueDecode
param_keysijmpid, jsonrpc, method, params
param_typesifsoint, number, string, object
param_lens7-3-10-179method ~10 chars, params object ~179 bytes

Three of those POSTs reached the origin and got 401 — past HAProxy, into the MCP gateway. /sse sometimes 200. About sixteen requests in the week. No .env. No wp-login.php.

That is someone who knows MCP. Nuclei does not send ijmp/ifso.

Actor B — a Node MCP SDK, one hostname only

Amazon IPs, UA node, JA4 t13d5212h1_b262b3658495_8e6e362c5eac. Thirty-eight requests, all to the product site, paths /mcp, /api/mcp, /sse. Challenged — and they fetched the captcha page. An SDK pointed at the product, not the internet.

Actor C — Metabase exploit in a browser costume

Same ptu/sso body as the labeled detector, different UA (Edge 127, Firefox 131, Safari 17.6), JA4 t13d141000_cbb2034c60b8_e7c285222651. Six of the nine origin-200s. Intent is exploitation. Result was still the HTML catch-all.

Actor D — a four-pack of wp2shell

UA cve-2026-63030/1.0, Oracle ASN, JA4 t13d1713h1_ab0a1bf427ad_89ab6efea773. Four POSTs, all denied. CVE-specific, not the WordPress firehose.

Then the agent wrote the policy

Same session. It had already recommended list-backed rules from an earlier hunt on allowed 200s (sparse header lists, scanner JA4s). MCP policy allowed create_pattern_list, create_ja4_entry, and create_security_rule. It refused apply_config.

What landed in the control plane:

Pattern list scanner_hdr_listsahuxxxx, aaaccccchruxxxx, aaachpruuxxxx (header-list tokens unique to the campaigns that were still getting 200s).

JA4 list bad_bots — Techoff, the GCP twin, the WordPress-batch JA4, the iPhone-spoof JA4.

Two rules, all listeners, action block:

http.request.fingerprint.header_list in $pattern:scanner_hdr_lists
and not http.request.user_agent contains "ClouDNS"
http.request.ja4 in $ja4:bad_bots

On that 48-hour window those lists covered about 52% of external HTTP 200s that had been getting through, with a ClouDNS exclusion so uptime checks survive. New campaigns become another line in a list, not another rule.

The Metabase and MCP fingerprints from this week’s hunt are the next two lines — path + param_types, and param_keys = "ijmp". You grow the lists. You do not grow the rulebook.

What this is worth

Without MCPWith coreX + SIEM MCP
Export CSVs. Pivot in a SIEM UI. Guess at POST bodies you did not log.Agent queries decoded req_fp subfields directly.
Ticket: “please block these IPs.” IPs rotate overnight.Lists of header-list tokens and JA4s that survive IP/UA rotation.
Analysts with prod access, or a queue behind people who have it.Gateway policy: agent can create, cannot apply.
Hours, and you still mix scanners with the one client that speaks your protocol.Minutes to separate 191k sprays from sixteen MCP JSON-RPC calls.

The impact is not “the model is smart.” The impact is that the SIEM, the fingerprint, and the control plane are the same conversation. The agent saw that so on /api/session/reset_password is the Metabase 0-day shape. It saw that ijmp/ifso is an MCP client, not a bot. It wrote rules in the same language the GUI uses. A human still applies them.

If you are not putting an MCP gateway in front of your SIEM and your edge, you are doing this hunt with a browser and a long day. We already did it once this week. The lists are in production.

How it works: the coreX MCP Gateway brokers agent access to the coreX MCP Server and external MCP servers like the SIEM, with policy, DLP, and guardrails at the gateway. The request fingerprints behind this hunt are covered in Fingerprinting the HTTP Request.