I'm currently learning about LFI attacks. I have coded a simple example and tried to get the content of secrets.txt with an unsafe input ($_GET['extension']).

I have tried the following payloads with no success:

  • /../../../private/secrets.txt
  • /../../private/secrets.txt
  • docx/../../../private/secrets.txt
  • pdf/../../../private/secrets.txt
  • ../../private/secrets.txt

This is the current dir structure

Dir structure

This is the code I have written to learn about LFI:


$_GET['extension'] = isset($_GET['extension']) ? $_GET['extension'] : 'pdf';
$output = [];
$file_info = [
  'id' => 128,
  'file_name' => 'awesome-file',

$path = "data/public/generated/{$file_info['id']}.{$_GET['extension']}";
if (file_exists($path)) {
  $file = file_get_contents($path);
  $base64 = chunk_split(base64_encode($file));
  $output['data'] = [
    "name" => "{$file_info['file_name']}.{$_GET['extension']}",
    "file" => (
      $_GET['extension'] === 'pdf'
        ? "data:application/pdf;base64,$base64"
        : "data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,$base64"
} else $output['code'] = 'no-file';


So my question is,

  • Is this code even vulnerable to LFI?
  • If it is, how can you read the content of secrets.txt?


  • macOS Ventura 13.0.1


  • Version 8.2

File permissions:

  • data,private,public,generated (drwxr-xr-x)
  • secrets.txt,128.docx,128.docx (-rw-r--r--)

2 Answers 2


This code and file structure is not vulnerable at this time.

Since you are getting 128 from a trusted source (on a real scenario that would probably be a database), it is added before the vulnerable piece and there is no directory that can be used to go back.

If for example at a later point something created a public/generated/128.tempdir folder, then ?extension=tempdir/../../../private/secrets.txt would reveal the contents of secrets.txt

Since the code needs the mime type for each extension, I would recommend having an array of valid extensions and their mime types, rejecting any extension not there.

Unrelated to the security aspects, as it seems to return the file in a data uri, you probably wouldn't want to use chunk_split(). And I don't recommend returning large files as a base64 blob in json, it's better (as in efficient) to simply output the file contents when needed.

  • Thanks for your answer, if I create a folder named 128.tempdir and use tempdir/../../../private/secrets.txt (note the triple ../ not double) as the paylaod and I was able to read the content of secrets.txt, but is this the only way? Also why is it bad to use chunk_split? Appreciate your help :)
    – kokitax
    Sep 11, 2023 at 21:35
  • Indeed, it's three levels (128.tempdir, generated and public), answer updated.
    – Ángel
    Sep 11, 2023 at 23:20
  • Regarding "is this the only way?", that's a tricky question, since it's so easy to show that another way works, yet it's not obvious that no other way does. Still, this is probably the basic way. There may be some others if there's a symlink there somewhere, and \ is likely to also work (in addition to /) if the OS is Windows and
    – Ángel
    Sep 11, 2023 at 23:22
  • As for chunk_split, it's a bit pointless here, since it will add newlines to the base64 text, but you are returning everything on one url
    – Ángel
    Sep 11, 2023 at 23:23
  • I see, I will try to find new payloads and some other cases in which I would be able to read the contents of secrets.txt. Thanks for your help.
    – kokitax
    Sep 12, 2023 at 6:48

Your code seems vulnerable to path traversal/file read, not LFI.

LFI works when an attacker can execute code from a file, usually exploiting a “dynamic file inclusion” mechanism implemented in the target application. This is due to the use of user-supplied input without proper validation.

Here we can consider an attacker that is able to upload a docx file inside the public folder.


The path does exist, and since the extension is not pdf, the browser attempts to display the file content data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,$base64 which was converted to base64. This instead of 128.docx

Other payloads could also be exploitable to access secrets.txt, but it may depend on many characteristics, ie. OS version, file permissions, web server+php configurations and versions.

  • 2
    The code uses file_get_contents($path); not include() or require() so it would be vulnerable to path traversal/file read. Not local file inclusion. In short you can read files, but not execute code as file_get_contents($path); does not evaluate the file content through the PHP interpreter like include() does.
    – wireghoul
    Sep 8, 2023 at 23:06
  • Thats important, see my updated answer.
    – v-g
    Sep 9, 2023 at 2:56
  • I created the document you specified inside the public folder and tried to read it with the payload you provided and I couldn't manage to read the file. Also, what's the difference between the payload you provided docx/../evil-supplied.docx and the payload I tried docx/../../../private/secrets.txt?. Thanks for your time
    – kokitax
    Sep 11, 2023 at 21:22
  • I edited my answer and provided versions and file permissions
    – kokitax
    Sep 11, 2023 at 21:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .