From File Upload To LFI: A Journey To Exploitation
Recently I had a client that asked for a black-box pentest for a new web app that the company was about to release. The objective of this black-box penetration test is to assess the security posture of the web application developed for the client. The scope includes testing for common web application vulnerabilities such as authentication flaws, injection attacks, misconfigurations, and access control issues, ensuring that the application won’t be compromised by any threat actor.
In this article, I’ll walk you through how I discovered and exploited a vulnerable file upload functionality in an environment of disabled PHP functions such as exec(), passthru() and systen() , pivoted to Local File Inclusion (LFI), and ultimately gained access to critical application data, including a database.

A File Upload Vulnerability
While mapping the target web application, I noticed a file upload functionality designed to allow users to upload documents and images. The application claimed to sanitize inputs rigorously. However, during my testing, I found that the file upload feature did not filter out PHP files. This immediately raised a red flag, and I thought that here I’m going to win this engagement with a fancy webshell.
So I uploaded a simple and generic PHP webshell:
<?php system($_GET['chux']) ?>
After uploading it, I noticed that the server did store the file, but the development team insisted that exploiting this would be impossible to exploit it. They claimed their PHP configuration was hardened, with functions like system()
, exec()
, and passthru()
disabled in the php.ini file. These restrictions typically prevent attackers from executing system commands directly, which can stop most RCE attempts.
I used Acunetix PHP script to check what functions are enabled:
<?php
print_r(preg_grep("/^(system|exec|shell_exec|passthru|proc_open|popen|curl_exec|curl_multi_exec|parse_ini_file|show_source)$/", get_defined_functions(TRUE)["internal"]));
?>
To my surprise, non of the functions above were enabled on the server. In fact, the developer team leader even implied that they used the same PHP script to check what functions to disable in the first place.
We need to find a creative way to exploit this file upload functionality!
Assessing the Situation
While these PHP configurations initially seemed restrictive, I knew they weren’t foolproof. PHP has many other functions that can be abused depending on the context.
I thought that maybe instead of running directly to RCE with a cool webshell, I should come from a new direction that the development team did not expect.
And then I came up with an idea: let’s try to just get arbitrary file read on the server. Chances are that functions like incluce(), require() and file_get_contents() won’t be disabled, as they could be used for many different things in the app.
To test this, I uploaded a simple PHP script containing the following code:
<?php
$file = $_GET['file'];
include($file);
?>
This script accepts a file
parameter via the URL and passes it to the include()
function. If the server allows this script to execute, it would let me include and execute arbitrary files from the server, effectively achieving LFI. Unfortunately it won’t help me to get RCE, but at least I’ll be able to read files, hoping to find some secrets.
After uploading the script and accessing it via curl, I tested its functionality by passing a few file paths. Like I thought, the script executed successfully, confirming that the include()
function was not restricted.

Now let’s find some secrets to escalate our vulnerability!
Using the ability to read local files on the server, I took a wordlist of 1500 sensitive files to bruteforce with the webshell I just uploaded.
One of my primary targets was the .env
file. In modern web applications, this file often contains sensitive configuration data, such as database credentials, API keys, and cryptographic secrets. Luckily I could access this file and see very useful results:

The JWT_SECRET immediately caught my attention, as it is a critical component used to sign and validate users’ token and permissions.
Using The JWT Secret
With the JWT_SECRET in hand, I could craft valid JWTs for the application. JWTs are commonly used for authentication and authorization, and possessing the secret key allows an attacker to create tokens that the server will trust.
To identify how the application used JWTs, I logged in as a regular user and intercepted the authentication token issued by the server. Decoding the JWT revealed its payload, which included a role field:
{
"uid": 123456,
"role": "client"
}
Using the JWT_SECRET from the .env
file, I created a new token with the role field modified to admin. Tools like jwt.io or scripting libraries in Python or Java make this process straightforward.
After generating the forged token, I replaced the original JWT in my browser’s cookies with the new one. Refreshing the page granted me administrative access to the application!!!
As an admin, I now had access to the application’s management interface, including a feature for executing database queries. Using this feature, I dumped the entire database, which included user credentials, personal information, and other sensitive data.
The ability to execute arbitrary database queries opened up further exploitation opportunities, such as manipulating application data or exfiltrating more sensitive information.
Summary
In many cases, it’s ironically that instead of trying to attack the places that did not get extra layer of security, sometimes challenging the security mechanism of a specific functionality can be beneficial. I had another similar case when I challenged SSRF mitidation of Directus and evenually bypassed the app’s security check and got a CVE for that (CVE-2024–46990).
This exploit chain — from file upload to LFI, and finally to full administrative access — demonstrates how small oversights can lead to severe security breaches. By understanding these vulnerabilities and adopting proactive security measures, developers and organizations can protect their systems from similar attacks.
Hope you enjoyed the article :)
Keep on hacking!