Sploitus

Exploit for Out-of-bounds Write in Php

githubexploit Β· 2026-08-15

Exploit Code

README137 lines
## https://sploitus.com/exploit?id=78BE315A-C781-588D-BD38-065DB981EF80
# CVE-2026-17544 β€” PHP bcmath OOB write β†’ universal memory-only RCE

A weaponized proof-of-concept for **CVE-2026-17544**, an out-of-bounds write in PHP's
rewritten `bcmath` extension (`bc_str2num`). This turns the publicly known **DoS-only**
crash into a **full, universal, memory-only remote code execution primitive** that
bypasses both `disable_functions` **and** `open_basedir` at the same time.

> **Authorized research / educational use only.** Run this only against systems you own
> or are explicitly authorized to test.


---

## TL;DR

- **Bug:** `bc_str2num` truncates a fractional part on a manual `scale`, re-trims trailing
  zeros, but does not update `fractional_end`. The buffer is sized for the re-trimmed
  length while the copy uses the pre-trim length β†’ controlled **out-of-bounds zero-write**
  into the Zend MM heap.
- **Exploit:** the OOB zero-write zeroes the refcount of a neighbouring `zend_string`
  (length preserved) β†’ **refcount-desync use-after-free** β†’ giant-string relative R/W β†’
  fault-safe absolute arbitrary read β†’ runtime symbol resolution β†’ a fully **data-only**
  native function call.
- **Payload:** builds a fake `zend_class_entry` + inline `function_table` + fake
  `zend_function{ handler = zif_shell_exec }`, then calls it as a method. The command
  output is returned as a string and delivered into the response/stdout.
- **No hardcoded offsets.** Base address, `.data`, and the target symbol are all resolved
  at runtime by scanning memory β€” so a single file works across builds and distributions.

---

## Threat model β€” read this first

This is a **sandbox-escape / post-exploitation** primitive, **not** a remote-input-only RCE.

| Scenario | Result |
|---|---|
| Attacker can already execute PHP (webshell, deserialization, `eval` sink) but is boxed by `disable_functions` + `open_basedir` | βœ… **Native RCE** (this PoC) |
| Application merely calls a bcmath function on raw user input, e.g. `bccomp($_POST['v'])` | ❌ Only DoS / crash |

A single bcmath call on attacker input gives one **blind** OOB zero-write with no feedback.
Full RCE requires arbitrary PHP execution to groom the heap, build the R/W primitives, and
run the resolver β€” none of which is expressible as a function argument. So the realistic
ceiling for input-only exposure is a **remote denial of service**; code execution requires
an existing PHP-execution context that this PoC then frees from the sandbox.

---

## Affected versions

The bug lives only in the **rewritten** bcmath (PHP 8.4 / 8.5 line):

| PHP branch | Vulnerable |
|---|---|
| 7.x, 8.0 – 8.3 | No (old `libbcmath`, different `bc_str2num`) |
| **8.4.0 – 8.4.23** | **Yes** |
| **8.5.0 – 8.5.8** | **Yes** |
| β‰₯ 8.4.24 / β‰₯ 8.5.9 | Patched |

Verified end-to-end (`uid=0`, output captured) on **PHP 8.4.23** and **PHP 8.5.8**, amd64,
with the same unmodified file β€” the Zend struct layout is identical across 8.4 / 8.5.

---

## Requirements / assumptions

- Linux **amd64**.
- A PHP 8.4/8.5 binary in the vulnerable range with `bcmath` enabled.
- **Contiguous** binary mapping (every standard distribution / production build). The runtime
  base scan walks down page-by-page from `.text` to the ELF header; this is safe on any
  contiguously-mapped binary. Only artificial `-z separate-code` builds with unmapped 2 MB
  gaps cannot be auto-resolved by a blind scan (not seen in production).
- For web delivery, request isolation is required so a per-request `exit()` does not kill the
  server: **php-fpm**, Apache mod_php, or the built-in server with `PHP_CLI_SERVER_WORKERS>0`.

---

## Usage

### CLI

```bash
php exploit.php "id; uname -a"
```

### Web (post-exploitation, inside a sandboxed PHP context)

Deploy as e.g. `shell.php`, then:

```
GET  /shell.php?cmd=id
POST cmd=id
```

The command output is captured via `zif_shell_exec` and written into the response body.

---

## How it works (detailed)

1. **Trigger + groom.** Spray fixed-size strings, punch a hole, and run the vulnerable
   `bccomp()` so the OOB zero-write lands on a neighbouring `zend_string`'s refcount only
   (length preserved).
2. **Refcount-desync UAF.** The victim string is freed prematurely while a reference still
   dangles.
3. **Giant string.** The freed slot is reclaimed by an object; the dangling string's length
   field becomes a huge value β†’ relative read/write over the heap.
4. **Absolute read.** A fake `zend_reference` (via indirection) yields a fault-safe absolute
   arbitrary read that never touches the refcounted header of the target.
5. **Resolve.** From a leaked `.text` pointer: page-step down to the ELF magic (base), parse
   program headers to find `.data`, then scan `.data` for the `shell_exec`
   `zend_function_entry` whose handler points into `.text`.
6. **Data-only call.** Construct a fake `zend_class_entry`, an inline `function_table` with a
   single bucket (correct `zend_string` hash, terminated collision chain), and a fake
   `zend_internal_function` whose handler is `zif_shell_exec`; corrupt the probe object's
   class pointer and invoke the method. Output is returned and echoed.

---

## Files

- `exploit.php` β€” the universal memory-only exploit (CLI + web).

---

## Credits

- Original public DoS proof-of-concept: [boreas37/cve-2026-17544-poc](https://github.com/boreas37/cve-2026-17544-poc).
- Memory-disclosure / data-only-call techniques inspired by prior PHP exploitation research.

---

## Disclaimer

Provided for security research and education. The author is not responsible for misuse.
Do not deploy against systems you are not authorized to test.