← Back to Feed CACHED · 2026-09-26 22:18:03 · CACHE_KEY CVE-2026-8838
CVE-2026-8838 · CWE-94 · Disclosed 2026-05-18

Remote Code Execution via eval() Injection in amazon-redshift-python-driver vector_in()

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

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.

"Rogue-server prerequisite and default TLS verification slash this from CRITICAL to MEDIUM"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Establish rogue Redshift endpoint or MITM position

The attacker must either stand up a server speaking the Redshift wire protocol (PostgreSQL-based) and cause the victim client to connect to it, or achieve a network man-in-the-middle between the client and the legitimate AWS Redshift cluster. For the rogue-server path this means poisoning DNS, modifying environment variables / K8s secrets, exploiting an SSRF that allows connection-string injection, or compromising the config store. For MITM, the attacker needs Layer 2/3 network position *and* must defeat TLS.
Conditions required:
  • Network position on the path between client and Redshift, OR ability to modify the client's connection target
Where this breaks in practice:
  • Default ssl=True and sslmode=verify-ca in 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
Detection/coverage: Network monitoring / DNS anomaly detection would flag connections to non-AWS Redshift endpoints; certificate pinning or CT log monitoring catches rogue certs
STEP 02

Client connects and authenticates

The client application must establish a connection and authenticate. The rogue server must convincingly complete the PostgreSQL/Redshift handshake including TLS negotiation (if not bypassed) and authentication exchange. The PoC (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.
Conditions required:
  • Client application must initiate a connection (automated pipeline or interactive session)
  • TLS certificate validation must be absent or bypassed
Where this breaks in practice:
  • 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
Detection/coverage: Failed TLS handshakes or certificate mismatch errors appear in application logs and SIEM
STEP 03

Client executes a query returning vector-type data

The eval() sink only triggers inside the 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.
Conditions required:
  • Application issues a query (any query — the rogue server controls the response metadata and can declare any column type)
Where this breaks in practice:
  • 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
STEP 04

eval() executes attacker payload on client host

The 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.
Conditions required:
  • Vulnerable driver version ≤ 2.1.13 is installed
Where this breaks in practice:
  • 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
Detection/coverage: EDR/XDR on the host would detect anomalous child-process spawning from a Python data-pipeline process; SIEM correlation of unusual outbound connections from pipeline hosts
03 · Compensating Control

1
MEDIUM 5.6→IGNORE 0.0
SEVERITY REDUCED
Upgrade redshift_connector to ≥ 2.1.14 — The definitive fix. The patched version replaces 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.
2
MEDIUM 5.6→LOW 3.1
SEVERITY REDUCED
Verify TLS is not explicitly disabled in connection code — Audit all call sites for 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.
3
MEDIUM 5.6→LOW 2.8
SEVERITY REDUCED
Lock down connection-string sources — Ensure Redshift endpoint hostnames, ports, and credentials are sourced from a hardened secrets manager (AWS Secrets Manager, HashiCorp Vault, K8s sealed secrets) with change auditing and restricted write access. This prevents an attacker from redirecting connections to a rogue server via config manipulation.
4
MEDIUM 5.6→LOW 2.5
SEVERITY REDUCED
Enforce egress network policy for data-pipeline hosts — Restrict outbound connections from Airflow workers, dbt runners, and ETL hosts to only approved AWS Redshift cluster endpoints (by FQDN or IP allowlist). This blocks the rogue-server path at the network layer even if the connection string is modified.
What doesn't work
  • 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.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNone 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-ConceptOne 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.
EPSS0.00803 (bottom ~20th percentile) — FIRST model assigns very low 30-day exploitation probability, consistent with the high-friction prerequisites.
CVSS VectorCVSS: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 Versionsredshift_connector (PyPI) ≤ 2.1.13. All prior releases back to initial vector-type support are affected.
Fixed Version2.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 PostureDriver 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.
ResearcherKexin Chen (@ckx-sec) — coordinated disclosure via GitHub Security Advisory GHSA-29h4-r29x-hchv.
Vendor BulletinAWS Security Bulletin 2026-033 — published 2026-05-18.

Sources.

  1. GitHub Security Advisory GHSA-29h4-r29x-hchv
  2. AWS Security Bulletin 2026-033
  3. Patching Commit (eval → int parse)
  4. PoC — Maxime288/CVE-2026-8838-RCE
  5. PyPI Download Stats — redshift-connector
  6. redshift_connector SSL issue #158 (sslmode discussion)
  7. NVD — CVE-2026-8838
  8. SentinelOne Vulnerability Database
05 · The Call

Final Verdict
↓ DOWNGRADED to MEDIUM (5.6/10)

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-ca or verify-full as sslmode values. Disabling TLS requires ssl=False, which is an explicit developer decision. This makes the MITM path near-infeasible in default deployments, invalidating the AC:L claim.
  • 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_connector library 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.

06 · Verification

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.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/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)