# [CRIT] [GHSA / CRITICAL] CVE-2026-48039: Meta Ads MCP: Unauthenticated HTTP MCP Tool Execution Leaks Operator Meta Access Token

**Source:** GitHub Security Advisories
**Published:** 2026-06-11
**Article:** https://github.com/advisories/GHSA-9gw6-46qc-99vr

## Threat Profile

Meta Ads MCP: Unauthenticated HTTP MCP Tool Execution Leaks Operator Meta Access Token

# Unauthenticated HTTP MCP Tool Execution Leaks Operator Meta Access Token

| Field            | Value |
| ---------------- | ----- |
| Repository       | pipeboard-co/meta-ads-mcp |
| Affected version | ≤ 1.0.101 (commit 496c988 ~ 7d14226); Versions 1.0.102–1.0.105 lack git tags, so patch status is unconfirmed. |
| Vulnerability    | CWE-287 — Improper Authentication |
| Severity         | Critical |
| CVSS …

## Indicators of Compromise (high-fidelity only)

- **CVE:** `CVE-2026-48039`

## MITRE ATT&CK Techniques

- **T1190** — Exploit Public-Facing Application
- **T1528** — Steal Application Access Token
- **T1098.001** — Account Manipulation: Additional Cloud Credentials
- **T1204.002** — User Execution: Malicious File
- **T1133** — External Remote Services
- **T1041** — Exfiltration Over C2 Channel
- **T1552.001** — Unsecured Credentials: Credentials In Files
- **T1592.002** — Gather Victim Host Information: Software
- **T1518** — Software Discovery
- **T1059.006** — Command and Scripting Interpreter: Python
- **T1610** — Deploy Container

## Kill chain phases observed

_(none detected from narrative keywords)_

## Recommended hunts

### Unauthenticated POST to /mcp endpoint on TCP 8080 (CVE-2026-48039)

`UC_47_3` · phase: **delivery** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count, values(Web.url) as urls, values(Web.user_agent) as user_agents, values(Web.http_user_agent) as http_user_agents, values(Web.status) as statuses from datamodel=Web.Web where Web.http_method=POST Web.dest_port=8080 Web.url="*/mcp*" Web.status=200 NOT (Web.src=10.0.0.0/8 OR Web.src=192.168.0.0/16 OR Web.src=172.16.0.0/12 OR Web.src=127.0.0.0/8 OR Web.src=169.254.0.0/16) by Web.src, Web.dest, _time span=1m | `drop_dm_object_name(Web)` | where count >= 1 | sort - _time
```

**Defender KQL:**
```kql
// CVE-2026-48039 — inbound to vulnerable meta-ads-mcp listener on 8080
let MCPHosts = DeviceProcessEvents
    | where Timestamp > ago(7d)
    | where ProcessCommandLine has_any ("meta_ads_mcp", "meta-ads-mcp")
    | distinct DeviceId, DeviceName;
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where ActionType == "InboundConnectionAccepted"
| where LocalPort == 8080
| where RemoteIPType == "Public"
| where not(ipv4_is_private(RemoteIP))
| join kind=inner MCPHosts on DeviceId
| summarize ConnCount = count(), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), DistinctSrc = dcount(RemoteIP)
          by DeviceName, RemoteIP, LocalPort, InitiatingProcessFileName
| order by FirstSeen desc
```

### Meta Graph API request with access_token in URL query string (CVE-2026-48039 leak signature)

`UC_47_4` · phase: **c2** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count, values(Web.url) as urls, values(Web.user_agent) as ua, values(Web.dest) as dests from datamodel=Web.Web where (Web.dest="graph.facebook.com" OR Web.url="*graph.facebook.com*") Web.url="*access_token=*" by Web.src, Web.user, _time span=5m | `drop_dm_object_name(Web)` | rex field=urls "access_token=(?<token_preview>[A-Za-z0-9_\-]{6,12})" | eval src_kind=if(match(src,"^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)"),"internal","external") | table _time, src, src_kind, user, dests, token_preview, urls | sort - _time
```

