Sep 23, 2026·9 min read·16 visits
Unauthenticated remote SQL injection in ReactPress prior to v3.7.0 allows attackers to execute arbitrary SQL queries by tailoring query parameter keys in API requests, exposing sensitive database contents.
An unauthenticated remote SQL injection vulnerability exists in multiple API list endpoints of ReactPress prior to version 3.7.0. The vulnerability stems from unsafe construction of TypeORM QueryBuilder conditions, where untrusted HTTP query parameter keys are interpolated directly into SQL statements as identifiers without sanitization or validation.
The vulnerability designated as CVE-2026-61685 (also identified via GHSA-wmw4-mw6x-6vfm) is a high-severity SQL injection flaw affecting ReactPress, an open-source content publishing platform built on the NestJS framework and utilizing the TypeORM Object-Relational Mapper (ORM). The security flaw exists within the backend REST API endpoints responsible for retrieving lists of entities, including articles, comments, files, knowledge base items, and pages. These API components process user-supplied HTTP query parameters to enable dynamic filtering and pagination.
Under normal operations, clients query these endpoints using specific search parameters to locate records. However, because the application processes arbitrary query parameter keys as database column identifiers, the vulnerability exposes a direct attack surface to unauthenticated network clients. An attacker can construct malformed HTTP requests where the query keys contain embedded SQL commands. These commands bypass standard input validation, leading to direct execution of arbitrary database queries within the context of the application's database session.
This security advisory analyzes the underlying mechanics of this vulnerability, focusing on how TypeORM processes query builder conditions and why standard parameterization fails when SQL identifiers are dynamic. Additionally, this report outlines the precise code changes implemented in the patching release, provides concrete detection strategies, and discusses remediation paths for system administrators.
The root cause of CVE-2026-61685 lies in the insecure pattern of iterating over untrusted object keys and directly interpolating them into TypeORM's dynamic query builder statements. In NestJS-based architectures using TypeORM, developers often utilize SelectQueryBuilder to dynamically compile complex SQL queries based on HTTP request variables. In the vulnerable versions of ReactPress, the application extracted all unknown or miscellaneous query parameters into an object wrapper named otherParams. It then iterated through the keys of this object to build filtering conditions using the .andWhere() method.
While the value of the filter was correctly bound using parameterization (e.g., via .setParameter()), the key itself was interpolated directly into the query string using JavaScript template literals (article.${key}). In SQL engines and ORMs, parameterization is designed to secure data values, not database structure identifiers such as table names, column names, or operator syntax. When TypeORM compiles a query builder statement, it treats the string passed to .andWhere() as a raw template fragment, expecting the developer to have hardcoded or validated the structure. Because the application allowed arbitrary keys from the HTTP query string to define the column identifier, it effectively permitted the client to write arbitrary SQL fragments inside the query's conditional block.
To trigger the vulnerability, an attacker does not need to bypass authentication, as the affected listing endpoints are publicly accessible to support anonymous reading of published content. When the HTTP request is parsed by the NestJS controller, Express or Fastify maps the query string parameters into a key-value dictionary. The application's service layer processes every key-value pair in this dictionary, passing any unchecked key directly into the query builder. The SQL parser in the database engine then interprets the injected SQL syntax, ignoring the remainder of the legitimate query by utilizing comment markers (such as -- or /*).
To understand the technical manifestation of the bug, consider the vulnerable code implementation in the article service layer prior to the patch. The findAll function dynamically constructed its selection queries as follows:
// Vulnerable Code Path: server/src/modules/article/article.service.ts
if (otherParams) {
Object.keys(otherParams).forEach((key) => {
// DANGER: The variable 'key' is sourced directly from HTTP request parameters
// and interpolated directly into the SQL string without validation.
query.andWhere(`article.${key} LIKE :${key}`).setParameter(`${key}`, `%${otherParams[key]}%`);
});
}In this implementation, the backend trustingly maps each key to a database column. To correct this vulnerability, the vendor introduced a central validation utility in the file server/src/utils/query-whitelist.util.ts. This utility defines a strict allowlist of permissible database columns for each schema entity. Any parameter key that does not match one of these explicitly allowed strings is discarded before the query is constructed:
// Patched Code: server/src/utils/query-whitelist.util.ts
const ALLOWED_COLUMNS: Record<string, string[]> = {
article: [
'id', 'title', 'content', 'html', 'summary',
'status', 'views', 'likes', 'isRecommended',
'needPassword', 'password', 'publishAt', 'createAt', 'updateAt'
],
comment: [
'id', 'hostId', 'name', 'email', 'content', 'html', 'pass',
'userAgent', 'createAt', 'updateAt', 'parentCommentId',
'replyUserName', 'replyUserEmail'
],
file: ['id', 'originalname', 'filename', 'url', 'type', 'size', 'createAt', 'updateAt'],
page: ['id', 'title', 'path', 'content', 'html', 'status', 'views', 'publishAt', 'createAt', 'updateAt'],
knowledge: [
'id', 'title', 'content', 'html', 'summary', 'status', 'views',
'likes', 'order', 'parentId', 'publishAt', 'createAt', 'updateAt'
]
};
export function filterByWhitelist(entity: string, params: Record<string, unknown>): Record<string, unknown> {
const whitelist = ALLOWED_COLUMNS[entity];
if (!whitelist) return {};
const { page, pageSize, status, pass, ...otherParams } = params as Record<string, unknown>;
const filtered: Record<string, unknown> = {};
Object.keys(otherParams).forEach((key) => {
if (whitelist.includes(key)) {
filtered[key] = otherParams[key];
}
});
return filtered;
}In the patched service files, the application calls this utility to filter the query parameters. The resulting filtered object contains only validated keys, which are then safe to interpolate into the query:
// Patched Code Path: server/src/modules/article/article.service.ts
const filtered = filterByWhitelist('article', queryParams);
Object.keys(filtered).forEach((key) => {
// SECURE: 'key' is now guaranteed to be an element of the ALLOWED_COLUMNS array
query.andWhere(`article.${key} LIKE :${key}`).setParameter(`${key}`, `%${filtered[key]}%`);
});While this fix successfully blocks SQL injection by sanitizing the column identifiers, it is worth noting that dynamic SQL identifier interpolation remains an anti-pattern. A more robust design would involve mapping input parameters to strongly typed Query Transfer Objects (DTOs) enforced at the controller level using class-validator, rather than sanitizing generic dictionaries downstream in the service layer.
Exploitation of CVE-2026-61685 requires only the ability to send HTTP GET requests to the vulnerable endpoints. Because the vulnerability is triggered via the keys of the HTTP query string, standard payload structures must be modified to fit within the parsing logic of both the HTTP server and the database engine. In a typical scenario, an attacker targeting a PostgreSQL or MySQL backend utilizes time-based blind SQL injection to extract data.
To perform a basic check, an attacker sends an HTTP request where the parameter name itself contains the injection payload, terminated by a comment character to discard the remaining programmatically appended query syntax. A conceptual payload targeting PostgreSQL is structured as follows:
GET /api/article?id=1 AND pg_sleep(5) -- =test
When processed, the key is parsed as id=1 AND pg_sleep(5) -- and the value is parsed as test. The backend service constructs the following raw SQL fragment:
article.id=1 AND pg_sleep(5) -- LIKE :id=1 AND pg_sleep(5) --
When executed by the database engine, the double hyphen (--) comments out the trailing LIKE clause and its parameter placeholder. The database engine executes the command, pausing execution for 5 seconds before returning the HTTP response. By measuring this delay, the attacker confirms the injection point. To exfiltrate database contents, such as administrator session tokens or password hashes, the attacker can execute automated tools like sqlmap with custom parameters, or utilize a custom script to perform binary search exfiltration based on time-delays or conditional page sizes (Boolean-based).
The successful exploitation of CVE-2026-61685 leads to complete database compromise. Because the database session executes queries with the permissions of the application's database user, the attacker can read any table accessible to that user. In standard deployments, the database user possesses read and write privileges over the entire ReactPress schema, including tables storing administrator credentials, cryptographic salt values, system configuration settings, and user publication drafts.
With read access to the database, an attacker can extract bcrypt-hashed administrator passwords. While these hashes are protected by one-way hashing algorithms, they remain vulnerable to offline brute-force or dictionary attacks. If the database engine configured under ReactPress is PostgreSQL or MySQL, and the database user has elevated system privileges, the attacker could theoretically perform arbitrary file read operations (e.g., using pg_read_file or LOAD_FILE) or execute system commands depending on database engine configurations (e.g., via xp_cmdshell or user-defined functions).
Although the dynamic query building is located within read-oriented search endpoints, preventing direct data modification via standard SQL injection statements, database-specific capabilities such as stacked queries (supported by certain drivers like Postgres when configured to allow them) could enable modification or deletion of database tables. Consequently, the CVSS 3.1 rating is calculated at 7.5 (High), reflecting high confidentiality impact with low prerequisites and unauthenticated network access.
The primary remediation strategy for CVE-2026-61685 is upgrading the ReactPress server installation to version 3.7.0 or higher. This release integrates the strict whitelist-based filtering utility across all affected modules, rendering the query parameter keys immune to SQL syntax injection. Administrators running vulnerable versions can fetch and apply the patch directly from the repository:
git fetch --tags
git checkout tags/v3.7.0
npm install
npm run buildFor deployments where immediate upgrading is not possible, a manual hotfix must be implemented. Developers should create the query-whitelist.util.ts helper and refactor the findAll methods within the five affected service files: article.service.ts, comment.service.ts, file.service.ts, knowledge.service.ts, and page.service.ts to utilize the whitelist filtering utility.
To detect active exploitation attempts at the network layer, Web Application Firewalls (WAFs) should be configured with custom rules. Standard WAF rules often fail to inspect query parameter keys, focusing exclusively on parameter values. The following ModSecurity rule can be deployed to intercept malicious SQL characters inside HTTP query string keys:
SecRule ARGS_NAMES "@rx (?:\b(select|union|insert|update|delete|pg_sleep|sleep|delay)\b|--|\/\*|\*\/|'|\"|\))" \
"id:1000001,phase:2,deny,status:400,log,msg:'SQL Injection detected in query parameter key'"Additionally, database query logs should be monitored for syntax errors containing incomplete SQL fragments, unexpected sleep statements, or comment characters occurring within column identifier positions.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
ReactPress fecommunity | < 3.7.0 | 3.7.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-89 |
| Attack Vector | Network (Unauthenticated) |
| CVSS v3.1 Score | 7.5 (High) |
| EPSS Score | 0.00535 (44.13% percentile) |
| Impact | Confidentiality: High, Integrity: None, Availability: None |
| Exploit Status | Proof-of-Concept (PoC) |
| KEV Status | Not Listed |
The software constructs an SQL command using externally-influenced input, but it does not neutralize or incorrectly neutralizes elements that could modify the intended SQL command when it is sent to the database.
CVE-2026-16454 is an object-level authorization bypass vulnerability (CWE-284, CWE-862) in Eclipse hawkBit versions 1.0.3 and prior. The flaw exists within the Direct Device Integration (DDI) REST controller (hawkbit-ddi-resource), where endpoints responsible for enumerating software module metadata and serving firmware binaries fail to verify whether the requested software module is assigned to the authenticated device. Consequently, any device with valid credentials (such as a TargetToken or GatewayToken) can enumerate software module identifiers and download arbitrary firmware artifacts across the entire tenant namespace.
CVE-2026-62284 (GHSA-5w5g-j6hw-6p8f) is a code injection and server-side request forgery vulnerability in the console-proxy component of the OSAC Operator. The RemoteConfigResolver parses Kubernetes Secrets containing kubeconfig files via clientcmd.RESTConfigFromKubeConfig without sanitizing execution-related fields. An attacker with RBAC permissions to create or modify labeled Secrets in monitored namespaces can supply a malicious exec credential plugin, resulting in arbitrary command execution within the console-proxy pod.
CVE-2026-62361 (GHSA-xgjr-7j9q-2h4r) is an authenticated SQL injection and authorization bypass vulnerability in listmonk, an open-source, self-hosted newsletter and mailing list manager written in Go. In versions prior to 6.2.0, the subscriber export endpoint (GET /api/subscribers/export) directly interpolates user-supplied SQL filter expressions into a PostgreSQL query without invoking the table allowlist validation function (validateQueryTables). Consequently, authenticated users possessing the subscribers:sql_query and subscribers:get_all permissions can bypass relational access controls, execute arbitrary subqueries against restricted tables such as users and settings, and modify database state via writable Common Table Expressions (CTEs).
CVE-2026-28385 (GHSA-3gq2-x4qg-p4g6) is a Server-Side Request Forgery (SSRF) vulnerability in Canonical LXD versions 4.12 through 6.9. An authenticated user possessing the can_create_images entitlement can force the LXD daemon to execute arbitrary outbound HTTP and HTTPS requests via the /1.0/images API endpoint. Due to the absence of egress destination validation, an attacker can probe host loopback services, RFC 1918 private network segments, and cloud Instance Metadata Services (IMDS) from the network position of the privileged LXD daemon.
Canonical LXD and LXC Incus contain an arbitrary file write vulnerability (CWE-73 / CWE-59) in the `/1.0/instances/<name>/exec` REST API endpoint. When an instance is unpacked from an image archive, top-level symbolic links are extracted directly onto the host filesystem within the instance storage directory. If a crafted image includes a symbolic link named `exec-output` pointing to an arbitrary host path, invoking the execution endpoint with `record-output: true` causes the privileged host daemon to follow the symlink and write command output files directly to the target host directory. An authenticated attacker with permissions to import images and execute commands can exploit this flaw to write controlled content to sensitive host directories such as `/etc/cron.d`, resulting in arbitrary code execution on the host as `root`.
CVE-2026-48749 (tracked as GHSA-vghh-5rfx-xhq8 for LXD and GHSA-2q3f-q5pq-g8wv for Incus) is a critical container escape vulnerability in the Canonical LXD and Linux Containers Incus system container managers. By importing a specially crafted container image archive containing duplicate tar member headers, an authenticated attacker can replace the instance rootfs directory with a symbolic link pointing to the underlying host root filesystem. When performing file operations on a stopped container, the privileged forkfile helper binary resolves the symbolic link without confinement and chroots directly into the host filesystem, granting arbitrary root-level file read and write access.