Heap overflow in ngx_http_script_copy_capture_code()

Heap overflow in ngx_http_script_copy_capture_code()

The failure sits in rewrite processing, not in parsing a random header or some side channel. That matters because rewrite rules often sit on paths that look routine, which makes the broken case easy to miss until it is fed a URI that bends the code’s assumptions.

ngx_http_script_copy_capture_code() gets asked to copy capture data after the length pass has already made a size decision. When the is_args state is stale, that decision can be wrong for escaped input. The copy pass then writes more than the buffer was sized for. That is standard heap corruption territory, and it does not need much encouragement from an attacker.

The stale is_args state that skews rewrite processing

is_args is the bit of rewrite state that tracks whether the generated URI needs a query string separator. If that state lingers from an earlier rewrite step, later processing can treat the current URI as if it already has, or does not have, arguments when it should not.

That mismatch is enough to skew the estimated output length. Escaped characters make the gap worse because the encoded form may expand during copying. A length pass that is already off by one idea, not by one byte, is how the buffer ends up too small before the copy even starts.

The practical effect is simple: the module trusts a state snapshot that no longer matches the current rewrite result. Once the script engine starts copying capture data, the bad state turns into memory corruption rather than a clean reject.

How crafted URIs turn capture groups into heap corruption

The trigger is a crafted URI that contains escapable characters and hits rewrite logic that uses capture groups. The input does not need to be exotic. It needs to move through the rewrite module in a way that forces the length calculation and the later copy to disagree.

That becomes riskier when rewrite directives use ? in the replacement string and then feed into set or if with references to capture groups. Those patterns keep the script engine busy enough that the stale state can survive into the next step. The bad buffer sizing then lands in the heap, not in a harmless string object. From there, a worker process crash is the least interesting outcome.

Where rewrite directives with ? and set or if become dangerous

The risky shape is a rewrite rule that rewrites into a string containing ?, then hands the result into logic that still depends on captures. That is the sort of setup that works fine until the internal state machine gets its wires crossed.

A sensible hardening rule is to avoid that pattern entirely if patching is not yet possible. The more rewriting logic, captures, and conditional handling stack up in the same path, the less room there is for a stale flag to stay invisible.

Why nginx 1.30.1 closes the mismatch

nginx 1.30.1 closes the bug at the source. The fix matters because a WAF rule can spot exploitation attempts, but it cannot stop the broken buffer calculation inside the worker. Upgrading removes the stale-state mismatch before it gets a chance to reach ngx_http_script_copy_capture_code().

That leaves less room for heap spray games and less chance of turning a crash into something nastier. Patch first. Treat the WAF as a seatbelt, not a steering column.

Blocking the pattern before patching lands

If patching is delayed, cut the rewrite shape that feeds the bug.

Avoid:

  • rewrite directives with ? in the replacement string
  • follow-on set or if logic that references capture groups
  • complex capture handling on user-controlled URIs where escapable characters are likely

If a rule needs that structure, pull it apart. Keep rewrite logic boring and short. A tidy rule set is not glamorous, but it gives the script engine fewer chances to keep stale state alive long enough to corrupt the heap.

Cloudflare has already added WAF detections for heap buffer overflow and heap spray attempts linked to this issue. That helps with active exploitation, but it does not change the underlying boundary: the rewrite path is safe only when the vulnerable version is gone or the risky directive pattern is removed.

Tags:

Related posts

Heap overflow in ngx_http_script_copy_capture_code()

I spent too long trusting rewrite logic that looked harmless, and that is usually when nginx rewrite module is_args bites back. Escaped input, stale state, and a buffer sized on the wrong assumption...

Cloudflare Managed WAF rules: watch action changes

Cloudflare Managed WAF rules are easy to misread if you only skim the release notes. I look at the Security Events dashboard instead, because that is where a quiet detection turns into a block, or a...

Prometheus | v3.13.1

Prometheus v3 13 1 LTS bugfix: fixes TSDB head chunk cache to prevent wrong or missing recent range query samples after head truncation, recommended upgrade