**Defender KQL:**
```kql
// CVE-2026-48039 token leak — Graph API URL carrying access_token
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteUrl has "graph.facebook.com"
| where RemoteUrl has "access_token="
| extend TokenPreview = extract(@"access_token=([A-Za-z0-9_\-]{6,12})", 1, RemoteUrl)
| extend GraphPath = extract(@"graph\.facebook\.com/(v[0-9]+\.[0-9]+/[A-Za-z0-9_./-]+)", 1, RemoteUrl)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, RemoteIP, RemotePort, RemoteUrl, GraphPath, TokenPreview
| union (
    DeviceProcessEvents
    | where Timestamp > ago(7d)
    | where ProcessCommandLine has "graph.facebook.com" and ProcessCommandLine has "access_token="
    | where FileName in~ ("curl.exe","wget.exe","powershell.exe","pwsh.exe","python.exe","python3.exe")
    | extend TokenPreview = extract(@"access_token=([A-Za-z0-9_\-]{6,12})", 1, ProcessCommandLine)
    | extend GraphPath = extract(@"graph\.facebook\.com/(v[0-9]+\.[0-9]+/[A-Za-z0-9_./-]+)", 1, ProcessCommandLine)
    | project Timestamp, DeviceName, InitiatingProcessFileName=FileName, InitiatingProcessCommandLine=ProcessCommandLine, InitiatingProcessAccountName=AccountName, RemoteIP=tostring(dynamic(null)), RemotePort=toint(0), RemoteUrl=ProcessCommandLine, GraphPath, TokenPreview
)
| order by Timestamp desc
```

### Vulnerable meta-ads-mcp installation inventory (CVE-2026-48039) on managed hosts

`UC_47_5` · phase: **recon** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count, values(Processes.process) as cmd, values(Processes.process_name) as proc_name, values(Processes.parent_process_name) as parent, min(_time) as first_seen, max(_time) as last_seen from datamodel=Endpoint.Processes where (Processes.process="*meta_ads_mcp*" OR Processes.process="*meta-ads-mcp*") by Processes.dest, Processes.user | `drop_dm_object_name(Processes)` | rex field=cmd "meta[-_]ads[-_]mcp[=\s/-]*(?<version>\d+\.\d+\.\d+)" | eval vulnerable=if(isnotnull(version) AND match(version,"^1\.0\.(\d|[1-9]\d|10[0-8])$"),"YES","UNKNOWN") | table dest, user, version, vulnerable, parent, cmd, first_seen, last_seen | sort - last_seen
```

**Defender KQL:**
```kql
// CVE-2026-48039 — inventory hunt for meta-ads-mcp installations
let ProcessSightings = DeviceProcessEvents
    | where Timestamp > ago(14d)
    | where ProcessCommandLine has_any ("meta_ads_mcp", "meta-ads-mcp")
         or InitiatingProcessCommandLine has_any ("meta_ads_mcp", "meta-ads-mcp")
    | extend FullCmd = strcat(InitiatingProcessCommandLine, " || ", ProcessCommandLine)
    | extend VersionString = extract(@"meta[-_]ads[-_]mcp[=\s/\-]*([0-9]+\.[0-9]+\.[0-9]+)", 1, FullCmd)
    | extend ContainerImage = extract(@"(meta-ads-mcp[A-Za-z0-9._:-]*)", 1, FullCmd)
    | summarize FirstSeen = min(Timestamp), LastSeen = max(Timestamp), SampleCmd = any(FullCmd), VersionString = any(VersionString), ContainerImage = any(ContainerImage)
              by DeviceId, DeviceName;
let TVMSightings = DeviceTvmSoftwareInventory
    | where SoftwareName has_any ("meta-ads-mcp", "meta_ads_mcp")
    | project DeviceId, DeviceName, SoftwareName, SoftwareVersion;
ProcessSightings
| join kind=fullouter TVMSightings on DeviceId
| extend Version = coalesce(VersionString, SoftwareVersion)
| extend Vulnerable = case(
    isempty(Version), "UNKNOWN",
    Version matches regex @"^1\.0\.([0-9]|[1-9][0-9]|10[0-8])$", "YES (<=1.0.108)",
    "NO"
  )
