September 18, 2021

PHP fopen() function to local file inclusion

PHP fopen() function to local file inclusion
TL;DR: I abused a content delivery functionality that only allowed a specific domain to read local files on the server by providing the domain name as a file and abusing a path traversal issue.

This writeup is about a recent LFI vulnerability I found on a private program I've been working on for a while, and I found some interesting bugs on it, but I decided to share only this one, at least for now :)

So first of all, while I was google dorking for some interesting parameters I came through a page that looked like a video provider webpage and I found multiple endpoints for the same page that reflects with different videos which is just a parameter. i.e (https://videos.redacted.com/video/FAD4E99)

I checked that page looking for anything interesting I can find, and luckily I found a download video button, what was interesting is that the video was downloaded from an external CDN host and the download request had this video URL on it so it looked something similar to

https://videos.redacted.com/download-video?url=[URL]&name=[FILE-NAME]

The name parameter was just for the name of the file after being downloaded so it wasn't that important.

Anyway, after some while, I found how the filter for this `URL` parameter was, and to simplify it was like checking this regex pattern. `(.)*(\/)(.)*(\/cdn.redacted\/)(.)*`

So it was basically something like `[*]/[*]/cdn.redacted/[*]`, and anything not in that pattern will be filtered and a 403 response will come out...

Luckily there was an error popping up every time and it was `Warning: fopen([URL]): failed to open stream: HTTP request failed! HTTP/1.0 404 Not Found in readfile_chunked() (line 1718 of [PATH]).` as you can see clearly that my input is going through a PHP fopen() function, here I opened the [php.net](https://www.php.net/manual/en/function.fopen.php) site and started looking for some docs about this function to know what are the limitations I'm at and how can I read something with it.

The fopen function accepts various types of input, some are enabled by default and some aren't like javascript protocol based URLs and these are some of the most common ones.

$localRead = fopen("/home/rasmus/file.txt", "r");
$httpBased = fopen("http://www.example.com/", "r");
$ftpBased = fopen("ftp://user:password@example.com/somefile.txt", "r");

So if I started the payload with http(s)? then I cannot ever access the local files, I'll only be limited to access websites, but luckily in my case they allowed the parameter to enter the URL without specifying any protocol to be like `//xhzeem.me`, and this type of URL is handled as a local files request by the fopen() function, it doesn't recognize it as an HTTP request, so the payload I made finally was like this `/etc/cdn.redacted/../passwd` and the final exploit will be

https://videos.redacted.com/download-video?url=/etc/cdn.redacted/../passwd&name=xhzeem.txt

Burpsuite PoC Screenshot

The report was fixed on the same day and they told me that I bypassed an existing fix that was supposed to prevent the bug but turns out wasn't enough.