Your image parser is a remote kill switch
Codex Micro's unvalidated image upload endpoint decodes attacker-controlled rasters in-band with a check-then-act race, enabling CWE-400/CWE-770 resource exhaustion.
Codex Micro ships an image upload endpoint that decodes attacker-controlled files synchronously, in the request thread, with no pre-decode dimension cap, no per-identity concurrency limit, and no atomic admission control. There is no memory-corruption bug here. The primitive is uncontrolled resource consumption. The bug class maps to CWE-400, uncontrolled resource consumption, compounded by CWE-770, allocation of resources without limits or throttling, and gated by CWE-362, a check-then-act race on the shared worker budget. No CVE is assigned at time of writing. On the observed configuration the assessed vector is CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H, base 7.5. Availability only. That is enough to take the service down and hold it down.
The mechanism starts at decode. Image libraries do not allocate against the compressed file on disk. They allocate against the decoded raster. Raw size is width times height times channels times bytes-per-channel. A PNG or WebP header declares those dimensions before a single pixel is processed. A file of a few kilobytes can declare 30000 by 30000 pixels. At four bytes per pixel that is roughly 3.4 GB of contiguous heap the moment the decoder trusts the header. This is the classic decompression bomb, the same condition Pillow guards with DecompressionBombError and the same class that produced the ImageMagick coder failures in 2016. Codex Micro removes the guard by processing the upload in-band, before any size arbitration, on a request path that was never given a resource ceiling.
The race is the multiplier. A single oversized decode is a bounded event. The container has a memory limit, the kernel OOM killer reaps the worker, the request 500s, the pod restarts. Annoying, recoverable. The exploitable condition is the missing atomicity between admission and allocation. Codex Micro checks worker availability, then dispatches the decode, then accounts for the reservation. Those three steps are not a single transaction. Concurrent requests all read the same pre-decrement state. Every one of them passes the availability check before any one of them updates the counter. The check-then-act window is the whole point. N parallel uploads do not queue behind a limit of one. They all admit, all allocate, and the worker pool commits N times its intended peak memory in the same scheduler tick. This is time-of-check-to-time-of-use on the concurrency budget, and it turns a linear cost into a burst the host cannot absorb.
The exploit path needs no authentication, no session, and no malformed input in the parser sense. The requests are well-formed. The endpoint is public-facing. An attacker enumerates the upload route, constructs a set of small files with large declared dimensions, and issues them concurrently rather than sequentially. Concurrency is the weapon. The timing budget favours the attacker by a wide margin. The vulnerable window opens when the availability check reads the shared counter and closes when the decode reservation writes it back. On a synchronous in-band path that window spans the full decode call, tens to hundreds of milliseconds for a multi-gigabyte raster. Any request that arrives inside that span reads stale state and admits. Round-trip latency on a single keep-alive connection is smaller than the decode time by an order of magnitude, so a modest concurrency count lands the entire burst inside one open window. The attacker does not need to win a tight race. The race is wide open by construction. The files are individually valid, individually cheap to send, and individually catastrophic to decode. The server-side amplification factor between bytes-on-wire and bytes-in-heap is four to five orders of magnitude. A single commodity host saturating one connection pool can drive the target worker fleet into memory pressure faster than the orchestrator can reschedule. Initial access maps to MITRE T1190, exploitation of a public-facing application. The objective maps to T1499, endpoint denial of service, specifically T1499.003, application exhaustion flood, with a strong T1499.004 component, application or system exploitation, because the amplification comes from the decode primitive rather than raw request volume. That distinction matters for the defender. This is not a bandwidth flood. It is a logic-level amplification that a volumetric mitigation will not see.
The real-world shape of this is not exotic. Layer 7 resource-exhaustion against media processing endpoints is a standing technique because the economics favour the attacker. Cloudflare’s own L7 DDoS reporting has tracked the shift away from volumetric floods toward low-request, high-cost application attacks precisely because they evade rate thresholds tuned for packets-per-second. Pixel-flood and decompression-bomb payloads against image pipelines have been documented against ImageMagick, libvpx, and multiple SaaS thumbnail services. The tooling is trivial. A concurrency-capable HTTP client and a header-crafted image are the entire kit. No exploit framework, no memory grooming, no ROP. That is what makes the misconfiguration dangerous. The barrier to weaponisation is a for-loop, and the persistence is limited only by how long the attacker chooses to hold the connections open.
Telemetry is where the operator gets an honest answer, and the answer is that the loud signals are all downstream. What fires: worker RSS climbs vertically, cgroup memory.max events increment, the kernel emits oom-killer entries in dmesg, and the container runtime records OOMKilled with exit code 137. In Kubernetes that surfaces as pod restarts and CrashLoopBackOff, with Node memory pressure conditions and eviction events on the node object. At the edge, Cloudflare or the load balancer logs a spike in 502 and 524 responses as upstream workers stop answering. p99 latency detaches from p50. Request queue depth saturates. Those are all real, and they are all consequences, not causes. The SIEM correlation that matters is 5xx rate against upstream memory events against a concurrency spike on a single route from a narrow source set. If that rule does not exist, the incident reads as a capacity problem, and the on-call scales the fleet, which extends the attacker’s runway.
What does not fire is the more important half. The WAF stays silent. The requests carry no injection signature, no anomalous content-type, no oversized body. The compressed upload is small and standards-conformant. Signature and regex WAF rules inspect bytes on the wire, and the malicious property lives in a declared dimension field that only becomes hostile after decode. EDR stays silent because there is no new process, no child-process spawn, no LOLBin, no file write outside the temp path. Sysmon has nothing to key on. There is no Event ID 1, no Event ID 10 LSASS access, no Event ID 11 of interest. This attack lives entirely inside the memory behaviour of an already-running, already-trusted worker process. The endpoint sensors are watching for code execution and lateral movement. This is neither. The blind spot is structural. Detection has to come from application and platform metrics, not from the security stack, and most teams have not wired those metrics into the same pane as their alerts.
The detection that works is built at the application tier, not bolted on afterward. Emit a structured log at admission carrying the declared raster dimensions and the decoded byte estimate, before allocation, keyed to source identity. That single field turns an invisible attack into a queryable one, because the hostile property becomes a logged number rather than an inferred consequence. A correlation rule then keys on declared-raster bytes above the operational ceiling, grouped by source over a short window, joined to upstream 5xx and node memory pressure. That fires on the cause. Without the admission log the earliest signal is OOMKilled with exit 137, which is already the outage. Rate-of-decode-failure per route and concurrency-per-source on the media path are the two metrics that separate an attack from an organic traffic spike, and both have to sit in the same view as the security alerts or the on-call reads the symptom and scales the fleet.
The gap between decode-time cost and admission-time accounting is the whole vulnerability, and rate limiting alone does not close it. A per-IP request cap slows a naive flood. It does nothing against single-request amplification from a rotating source set, and it does nothing about the race, because the race admits a burst inside one window before the limiter’s counter reflects it. The patch boundary is specific. Validate declared dimensions and compressed-to-raster ratio before allocation, and reject anything past a hard pixel ceiling at header parse. Move decode off the request thread into a bounded work queue with a real semaphore, and enforce admission as an atomic compare-and-set against the worker budget so concurrent requests cannot all read the same pre-decrement state. Cap per-request and per-tenant memory at the process boundary. Apply cost-based limiting keyed to declared raster size, not request count.
Residual exposure after a naive fix is the part worth stating plainly. Adding rate limiting without fixing the race leaves the amplification intact for any attacker who fits a burst inside the limiter window. Adding a dimension cap without moving decode off-thread leaves the pool saturable by volume of legitimate-sized uploads. Fixing concurrency without a pixel ceiling leaves a single connection able to allocate multiple gigabytes per request. The controls are interdependent, and shipping one reads as remediation while leaving the exploitable condition standing. For anything carrying a SOCI availability obligation, an unbounded public decode path is a reportable single point of failure, and availability sits inside that regime alongside confidentiality and integrity. Active exploitation belongs with the platform and security team on an escalation path, not in a config change made quietly during business hours. The vulnerability is not sophisticated. It is a resource ceiling that was never set, on an endpoint that was never expected to be adversarial, and that is exactly the profile of the outages that get written up after the fact.
Keep Reading
linux-securityhtop is a reconnaissance surface
How htop and top expose Linux resource contention - OOM-killer steering, D-state telemetry gaps, niced miners, and PID exhaustion mapped to MITRE T1562 and T1499.
detection-engineeringThe flat line is the exploit
A flat SOC dashboard can be defense evasion, not a quiet network. How APTs shape telemetry to land in a pipeline's discard paths, and why the graph is the IOC.
vulnerability-researchXBOW topped HackerOne in 2025 and fixed nothing
Vulnerability research rewards finding, not fixing. Exploitation tracks disclosure, not the flaw. Why attackers react to reported CVEs, and where telemetry goes blind.
Stay in the loop
New writing delivered when it's ready. No schedule, no spam.