-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 NEFARIOUSPLAN-CANONICAL-V1 {"body_md":"## The HORKimhab repository is template scaffolding\n\nThe repository ships four files. The interesting one is `cve-id-template.txt`:\n\n```\n# lowercase \ne.g: cve-2025-46822.sh, cve-2025-46822-lab.sh, ...\n```\n\nThe \"Simple Usage\" section of `README.md`:\n\n```bash\ngit clone \ncd \n```\n\n```bash\n# Install dependencies\n\n\n# Run the project\n\n```\n\nFollowed by the line `Replace the commands above with your actual project setup steps.` The \"Credit or Reference\" section names `url1`. The CVE number does not appear in the README body. It appears only in the repository name.\n\nWe have this template on file. HORKimhab's [`CVE-2026-20223`](/posts/cve-2026-20223-the-advisory-was-the-poc) was the same scaffolding with a different CVE stamped on the URL. The author's relationship to the bug is the directory name on GitHub.\n\n## The fevar54 PoC is rejected at the first protected property\n\nThe fevar54 repository ships a Python file titled `PoC Funcional - CVE-2026-45247 (Mirasvit Full Page Cache Warmer RCE)`. The README states the vulnerable code path:\n\n```php\n$cookieValue = $_COOKIE['CacheWarmer'];\n$data = unserialize(base64_decode($cookieValue));\n```\n\nThe script's `build_malicious_cookie` constructs the serialized payload:\n\n```python\npayload = (\n f'O:37:\"Monolog\\\\Handler\\\\FingersCrossedHandler\":3:{{'\n f's:11:\"*passthru\";'\n f'O:23:\"Monolog\\\\Handler\\\\StreamHandler\":3:{{'\n f's:9:\"*process\";'\n f'O:28:\"Monolog\\\\Processor\\\\IntrospectionProcessor\":1:{{'\n f's:6:\"*skips\";a:0:{{}}'\n f'}}'\n f's:6:\"*url\";s:{27 + len(cmd)}:\"php://filter/write=exec|{cmd}\";'\n f's:9:\"*bubble\";b:1;'\n f'}}'\n ...\n)\n```\n\nPHP serializes a class's protected property by prefixing the property name with three bytes: a NUL, a literal `*`, and a NUL. The serialized form of FingersCrossedHandler's protected `passthru` is the byte sequence `\\x00*\\x00passthru`, eleven bytes total. The Python string `\"*passthru\"` is nine bytes. The script declares the length as `11`. `unserialize` reads the declared length, consumes eleven bytes from the buffer, and finds eleven bytes that are `*passthru\";O` because the count spills past the closing quote into the next characters of the script's own emit. It looks for the closing `\";` at offset eleven, finds `:`, and returns `false`. The chain ends before any Monolog code runs.\n\nThe same error appears in every protected-property declaration in the payload. `s:9:\"*socket\"` declares nine bytes for seven. `s:10:\"*handler\"` declares ten for eight. `s:9:\"*process\"` declares nine for eight. Each length is the size the field would be with the NUL prefix the script forgot to write.\n\nThe script also defines three payload-generation methods on the `PHPObjectPayload` class: `generate_syslog_udp_handler_payload`, `generate_buffer_handler_payload`, `generate_fingers_crossed_payload`. Only one of the three is ever called. All three contain the same byte-level errors in the protected-property declarations. The author wrote three variants of the same broken payload, then picked one.\n\n## `php://filter/write=exec|` is not a PHP stream filter\n\nThe intended sink, supplied as the `StreamHandler`'s `*url`:\n\n```\nphp://filter/write=exec|\n```\n\n`php://filter` is a real PHP stream wrapper. The filter names it accepts are a fixed list compiled into the runtime: `convert.base64-encode`, `convert.base64-decode`, `convert.iconv.*`, `string.toupper`, `string.tolower`, `string.rot13`, `string.strip_tags`, `convert.quoted-printable-encode`, `zlib.deflate`, `bzip2.compress`, plus a few more from optional extensions. There is no `exec` filter in PHP. There has never been an `exec` filter in PHP.\n\nThe script's exfiltration check reads the HTTP response for `uid=` strings:\n\n```python\nif 'PWNED' in resp.text or 'uid=' in resp.text:\n print(\"[+] ¡Comando ejecutado exitosamente!\")\n match = re.search(r'(uid=[^\\s]+|PWNED[^\\s]+)', resp.text)\n```\n\nThe check is reasonable for an exploit that actually reached `system()`. This exploit does not. There is nothing in the response to extract.\n\n## The cookie name and the payload prefix are the same string\n\nThe script builds the cookie value:\n\n```python\ncookie_value = f\"CacheWarmer:{payload_b64}\"\nreturn cookie_value\n```\n\nThen sets it as a cookie named `CacheWarmer`:\n\n```python\nself.session.get(\n self.target_url,\n cookies={'CacheWarmer': malicious_cookie},\n timeout=30\n)\n```\n\nThe HTTP request carries `Cookie: CacheWarmer=CacheWarmer:`. On the server, `$_COOKIE['CacheWarmer']` resolves to the string `CacheWarmer:`. PHP's `base64_decode` in default mode silently strips characters that are not in the base64 alphabet. The colon is stripped. The eleven letters of `CacheWarmer` are themselves valid base64 characters and decode to eight bytes of garbage, which are concatenated with the actual payload's bytes shifted by one byte of alignment. `unserialize` of that buffer fails on the first byte because PHP serialized values start with `O:`, `s:`, `a:`, `i:`, or `b:`, not whatever the eleven letters of `CacheWarmer` decode to. The cookie name and the payload prefix are the same string.\n\n## The verification check is also theater\n\nBefore the script fires the payload, it tries to confirm the target is Magento running Mirasvit Cache Warmer. The method checks three paths:\n\n```python\ntest_paths = [\n '/magento_version',\n '/pub/static/version.php',\n '/static/version.php'\n]\n```\n\nIf none return 200, it tries `/pub/media/mirasvit/cache_warmer/CHANGELOG.md`, a path Mirasvit does not actually expose under `pub/media` (Mirasvit modules live under `app/code/Mirasvit/CacheWarmer/`). If that also fails:\n\n```python\nprint(\"[!] No se pudo determinar si el objetivo es vulnerable\")\nreturn True # Asumimos vulnerable para continuar\n```\n\nThe Spanish comment translates to \"We assume vulnerable to continue.\" The method returns true regardless of what the target actually serves. Every URL the operator supplies is judged vulnerable. The verification is decoration; the broken exploit fires against whatever the operator points it at.\n\n## What KEV listings rest on\n\nCISA's KEV catalog records vulnerabilities for which CISA has reliable evidence of in-the-wild exploitation. The evidence is telemetry: victim reports from federal agencies, IOC feeds from private partners, EDR vendor notifications. A CVE that lands on KEV without a working public PoC is the catalog working as designed. KEV exists to surface threats CISA sees in the wild before the public exploit ecosystem catches up.\n\nThe gap on CVE-2026-45247 is distinctive. KEV listings without working public PoCs are common. KEV listings whose only public PoCs are a template stub and a 280-line approximation that cannot unserialize are less so. Either CISA's underlying chain has not been published, or the underlying chain reaches the bug through a route different from what PoC authors have tried to reconstruct from the CVE description.\n\nMirasvit's 1.11.12 release is the only other reference point in the public record. The patched commit is not public, but Magento PHP object injection bugs in third-party modules have a stable mitigation shape: replace cookie-value `unserialize` with JSON parsing, or pass `['allowed_classes' => false]`. Whoever wrote the patch knows where the change lands. Whoever is exploiting in the wild knows what the patch closes. Neither knowledge is on GitHub.\n\n## The pattern\n\nThe catalog already names this shape: the placeholder-poc pattern is a script that attaches authorship to a CVE-numbered repository, structured like an exploit, missing the operational primitive. The mature exhibits include HORKimhab's prior [CVE-2026-20223](/posts/cve-2026-20223-the-advisory-was-the-poc) stub, the byte-identical [Fragnesia REPLs that stopped at the syscall before the primitive](/posts/cve-2026-46300-fragnesia-placeholders-stop-at-verify-write-denied), and the [kaleth4 CredSSP repository whose CLI flags the code never parsed](/posts/credssp-cve-2025-47987-poc-documents-a-different-project).\n\nCVE-2026-45247 sharpens the pattern in two directions at once. HORKimhab's repository repeats the same template the author used against CVE-2026-20223; the placeholder lives in the README. fevar54's repository moves the placeholder into the bytes. The script is structurally complete. The imports resolve. The Monolog classes are real. The gadget chain target family (`FingersCrossedHandler`, `BufferHandler`, `StreamHandler`) is what a working PHP Object Injection exploit against Magento would target. The cookie name matches the vendor's `CacheWarmer` cookie. Every protected-property length declaration is wrong by the three bytes of the NUL prefix the script forgot to encode. The shape is what a language model produces when asked to write a serialized PHP payload from a class diagram without running PHP.\n\nKEV's patch deadline was June 6. Today is June 11. The vendor's patch exists. The exploitation telemetry exists. The two PoCs published under the CVE number cannot reach the bug.\n\nPoCs: [HORKimhab/CVE-2026-45247](https://github.com/HORKimhab/CVE-2026-45247), [fevar54/PoC-Funcional---CVE-2026-45247-Mirasvit-Full-Page-Cache-Warmer-RCE-](https://github.com/fevar54/PoC-Funcional---CVE-2026-45247-Mirasvit-Full-Page-Cache-Warmer-RCE-)","closing_line":"The CVE number in the repository name is the only part of either artifact that is accurate.","hook_md":"CISA added CVE-2026-45247 to the Known Exploited Vulnerabilities catalog on June 3, 2026 with a federal-agency patch deadline of June 6. Today is June 11. The two PoCs published against the CVE number are HORKimhab's `CVE-2026-45247`, whose README's \"Simple Usage\" section contains the literal strings ``, ``, and ``, and fevar54's `PoC-Funcional---CVE-2026-45247-Mirasvit-Full-Page-Cache-Warmer-RCE-`, whose 280-line Python builds a serialized PHP payload PHP cannot parse. Both repositories are public record. CISA's exploitation telemetry and GitHub's exploit corpus are not the same source.","post_id":604,"slug":"mirasvit-cve-2026-45247-poc-cannot-unserialize","title":"CVE-2026-45247: KEV Says Exploited, The Public PoC Cannot Unserialize","type":"initial","unreadable_sentence":"CISA's exploitation telemetry and GitHub's exploit corpus are not the same source."} -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQRf0htP5+SjynlxywneZjl4jgkQJgUCair8RAAKCRDeZjl4jgkQ Jpc/AP9XoembLrFYH6tu46ztgIFK/h6QRxssCIC8zSamx0d5swD/SfDzykCwem7x mDGDiDc6JDhBo2DWVuST7k+PxnNxUwo= =7+X8 -----END PGP SIGNATURE-----