Field Reports

After php2shell, I went looking for quieter bugs

The public php2shell chain made me reopen PHP. What I actually practiced was not a flashy exploit, but a local lab habit: how to rank findings by evidence, how not to overclaim, and how to write something a maintainer can triage.

The phrase that pulled me back into PHP this week was php2shell — not as a kit to copy, but as a reminder that a language runtime still has a long, sharp edge when the wrong bytes meet the wrong assumptions. Cloudflare even turned the related CVEs into a time-boxed WAF CTF. I am not writing a walkthrough of that chain. I am writing what happened when I used the noise as an excuse to open a local lab again.

What I did not do

I did not chase remote code execution for its own sake. I did not paste public PoCs into anything that was not mine. I did not treat “this looks like a CVE” as the same sentence as “I have a report worth sending.”

What I did was narrower: build recent PHP CLI binaries, load a few PECL extensions that talk to untrusted bytes, and keep notes that separate crash, sanitizer diagnosis, normal-build impact, and application preconditions. That last column is the one that saves you from embarrassing yourself in a private advisory form.

Four kinds of “finding”

After a few days of local probes, almost everything I wrote down fell into one of four buckets. Naming them for myself turned out more useful than any severity calculator.

  1. Unconditional process death. A short input, default settings, optimized build, signal every time. Maintainers argue about the score; they rarely argue about whether it is real.
  2. Conditional death. Same crash quality, but only if the application does something common yet not universal — decode attacker bytes, keep a stream open, call an API from a callback. These are still security bugs when the precondition matches real deployments. They are also where reports get long and defensive.
  3. Sanitizer-only or DEBUG-only. UBSan overflow, a hash-table assertion, an ASan note that never becomes a normal-build crash. Keep the note. Do not ship it as a CVE candidate until you can show availability or memory-safety impact without the sanitizer.
  4. Already on the public board. A variant that lands on the same free-then-use path as an open issue. Save the evidence for the maintainer’s duplicate check. Do not invent a second root cause out of pride.

The emotional trap is treating every ASan stack as a trophy. The professional habit is treating most of them as homework.

What “ready to report” started to mean

I ended up with a private checklist that has almost nothing to do with CVSS:

  • Can a stranger reproduce it with a stock build and a single short command?
  • Is the failing path still present on current release tags, not only on a half-remembered branch?
  • Did I say, in one sentence, what the application must do for the bug to matter?
  • Did I refuse claims I did not prove — especially code execution?
  • Did I look for the nearest public issue before calling it novel?

If the answer to the last question is “maybe overlapping,” the report still may be worth sending. It just has to lead with the difference in trigger and read site, not with a victory lap.

Writing for the person who will skim

PHP’s own security guidance is blunt about long essays. Extension maintainers are usually busier. The drafts I trust enough to keep are the short ones: a map of bytes, expected versus actual, a one-line guard that makes the bad case fail closed, and a note about what I did not test.

The long drafts are for me. They hold version matrices, allocator differences, and “why this is not that other bug.” Those belong in an evidence zip, not in the first screen of a private advisory.

What I am still chewing on

  • How much of an extension’s “PHP-only” mode is still a default that real services leave on.
  • How often a streaming decoder’s lifetime bugs look like leaks until a cleanup path turns them into use-after-free under a sanitizer.
  • How to keep a research board honest when one crash is spectacular and another is merely more novel.

None of that needs a public vulnerability description to be useful. The board itself is the practice.

Closing

php2shell was the spark. The work that followed was quieter: local processes, boring checklists, and a deliberate refusal to confuse a good lab day with a finished disclosure. If any of those notes become reports, they will go through private channels and maintainer triage first. Until then, this is just a field note from the bench — what I practiced, not what I claimed.

Totooria Helmold, Atelier Arland