Detect Mercury Unauthenticated RCE via /api/terminals Access Control Bypass (CVE-2026-111057) in IBM QRadar
Detects exploitation of CVE-2026-111057, a CVSS 10.0 unauthenticated remote code execution vulnerability in mljar Mercury (pip package) versions >= 3.0.0 and <= 3.2.3. A flawed regular expression used for access control (CWE-185) allows attackers to bypass authentication (CWE-284) on the /api/terminals endpoint, which exposes an interactive terminal/WebSocket interface. Unauthenticated attackers can open a terminal session and execute arbitrary OS commands in the context of the Mercury server process. This detection looks for anomalous HTTP/WebSocket access to /api/terminals endpoints, terminal session establishment without preceding authentication, and child-process command execution spawned from the Mercury/python/gunicorn/uvicorn process tree. Fixed in v3.2.4 (commit 8c467129037ee9df269d543ac217ffd33b97bf3f).
MITRE ATT&CK
- Tactic
- Initial Access Execution
QRadar Detection Query
SELECT sourceip, destinationip, "URL", "httpResponseCode", "userName", QIDNAME(qid) AS eventName, count(*) AS cnt FROM events WHERE ("URL" ILIKE '%/api/terminals%' OR "URL" ILIKE '%/terminals/%') AND ("httpResponseCode" IN ('101','200','201','204')) AND ("userName" IS NULL OR "userName" = '-' OR "userName" = '') GROUP BY sourceip, destinationip, "URL", "httpResponseCode", "userName", qid ORDER BY cnt DESC LAST 24 HOURS Returns unauthenticated web requests to the Mercury /api/terminals endpoint, including WebSocket upgrades (101), as captured by QRadar's web/proxy DSMs. Use to scope which source IPs reached the vulnerable endpoint.
Data Sources
Required Tables
False Positives & Tuning
- Proxy configurations that do not populate the userName field for authenticated admin sessions.
- Authorized scanning or security-assessment traffic.
- Monitoring probes lacking authentication.
Other platforms for CVE-2026-111057
Testing Methodology
Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1 Unauthenticated access to Mercury /api/terminals endpoint
Expected signal: Web/proxy access log entry for GET /api/terminals with no authenticated username and a 2xx/101 response code.
- Test 2 Establish WebSocket terminal session via bypass
Expected signal: HTTP 101 Switching Protocols response logged for /api/terminals from an unauthenticated client; WebSocket connection record.
- Test 3 Command execution from Mercury process tree
Expected signal: Process-start events showing /bin/sh, id, whoami, and curl spawned with a python parent process.
Response Playbook
Triage
- Confirm the affected host is running mljar Mercury via pip and determine the installed version (e.g., `pip show mercury`); versions >= 3.0.0 and <= 3.2.3 are vulnerable, 3.2.4+ is patched.
- Review web/proxy logs for the source IP(s) that accessed /api/terminals and determine whether any request lacked authentication and received a 101 (WebSocket upgrade) or 2xx response indicating a successful terminal session.
- Correlate the terminal access timestamps with endpoint process-execution telemetry to identify any shell or command processes spawned from the Mercury/python/gunicorn/uvicorn parent process.
- Determine whether the source IP is internal, external, or a known scanner, and whether the access pattern is interactive (sustained WebSocket) versus a one-off probe.
Containment
- Immediately block the offending source IP(s) at the perimeter/WAF and restrict access to the Mercury /api/terminals endpoint.
- Upgrade Mercury to v3.2.4 or later, or take the Mercury service offline until patched; as an interim control, place the app behind an authenticating reverse proxy that denies unauthenticated access to /api/terminals.
- If command execution is confirmed, isolate the affected host from the network to prevent lateral movement.
Evidence Collection
- Preserve web server, reverse proxy, and WebSocket access logs covering the suspected exploitation window, including full request URIs, status codes, and source IPs.
- Capture process-execution logs (EDR/auditd/sysmon-for-linux) showing child processes spawned by the Mercury server process, including command lines and process trees.
- Snapshot the host filesystem and memory if RCE is confirmed, and collect any dropped files, cron entries, or new user accounts created during the window.
Escalation Criteria
- !Escalate to incident response immediately if process-execution telemetry confirms shell or command execution originating from the Mercury process tree following unauthenticated terminal access.
- !Escalate if evidence of persistence (new accounts, cron jobs, modified startup scripts), credential access, or lateral movement is found on the affected host.
- !Escalate if the Mercury instance is internet-facing and unpatched, given the CVSS 10.0 unauthenticated RCE exposure.
Investigation Guide
Related Techniques
Forensic Artifacts
- >
Web/proxy access log entries showing /api/terminals requests with HTTP 101 upgrades or 2xx responses from unauthenticated clients. - >
Child processes (sh, bash, curl, wget, nc, python) parented by the Mercury/gunicorn/uvicorn process, visible in EDR/auditd telemetry. - >
WebSocket connection records and any temporary files, reverse-shell binaries, or scripts dropped during the session.
Tuning Guidance
Baseline which source IPs and users legitimately access /api/terminals in your environment; if the Mercury terminal feature is used by administrators, allowlist their IPs and focus alerting on unauthenticated (empty username) access and WebSocket upgrades from unexpected sources. Where a reverse proxy strips usernames, rely on the process-execution correlation (shell children of the Mercury process) rather than log-only auth state. Suppress known vulnerability scanners by source IP. After upgrading to Mercury 3.2.4+, shift the detection to a pure anomaly/monitoring posture since the bypass is patched.
Hunting Queries
Surfaces all WebSocket upgrade (101) events against the Mercury terminal endpoint grouped by source IP and user to find unauthenticated interactive terminal sessions.
W3CIISLog | where csUriStem has "/api/terminals" | where scStatus == 101 | summarize count() by cIP, csUsername, bin(TimeGenerated, 1h) | order by count_ desc index=web uri_path="*/api/terminals*" status=101 | stats count by src_ip user | sort - count Hunts for shell and reconnaissance child processes spawned from the Mercury server process tree, indicating command execution via the terminal bypass.
DeviceProcessEvents | where InitiatingProcessFileName in~ ("python", "python3", "gunicorn", "uvicorn", "mercury") | where FileName in~ ("sh", "bash", "dash", "curl", "wget", "nc", "ncat", "whoami", "id") | project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine index=edr parent_process_name IN ("python","python3","gunicorn","uvicorn","mercury") process_name IN ("sh","bash","curl","wget","nc","whoami","id") | table _time host parent_process_name process_name process Atomic Red Team Tests
Simulates an unauthenticated HTTP request to the vulnerable /api/terminals endpoint to verify web-log detection fires. Lab-only against a test Mercury instance.
Command
curl -s -o /dev/null -w "%{http_code}" -X GET "http://127.0.0.1:8000/api/terminals" -H "User-Agent: cve-2026-111057-test" Cleanup
rm -f /tmp/mercury_test_response.txt 2>/dev/null || true Expected Telemetry
Web/proxy access log entry for GET /api/terminals with no authenticated username and a 2xx/101 response code.
Expected Detection
The kql/spl/qradar_aql/sumo_logic/chronicle_yaral queries flag the unauthenticated request to /api/terminals.
Simulates establishing a WebSocket terminal session to /api/terminals without authentication using a scripted client. Lab-only.
Command
python3 -c "import socket; s=socket.create_connection(('127.0.0.1',8000)); s.sendall(b'GET /api/terminals HTTP/1.1\r\nHost: 127.0.0.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n'); print(s.recv(256)); s.close()" Cleanup
echo 'no cleanup required' Expected Telemetry
HTTP 101 Switching Protocols response logged for /api/terminals from an unauthenticated client; WebSocket connection record.
Expected Detection
The kql and spl WebSocket-upgrade (status 101) queries and the chronicle_yaral rule detect the upgrade on the terminal endpoint.
Simulates post-exploitation command execution by spawning a shell/recon command as a child of a python process standing in for the Mercury server. Lab-only.
Command
python3 -c "import subprocess; subprocess.run(['/bin/sh','-c','id; whoami; curl -s http://127.0.0.1/ >/dev/null'])" Cleanup
echo 'no cleanup required' Expected Telemetry
Process-start events showing /bin/sh, id, whoami, and curl spawned with a python parent process.
Expected Detection
The elastic_eql sequence and crowdstrike_cql query detect shell/recon child processes parented by the Mercury/python process tree.