| project DeviceName = coalesce(DeviceName, DeviceName1), Version, Vulnerable, ContainerImage, SampleCmd, FirstSeen, LastSeen
| order by Vulnerable desc, LastSeen desc
```

### meta-ads-mcp Streamable HTTP listener bound to non-loopback interface

`UC_47_6` · phase: **weapon** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count, values(Processes.process) as cmd, values(Processes.parent_process_name) as parent, min(_time) as first_seen from datamodel=Endpoint.Processes where (Processes.process="*meta_ads_mcp*" OR Processes.process="*meta-ads-mcp*" OR Processes.parent_process="*meta_ads_mcp*") by Processes.dest, Processes.user | `drop_dm_object_name(Processes)` | where match(cmd,"(?i)(--host\s+0\.0\.0\.0|--host\s+\*|0\.0\.0\.0:8080|-p\s+8080:8080|-p\s+0\.0\.0\.0:8080:)") AND NOT match(cmd,"(?i)(127\.0\.0\.1:8080:8080|--host\s+127\.0\.0\.1|--host\s+localhost)") | table dest, user, parent, cmd, first_seen | sort - first_seen
```

**Defender KQL:**
```kql
// CVE-2026-48039 — meta-ads-mcp listener exposed beyond loopback
DeviceProcessEvents
| where Timestamp > ago(14d)
| where ProcessCommandLine has_any ("meta_ads_mcp", "meta-ads-mcp")
     or InitiatingProcessCommandLine has_any ("meta_ads_mcp", "meta-ads-mcp")
| extend FullCmd = strcat(InitiatingProcessCommandLine, " || ", ProcessCommandLine)
| where FullCmd matches regex @"(?i)(--host\s+0\.0\.0\.0|--host\s+\*|0\.0\.0\.0:8080|-p\s+8080:8080(\s|$)|-p\s+0\.0\.0\.0:8080:|--publish[= ]0\.0\.0\.0:8080)"
| where not(FullCmd matches regex @"(?i)(127\.0\.0\.1:8080:8080|-p\s+127\.0\.0\.1:8080|--host\s+127\.0\.0\.1|--host\s+localhost)")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
```

### CVE-2026-48039 PoC artifact execution (meta-ads-mcp-vuln001 image, FAKE_TOKEN_FOR_POC_DEMO env)

`UC_47_7` · phase: **exploit** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count, values(Processes.process) as cmd, values(Processes.parent_process_name) as parent, min(_time) as first_seen from datamodel=Endpoint.Processes where (Processes.process="*meta-ads-mcp-vuln001*" OR Processes.process="*FAKE_TOKEN_FOR_POC_DEMO*" OR Processes.process="*poc.py*") by Processes.dest, Processes.user, Processes.process_name | `drop_dm_object_name(Processes)` | where (match(cmd,"meta-ads-mcp-vuln001|FAKE_TOKEN_FOR_POC_DEMO")) OR (match(cmd,"poc\.py") AND match(cmd,"(?i)(meta_ads_mcp|meta-ads-mcp|graph\.facebook\.com|/mcp)")) | table dest, user, process_name, parent, cmd, first_seen | sort - first_seen
```

**Defender KQL:**
```kql
// CVE-2026-48039 — PoC artefacts (docker image, fake token literal, poc.py exploit)
let ProcMatches = DeviceProcessEvents
    | where Timestamp > ago(30d)
    | where ProcessCommandLine has "meta-ads-mcp-vuln001"
         or ProcessCommandLine has "FAKE_TOKEN_FOR_POC_DEMO"
         or InitiatingProcessCommandLine has "meta-ads-mcp-vuln001"
         or InitiatingProcessCommandLine has "FAKE_TOKEN_FOR_POC_DEMO"
         or (ProcessCommandLine has "poc.py" and (ProcessCommandLine has_any ("meta_ads_mcp","meta-ads-mcp","graph.facebook.com","/mcp","tools/call","get_ad_accounts")))
    | project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, Source = "process";
