{"uuid": "cf8860fa-c2b0-4ae8-a683-31b3c4916ec0", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-42945", "type": "seen", "source": "https://gist.github.com/yogesh-mishra-pagero/4290cc24fe8ff89360cadb41a125036c", "content": "# CVE-2026-42945 (nginx rewrite RCE) \u2014 `tms-nginx` Impact Analysis\n\n## TL;DR\n\n| Question | Answer |\n|---|---|\n| Does `tms-nginx` contain the vulnerable nginx code? | \u2705 **Yes** \u2014 base image `nginx:1.29.8`, inside the affected range (0.6.27 \u2013 1.30.0). |\n| Does the vulnerability impact `tms-nginx` in practice? | \u274c **No** \u2014 the bug requires a `rewrite` directive to set the `is_args` flag and a downstream capture-using sink. `tms-nginx` has no `rewrite`, no `set $var ...`, and no capture-interpolating `proxy_pass` anywhere in any environment. Not currently exploitable. |\n| Should we patch anyway? | Yes, on the normal renovate cycle \u2014 defense-in-depth, not an emergency. |\n\n## About the CVE\n\n| | |\n|---|---|\n| CVE | CVE-2026-42945 (plus -42946, -40701, -42934) |\n| Type | Heap buffer overflow in `ngx_http_rewrite_module` \u2192 unauthenticated RCE |\n| Affected (Open Source) | 0.6.27 \u2013 1.30.0 |\n| Fixed (Open Source) | 1.30.1, 1.31.0+ |\n| Vendor advisory | https://my.f5.com/manage/s/article/K000160932 |\n\n### Trigger condition\n\nThe bug is a two-pass length/copy mismatch in nginx's script engine:\n\n- **Length pass** computes the buffer size with `is_args = 0` on a freshly-zeroed sub-engine \u2014 returns the raw capture length.\n- **Copy pass** runs with `is_args = 1` (set on the main engine because the rewrite replacement contains `?`) \u2014 calls `ngx_escape_uri` with `NGX_ESCAPE_ARGS`, expanding each escapable byte to 3 bytes.\n\nThe undersized buffer overflows with attacker-controlled URI data.\n\nThe bug fires when **all** of these hold in the same `location`/`server` script chain:\n\n1. A `rewrite` directive whose **replacement contains `?`** (this sets `is_args` on the main engine).\n2. A capture (`$1`, `${name}`, etc.) is referenced **somewhere in the same script chain** \u2014 that reference can be in the rewrite replacement itself, in a subsequent `set $var $N`, or in a capture-interpolating `proxy_pass`.\n3. The capture value contains bytes that need URI-escaping.\n\nImportant caveats:\n\n- `?` inside a regex named-capture group `(?...)` is **pattern syntax**, not a `?` in the replacement. Those do not trigger the bug.\n- A regex `location ~ ^/(.*)$ { ... }` block with `(.*)` capture is harmless on its own \u2014 the bug needs both the `?`-replacement AND a capture sink in the same chain.\n- `return 301 ...$request_uri;` does not trigger \u2014 `return` is in `ngx_http_rewrite_module` but does not exercise the buggy two-pass length/copy machinery.\n\n## Part 1 \u2014 Does `tms-nginx` contain the vulnerable code?\n\n`tms-nginx` is the on-prem TMS reverse proxy, deployed as a Docker container on the `tms*` hosts (thn-prod, thn-staging, thn-test, sth-dr) by the `managed_systemd_unit` Ansible role. It terminates TLS for `tms.pageroonline.com`, `lms.primelog.com`, and `tms-int.pageroonline.com`, and is therefore public-facing on ports 80/443.\n\n### Image build\n\n`tms-nginx/Dockerfile`:\n\n```dockerfile\nFROM nginx:1.29.8@sha256:7f0adca1fc6c29c8dc49a2e90037a10ba20dc266baaed0988e9fb4d0d8b85ba0\n```\n\n`1.29.8 &lt; 1.30.0` \u2192 **inside the affected range.** The vulnerable `ngx_http_rewrite_module` code is in every running `tms-nginx` container today.\n\nNote: `NGINX_VERSION=...` in `tms-version//nginx.version` (e.g. `2.0-b126`, `1.50.1-GO`) is the **Pagero image tag**, not the upstream nginx version. The upstream nginx version is whatever `FROM nginx:...` was at the time the image was built \u2014 historically `1.29.x` or older mainline, all in the affected range.\n\n### Deployment\n\n`tms-deploy/roles/managed_systemd_unit/templates/etc/systemd/system/nginx.service` runs the image with port 80/443 published on `0.0.0.0` and the per-env config files mounted from the host:\n\n```ini\nExecStart=/usr/bin/docker run \\\n    --name ${CONTAINER_NAME} \\\n    -p 0.0.0.0:80:80 \\\n    -p 0.0.0.0:443:443 \\\n    -v /etc/nginx/services.yaml:/etc/nginx/services.yaml \\\n    -v /etc/nginx/service_locations.conf:/etc/nginx/service_locations.conf \\\n    -v /etc/nginx/ip_restrictions.conf:/etc/nginx/ip_restrictions.conf \\\n    ...\n    ${REPOSITORY_NAME}/${CONTAINER_NAME}:${NGINX_VERSION}\n```\n\nThe runtime `nginx.conf` is rendered by `confd` from `tms-nginx/package/etc/confd/templates/nginx.conf.tmpl` against the per-host `services.yaml`. The directives that actually end up in nginx come from three sources only:\n\n1. `tms-nginx/package/etc/confd/templates/nginx.conf.tmpl` (baked into the image).\n2. `tms-deploy/config///etc/nginx/service_locations.conf` (mounted from host).\n3. `tms-deploy/config///etc/nginx/ip_restrictions.conf` (mounted from host).\n\n## Part 2 \u2014 Does the vulnerability impact `tms-nginx`?\n\nTo impact us, the trigger condition must exist in the rendered nginx config. Each of the three sources above was audited against the broad trigger model from Part 1.\n\n### `nginx.conf.tmpl` (baked into the image)\n\nContents (relevant):\n\n- `http { ... }` global settings (logging, SSL, proxy timeouts).\n- Dynamic `upstream api- { ... }` blocks generated from `services.yaml`.\n- Per-server `server { listen 80; ... return 301 https://$server_name$request_uri; }` HTTP\u2192HTTPS redirects.\n- Per-server `server { listen 443 ssl; ... include service_locations.conf; }` TLS server blocks.\n\nNo `rewrite`, no `set $var ...`, no `if ($var ...)` script directives. The only redirect is `return 301`, which is not a CVE trigger.\n\n### `service_locations.conf` (per environment)\n\nAudited across all four environments (thn-prod, thn-staging, thn-test, sth-dr):\n\n- `tms-deploy/config/thn/prod/etc/nginx/service_locations.conf`\n- `tms-deploy/config/thn/staging/etc/nginx/service_locations.conf`\n- `tms-deploy/config/thn/test/etc/nginx/service_locations.conf`\n- `tms-deploy/config/sth/dr/etc/nginx/service_locations.conf`\n\nEvery block follows this shape:\n\n```nginx\nlocation / {\n  include ip_restrictions.conf;\n  proxy_pass http://api-/;\n}\n```\n\nThe only regex `location` block:\n\n```nginx\nlocation / {\n  location ~^/(.*)/ {\n    include ip_restrictions.conf;\n    proxy_pass http://api-primelog;\n  }\n  location / {\n    include ip_restrictions.conf;\n    proxy_pass http://api-primelog/primelog;\n  }\n}\n```\n\nThe regex `~^/(.*)/` does capture `$1`, but `proxy_pass http://api-primelog;` **does not reference the capture** \u2014 the capture is dropped, not interpolated into the upstream URL. No `?`, no `set`, no capture sink.\n\n### `ip_restrictions.conf` (per environment)\n\nOnly `allow`/`deny` directives (currently commented out). Not part of the script engine \u2014 no impact on this CVE.\n\n### Summary table\n\n| Construct under the broad trigger model | Present in `tms-nginx`? |\n|---|---|\n| `rewrite` directive (any) | **None** \u2014 zero across all sources, all environments. |\n| `?` in any rewrite replacement | n/a \u2014 no rewrites |\n| `set $var ...` directive | **None.** |\n| `if ($var ...)` block | **None.** |\n| `proxy_pass http://upstream/$N...` (capture-interpolating) | **None** \u2014 the one regex `location` does not interpolate `$1`. |\n\n### Conclusion of audit\n\n**The vulnerability is present in the binary but unreachable through `tms-nginx`'s configuration.** No script chain in any environment evaluates a captured `$N` while `is_args` could be set, because there is no `rewrite` to set `is_args` and no capture-using sink in any chain. An attacker cannot trigger the buggy code path via any traffic that reaches `tms-nginx` today.\n\n## Part 3 \u2014 What to do\n\n### Short term (now)\n\n- **No emergency action needed.** `tms-nginx` is not currently exploitable.\n- **Code-review rule** while the vulnerable nginx is still in use:\n  - Don't introduce a `rewrite` directive whose replacement contains `?`.\n  - Don't introduce a `set $var $N` (or any capture-using directive) in the same chain as such a rewrite.\n  - Don't introduce a capture-interpolating `proxy_pass`/`return`/`add_header` downstream of such a rewrite.\n\n### Medium term (normal renovate cycle)\n\nBump the base image in `tms-nginx/Dockerfile` once the official `nginx:` Docker library publishes a fixed tag:\n\n```dockerfile\nFROM nginx:1.29.8@sha256:7f0adca1fc6c29c8dc49a2e90037a10ba20dc266baaed0988e9fb4d0d8b85ba0\n```\n\n\u2192 should become `nginx:1.30.1` (stable) or `nginx:1.31.0+` (mainline) with a refreshed digest. Renovate is already wired up via `tms-nginx/renovate.json` extending `github&gt;pagero/renovate-config//team-buzzard/default.json5`, so this should arrive as a normal PR.\n\nAfter the image is rebuilt and a new `tms-nginx` tag is pushed, update the env-specific `nginx.version` files in `tms-version/`, then deploy via the existing `update_util_service.yml` flow:\n\n```bash\nansible-playbook update_util_service.yml -kK \\\n  --extra-vars 'services_version_commit=HEAD site_env=thn/prod service=tms-nginx' \\\n  --diff -l tms1.prod.thn.int.pagero.com\n```\n\nReasons to bump (not urgent, but worth doing):\n\n1. **Defense in depth** \u2014 a future config change adding a `?`-replacement rewrite plus any capture sink would silently re-introduce exploitability on a public-facing service.\n2. **Other CVEs in the same disclosure** (-42946, -40701, -42934) are also memory corruption issues in the same area; the patched nginx versions fix all four.\n3. **Scanner hygiene** \u2014 vulnerability scanners (Qualys/BigFix) will flag this regardless of exploitability.\n\n### Operational caveats for the bump\n\n- `tms-nginx` is on `nginx:1.29.8` today \u2014 the jump to `1.30.1` or `1.31.0` is one minor version. Smaller blast radius than older fleets, but treat with normal caution: deploy to test/staging first, watch error logs for behavioural changes (HTTP/2, regex `location` semantics, header parsing), don't roll fleet-wide on the same day.\n- `tms-nginx` is a SPoF for inbound TMS traffic in each datacenter (paired with keepalived for VIP failover). Bump test \u2192 staging \u2192 DR (sth) \u2192 prod (thn) in that order.\n\n## Re-audit when configs change\n\nRun before merging any change that touches `tms-nginx/`, `tms-deploy/config/*/etc/nginx/`, or any other source that ends up in the rendered `nginx.conf`:\n\n```bash\n# 1. rewrite directive whose replacement contains '?' \u2014 sets is_args on the main engine\ngrep -rn -E '\\brewrite\\s+\\S+\\s+[^;]*\\?' \\\n  ~/github-clone-folder/tms-nginx/ \\\n  ~/github-clone-folder/tms-deploy/config/\n\n# 2. 'set $var ...' directive \u2014 capture sink downstream of a rewrite\ngrep -rn -E '^\\s*set\\s+\\$' \\\n  ~/github-clone-folder/tms-nginx/ \\\n  ~/github-clone-folder/tms-deploy/config/\n\n# 3. proxy_pass / return / add_header that interpolates a numeric or named capture\ngrep -rn -E '(proxy_pass|return|add_header)\\s+[^;]*\\$([0-9]|\\{)' \\\n  ~/github-clone-folder/tms-nginx/ \\\n  ~/github-clone-folder/tms-deploy/config/\n```\n\n**Decision rule:** If (1) returns hits AND either (2) or (3) returns hits in the same `location`/`server` block, the affected config has likely become exploitable on the vulnerable nginx \u2014 patching the base image becomes urgent.\n\nAll three return empty for `tms-nginx` today.\n\n## Audit invariant (suggested addition to `tms-nginx` README)\n\n&gt; **CVE-2026-42945 audit invariant:** while pinned to upstream nginx &lt; 1.30.1, `tms-nginx` and the configs it consumes from `tms-deploy/config/*/etc/nginx/` must not contain any `rewrite` directive whose replacement contains `?`, any `set $var ...` directive, or any capture-interpolating `proxy_pass`/`return`/`add_header`. If any of these are introduced, bump the base image first.\n\n## References\n\n- Vendor advisory: https://my.f5.com/manage/s/article/K000160932\n- Technical write-up: https://depthfirst.com/research/nginx-rift-achieving-nginx-rce-via-an-18-year-old-vulnerability\n- Public PoC: https://github.com/DepthFirstDisclosures/Nginx-Rift\n", "creation_timestamp": "2026-05-15T09:48:34.000000Z"}