A booby-trapped fortune cookie that only detonates if someone swaps out the entire restaurant kitchen
CVE-2026-8838 sits in the vector_in() type-handler of the amazon-redshift-python-driver (redshift_connector) versions ≤ 2.1.13. When a query returns a column of vector type, the driver deserializes the server's response by passing it straight into Python's eval(). A rogue Redshift-protocol server — or a man-in-the-middle sitting between the client and AWS — can embed arbitrary Python in the vector payload and achieve full code execution on the client host with the privileges of the calling process. The fix in v2.1.14 replaces eval() with explicit int() parsing via a list comprehension.
The vendor scored this CVSS 9.8 CRITICAL with a vector of AV:N/AC:L/PR:N/UI:N — the textbook 'unauthenticated remote RCE' framing. That vector is materially misleading. The attacker is not sending a packet to a listening service; they must *become* the server (or intercept the connection) *and* the client must issue a query returning vector data. Worse for the attacker, redshift_connector defaults to ssl=True with sslmode=verify-ca, meaning TLS certificate validation is on unless the developer explicitly disables it. Realistic exploitation requires either prior access to reconfigure connection strings (DNS, env vars, K8s secrets) or a network position *plus* a TLS downgrade — each a separate, high-friction prerequisite. EPSS agrees: at 0.008 this is bottom-quintile exploitation probability.
4 steps from start to impact.
Establish rogue Redshift endpoint or MITM position
- Network position on the path between client and Redshift, OR ability to modify the client's connection target
- Default
ssl=Trueandsslmode=verify-cain redshift_connector make MITM infeasible without a CA-signed certificate for the Redshift endpoint - Connection strings to AWS Redshift are typically hardened in secrets managers, IAM roles, or environment-variable vaults — not user-controlled
- SSRF-to-connection-string injection requires a pre-existing SSRF vuln in the application, compounding the chain
Client connects and authenticates
Maxime288/CVE-2026-8838-RCE) includes a poc_server.py that handles this, but it only works when the client connects with ssl=False or certificate validation is disabled.- Client application must initiate a connection (automated pipeline or interactive session)
- TLS certificate validation must be absent or bypassed
- Legitimate ETL pipelines run on schedules — the attacker must have persistence in the config to catch the next run
- If IAM authentication is used, the rogue server cannot complete the IAM auth flow without AWS STS cooperation
Client executes a query returning vector-type data
vector_in() type handler, which is invoked when the result set contains a column with Redshift's SUPERVECTOR / vector type OID. The rogue server must return a result description that declares a vector-type column and then supply the malicious payload in that column's data. Not all queries return vector data; the rogue server must craft a response that matches.- Application issues a query (any query — the rogue server controls the response metadata and can declare any column type)
- Minimal friction at this step — once the connection is established and the rogue server controls responses, it can declare any column type in the row description
eval() executes attacker payload on client host
vector_in() function receives the raw bytes from the server response, decodes them to a string, and passes them directly to eval(). The attacker's payload — e.g., __import__('os').system('whoami') — executes with the full privileges of the Python process. On data-pipeline workers this is typically a service account with access to data stores, secrets, and possibly cloud IAM roles.- Vulnerable driver version ≤ 2.1.13 is installed
- Dependabot / pip-audit / safety-check in CI would flag the outdated package
- Some environments use frozen/pinned dependencies that may already be on a patched version
eval() with safe integer parsing. Under the noisgate remediation SLA for MEDIUM, deploy within 365 days. Given the trivial upgrade (pip package, no breaking changes in the patch release), most teams can ship this in a single sprint.ssl=False or monkey-patched SSL contexts. The default (ssl=True, sslmode=verify-ca) is safe against MITM. Grep your codebase for ssl\s*=\s*False and sslmode overrides. If any are found, remediate immediately — these are the only configurations where the MITM path is viable. No mitigation SLA applies for MEDIUM — go straight to remediation.- WAF / reverse-proxy filtering — This is a client-side vulnerability triggered by server *responses*, not client *requests*. A WAF sits in front of servers, not clients; it never sees the malicious payload.
- Database-level ACLs or Redshift IAM policies — These control who can *access* data on the Redshift cluster. The attack does not target the cluster; it targets the client. Redshift permissions are irrelevant.
- Python sandboxing (RestrictedPython, PyPy sandbox) — Production data pipelines do not run inside Python sandboxes. Even if they did, eval() in CPython does not honor restricted execution modes. This is not a viable control.
The supporting signals.
| In-the-Wild Exploitation | None observed. Not listed in CISA KEV. No GreyNoise or Shadowserver tags. No campaign attribution from Mandiant, CrowdStrike, or SentinelOne as of 2026-09-27. |
|---|---|
| Proof-of-Concept | One public PoC: Maxime288/CVE-2026-8838-RCE — Python rogue-server + client pair demonstrating whoami execution. 0 stars/0 forks. Educational quality, not weaponized. No Metasploit, Nuclei, or ExploitDB modules found. |
| EPSS | 0.00803 (bottom ~20th percentile) — FIRST model assigns very low 30-day exploitation probability, consistent with the high-friction prerequisites. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (vendor, 9.8). Also scored CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N (9.3) on GHSA. Both treat this as trivially-exploitable unauthenticated remote RCE, which mischaracterizes the rogue-server/MITM prerequisite. |
| Affected Versions | redshift_connector (PyPI) ≤ 2.1.13. All prior releases back to initial vector-type support are affected. |
| Fixed Version | 2.1.14 — commit 69a69df replaces eval() with [int(x) for x in text.split()]. |
| Install Base / Exposure | ~43 million monthly PyPI downloads (pypistats). Widely used in AWS data pipelines, Airflow DAGs, dbt, and analytics stacks. However, this is a *client library* — no listening port, no Shodan/Censys/FOFA exposure surface. |
| TLS Default Posture | Driver defaults to ssl=True, sslmode=verify-ca. Only verify-ca and verify-full are supported; disable/allow/prefer are not valid values. MITM requires the developer to explicitly set ssl=False. |
| Researcher | Kexin Chen (@ckx-sec) — coordinated disclosure via GitHub Security Advisory GHSA-29h4-r29x-hchv. |
| Vendor Bulletin | AWS Security Bulletin 2026-033 — published 2026-05-18. |
Sources.
Why this verdict
- Rogue-server prerequisite is a full prior-compromise stage. The CVSS vector claims
PR:N(no privileges), but to redirect a client to a rogue endpoint the attacker must already control DNS, environment variables, K8s secrets, or a config store — each implying prior access to the infrastructure. This is not 'unauthenticated remote'; it is post-initial-access. - TLS verify-ca is default and not bypassable via sslmode. The driver only accepts
verify-caorverify-fullas sslmode values. Disabling TLS requiresssl=False, which is an explicit developer decision. This makes the MITM path near-infeasible in default deployments, invalidating theAC:Lclaim. - Client-side library with no listening surface. Unlike a server-side RCE, this bug cannot be triggered by sending packets to a target. The attacker must wait for or cause the client to connect — adding temporal friction and user-interaction dependency that the CVSS vector ignores.
- EPSS confirms low exploitation probability. At 0.008, FIRST's model places this in the bottom quintile. No in-the-wild exploitation, no KEV listing, no campaign attribution.
- Role multiplier: The
redshift_connectorlibrary runs on data-pipeline workers (Airflow, dbt, Dagster), application servers, developer laptops, and CI/CD runners. (a) *Low-value roles* (dev laptops, notebooks): host-level impact only, no fleet effect. (b) *Typical roles* (ETL workers, app servers): host-level compromise could expose data-store credentials and cloud IAM role tokens — meaningful but contained to that host. (c) *High-value roles* (CI/CD runners with broad deploy permissions): chain still requires rogue-server/MITM prerequisite, which itself implies prior infrastructure compromise; if the attacker already owns the CI/CD config store, they likely don't need this CVE. The component is NOT a canonical high-value-role component (not an IdP, hypervisor, PAM, backup, DC, edge appliance, or kernel agent). No plausible high-value-role scenario produces domain/fleet/supply-chain-scale blast radius from this CVE alone. No floor applies.
Why not higher?
A higher severity (HIGH or CRITICAL) would be warranted if the attack were achievable without prior infrastructure access — for example, if the library fetched data from an unauthenticated endpoint or if TLS were off by default. Neither is the case. The rogue-server prerequisite compounds with TLS enforcement to make the realistic attack complexity substantially higher than AC:L. The PoC is trivial but demands a lab environment where the client is pointed at the attacker's server with TLS disabled — a configuration that represents a vanishingly small fraction of production deployments.
Why not lower?
A lower severity (LOW or IGNORE) would undercount the impact *once the chain succeeds*: full arbitrary code execution on the client with the process's privileges, potentially exposing AWS credentials, data-store secrets, and IAM role tokens. The eval() primitive is maximally powerful — there is no sandbox, no restricted execution, no capability drop. The PoC exists and works in permissive configurations. Some fraction of deployments do set ssl=False (per GitHub issue #158 discussions), and SSRF-to-connection-string-injection is a plausible chaining scenario in multi-tenant analytics platforms.
Crowdsourced verification payload.
Run this script on any host where redshift_connector is installed. Execute with python3 check_cve_2026_8838.py. No special privileges required — it only inspects the installed package version via importlib.metadata.
#!/usr/bin/env python3
"""Check for CVE-2026-8838: eval() injection in amazon-redshift-python-driver.
Outputs VULNERABLE, PATCHED, or UNKNOWN.
Exit codes: 1 = VULNERABLE, 0 = PATCHED, 3 = UNKNOWN
"""
import sys
try:
from importlib.metadata import version as pkg_version, PackageNotFoundError
except ImportError:
from importlib_metadata import version as pkg_version, PackageNotFoundError
PACKAGE = "redshift_connector"
FIXED = (2, 1, 14)
def parse_version(v):
"""Parse a PEP-440 version string into a comparable tuple."""
parts = []
for seg in v.split("."):
try:
parts.append(int(seg))
except ValueError:
parts.append(0)
return tuple(parts)
try:
installed = pkg_version(PACKAGE)
except PackageNotFoundError:
print(f"UNKNOWN — {PACKAGE} is not installed on this host.")
sys.exit(3)
parsed = parse_version(installed)
if parsed >= FIXED:
print(f"PATCHED — {PACKAGE} {installed} (>= {'.'.join(map(str, FIXED))}).")
sys.exit(0)
else:
print(f"VULNERABLE — {PACKAGE} {installed} (< {'.'.join(map(str, FIXED))}). CVE-2026-8838 applies.")
sys.exit(1)