let FileMatches = DeviceFileEvents
    | where Timestamp > ago(30d)
    | where FileName =~ "poc.py"
    | where InitiatingProcessFileName in~ ("python.exe","python3.exe","code.exe","pwsh.exe","powershell.exe","cmd.exe","bash.exe","explorer.exe","curl.exe","git.exe")
         or InitiatingProcessCommandLine has_any ("meta_ads_mcp","meta-ads-mcp","GHSA-9gw6-46qc-99vr","CVE-2026-48039")
    | project Timestamp, DeviceName, AccountName = InitiatingProcessAccountName, FileName, ProcessCommandLine = "", InitiatingProcessFileName, InitiatingProcessCommandLine, Source = "file";
union ProcMatches, FileMatches
| order by Timestamp desc
```

### OAuth consent / suspicious app grant

`UC_OAUTH_ABUSE` · phase: **actions** · confidence: **High**

**Splunk SPL (CIM):**
```spl
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
    from datamodel=Authentication.Authentication
    where Authentication.action="success"
      AND Authentication.signature IN (
        "Consent to application",
        "Add app role assignment grant to user",
        "Add OAuth2PermissionGrant",
        "Add delegated permission grant")
    by Authentication.user, Authentication.app, Authentication.src, Authentication.signature
| `drop_dm_object_name(Authentication)`
```

**Defender KQL:**
```kql
CloudAppEvents
| where Timestamp > ago(7d)
| where ActionType in ("Consent to application.","Add OAuth2PermissionGrant.","Add delegated permission grant.")
| project Timestamp, AccountObjectId, AccountDisplayName, ActivityType,
          ActivityObjects, IPAddress, UserAgent
```

### Article-specific behavioural hunt — [GHSA / CRITICAL] CVE-2026-48039: Meta Ads MCP: Unauthenticated HTTP MCP Tool Ex

`UC_47_2` · phase: **exploit** · confidence: **High**

**Splunk SPL (CIM):**
```spl
``` Article-specific bespoke detection — [GHSA / CRITICAL] CVE-2026-48039: Meta Ads MCP: Unauthenticated HTTP MCP Tool Ex ```
| tstats `summariesonly` count earliest(_time) AS firstTime latest(_time) AS lastTime
    from datamodel=Endpoint.Processes
    where (Processes.process_name IN ("http_auth_integration.py","api.py","poc.py"))
    by Processes.dest, Processes.user, Processes.process_name,
       Processes.process, Processes.parent_process_name, Processes.process_path
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| append [
| tstats `summariesonly` count
    from datamodel=Endpoint.Filesystem
    where Filesystem.action IN ("created","modified")
      AND (Filesystem.file_name IN ("http_auth_integration.py","api.py","poc.py"))
    by Filesystem.dest, Filesystem.user, Filesystem.process_name,
       Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
]
```

**Defender KQL:**
```kql
// Article-specific bespoke detection — [GHSA / CRITICAL] CVE-2026-48039: Meta Ads MCP: Unauthenticated HTTP MCP Tool Ex
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where (FileName in~ ("http_auth_integration.py", "api.py", "poc.py"))
| project Timestamp, DeviceName, AccountName, FileName,
          FolderPath, ProcessCommandLine,
          InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc

// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FileName in~ ("http_auth_integration.py", "api.py", "poc.py"))
| project Timestamp, DeviceName, AccountName, FolderPath,
          FileName, ActionType, InitiatingProcessFileName,
          InitiatingProcessCommandLine
| order by Timestamp desc
```

### IOC-driven hunts (use shared templates)

These are standard IOC-substitution hunts — the canonical SPL and KQL live once in [`_TEMPLATES.md`](../_TEMPLATES.md), so we don't repeat the same boilerplate on every CVE / hash / network-IOC briefing.

- **Asset exposure — vulnerability matches article CVE(s)** ([template](../_TEMPLATES.md#asset-exposure)) — phase: **recon**, confidence: **High**
  - CVE(s): `CVE-2026-48039`


## Why this matters

Severity classified as **CRIT** based on: CVE present, 8 use case(s) fired, 11 technique(s) inferred. Read the full article for actor attribution, tooling details, and any defanged IOCs in the body that aren't visible in the RSS summary.
