Skip to content
SQL%20Injection%20in%20xmlrpc.php%20user Login%20username%20handling%20when%20USE XMLRPC%20is%20enabled.md

SQL%20Injection%20in%20xmlrpc.php%20user Login%20username%20handling%20when%20USE XMLRPC%20is%20enabled.md

github.com • October 13, 2026

【I previously submitted this vulnerability here: , but it was rejected due to "Product source not identifiable. Please with website of the affected product." Therefore, I have added the official website address here:

When Exponent CMS has USE_XMLRPC enabled, the public xmlrpc.php endpoint exposes MetaWeblog/Blogger XML-RPC methods. The shared helper userLogin() concatenates the attacker-controlled username directly into a SQL WHERE clause without expString::escape() or parameterized queries.

This is an unauthenticated SQL injection on an authentication path. The normal web login flow in loginController escapes credentials before calling user::login(), but the XML-RPC path does not reuse that protection.

Root cause xmlrpc.php implements an internal authentication helper, userLogin($username, $password, $src, $area). At line 62 it builds a dynamic SQL predicate by string concatenation:

$db->selectValue('user', 'id', "username='" . $username . "'")

The $username value originates from XML-RPC method parameters (for example metaWeblog.getUsersBlogs / blogger.getUsersBlogs) and is taken with scalarval() without SQL escaping. The selected id is then used to construct a user object and call authenticate().

Trust-boundary failure Credentials arriving over XML-RPC are external, untrusted input. The trust boundary should sanitize or parameterize them before they reach the database layer. That boundary fails inside userLogin(): unlike loginController::login(), which calls expString::escape(expString::sanitize(...)) before user::login(), the XML-RPC helper trusts raw XML-RPC strings.

Feature gate / entry: xmlrpc.php lines 22-25 require USE_XMLRPC == 1; otherwise the script exits. When enabled, lines 777-854 register methods including metaWeblog.getUsersBlogs and blogger.getUsersBlogs, then xmlrpc_server->service() handles the request with no network ACL.

Source: getUsersBlogs() lines 118-126 reads username and password from the XML-RPC message and calls userLogin($username, $password, null, 'create'). Other authenticated XML-RPC methods follow the same helper.

Sink: userLogin() line 62 concatenates $username into selectValue() on the user table. mysqli_database::selectValue() embeds the $where string directly into SQL.

Why this is exploitable

No authentication is required to invoke the XML-RPC method; login is the first step of the method itself.

Username is fully attacker-controlled and not escaped on this path.

Injection occurs before password verification, so crafted username values can alter the user-lookup query (boolean/time-based probing, unexpected user row selection, or broader SQL abuse depending on database privileges).

Official config.php.example sets USE_XMLRPC to '1', so deployments following the sample are exposed. The security default file sets USE_XMLRPC to '0' only when undefined; an explicit enabled config overrides it.

xmlrpc.php: userLogin(), getUsersBlogs(), xmlrpc_server dispatch map

Database access via $db->selectValue('user', ...)

Condition: USE_XMLRPC enabled

Core vulnerable code path:

XML-RPC surface is active only when USE_XMLRPC is enabled; once enabled, the endpoint is publicly reachable with no additional ACL.

Public MetaWeblog method extracts attacker-controlled username/password from the XML-RPC body and passes them to userLogin without escaping.

Unauthenticated SQL injection sink: username is concatenated into the WHERE clause used to load user.id before password authentication.

Dispatch map exposes getUsersBlogs under MetaWeblog/Blogger method names and serves requests over HTTP once USE_XMLRPC is on.

Contrast with the normal web login path, which escapes credentials before authentication. The XML-RPC userLogin helper does not apply the same control.

The site configuration defines USE_XMLRPC as 1 (framework/conf/config.php.example demonstrates this setting; live config must enable it).

/xmlrpc.php is reachable from the attacker network.

The application database backend accepts the injected SQL syntax (MySQL/mysqli is the common Exponent setup).

Confirm XML-RPC is enabled by posting a benign method call to /xmlrpc.php. If USE_XMLRPC is off, the script exits with an empty response; if on, the XML-RPC server answers.

Confirm XML-RPC is enabled by posting a benign method call to /xmlrpc.php. If USE_XMLRPC is off, the script exits with an empty response; if on, the XML-RPC server answers.

Send an unauthenticated MetaWeblog login/list call with a malicious username: Method: POST Endpoint: /xmlrpc.php Headers: Content-Type: text/xml (Host as applicable) Body: XML-RPC methodCall for metaWeblog.getUsersBlogs (or blogger.getUsersBlogs) Parameters: param0: arbitrary app key string param1 (username): SQL injection payload, for example a boolean or time-based payload breaking out of the username quotes param2 (password): any placeholder string

Send an unauthenticated MetaWeblog login/list call with a malicious username:

Endpoint: /xmlrpc.php

Headers: Content-Type: text/xml (Host as applicable)

Body: XML-RPC methodCall for metaWeblog.getUsersBlogs (or blogger.getUsersBlogs)

Parameters: param0: arbitrary app key string param1 (username): SQL injection payload, for example a boolean or time-based payload breaking out of the username quotes param2 (password): any placeholder string

param0: arbitrary app key string

param1 (username): SQL injection payload, for example a boolean or time-based payload breaking out of the username quotes

param2 (password): any placeholder string

Observe backend behavior: Expected vulnerable query shape: SELECT FROM user WHERE username='' LIMIT 0,1 With a quote-breaking username, the WHERE clause logic changes. Confirm via SQL/error logs, boolean response differences, or time delay. A generic "Login Failed" XML-RPC fault does not mean the injected SQL was not executed; authentication failure can still occur after a manipulated SELECT.

Observe backend behavior:

Expected vulnerable query shape: SELECT FROM user WHERE username='' LIMIT 0,1

With a quote-breaking username, the WHERE clause logic changes.

Confirm via SQL/error logs, boolean response differences, or time delay. A generic "Login Failed" XML-RPC fault does not mean the injected SQL was not executed; authentication failure can still occur after a manipulated SELECT.

The database executes attacker-influenced SQL during user lookup.

Depending on payload and DB privileges, the attacker can probe data, alter which user row is loaded, or extend the injection. Successful abuse of the auth path threatens account confidentiality and may enable further XML-RPC content operations if authentication is effectively bypassed or a privileged user row is selected.

This issue is independent of the web UI login form, which does escape input in loginController::login().

setdebug(3) on the XML-RPC server may increase verbosity in some environments; treat debug output as optional confirmation only.

Unauthenticated attackers can inject SQL through the XML-RPC authentication helper when USE_XMLRPC is enabled. Impact includes unauthorized database interaction on the user table and compromise of the authentication workflow.

Confidentiality: user records and related database content may be probed or disclosed through SQL injection techniques. Integrity/Availability: depending on database permissions and payload, queries may be extended beyond a simple SELECT; even read-only injection can enable account takeover paths if the selected user row can be controlled and subsequent password checks are bypassed or weakened by the manipulated lookup.

Because the sink sits on a pre-auth API used by multiple blog publishing methods, the blast radius includes the entire XML-RPC publishing surface once authentication is abused.

Vulnerability reproduction process

1、Send the following request.

The response time when sleep(0) is as follows.

When sleep(3), the response time increases by 3 seconds.

When querying the username in the database, the response time is as follows when the username does not exist:

When the username exists, the response time increases by 3 seconds.

Extracted Entities

Attack Types (1)

CWE Weaknesses (1)

Domains (1)