PHP upload extension bypass strange behavior

I’m working on making a box to test file upload vulnerabilities and understand what causes them. I made two versions of this box, the first allows non restricted uploads. This was easy to exploit because any reverse shell can be uploaded, browsed to, and executed - I used php in this example with no issues.

In the other situation, I turned on extension whitelisting. At a very basic level it says you can only upload jpgs and pngs (does no mime checks). In the past I have bypassed this by adding something like .php.jpg for example.

What’s weird is when I do this, it bypasses the upload restriction, but when I browse to the file it shows a page with a black square but does not execute. However, if I log into the server and run php script.php.jpg against the file I uploaded it executes as expected and I get a reverse shell.

Any idea what would cause the second file to not execute? Changes to PHP or Apache? I’m using Ubuntu 18.04 and LAMP (via apt-get install lamp-server^).

Thanks in advance for any knowledge that can be shared.

It doesn’t matter what the extension is. If you directly pass it to the php runtime on the command line, it will ALWAYS execute the php contained within. Your browser may be setting the way it wants to receive the content.

For instance, my firefox sets the header Accept: image/webp,*/* when requesting favicons which means it will accept anything, but will prefer Content-Type: image/webp. So as a result, since the server supports the resource type image/webp, it will send that resource with the header Content-Type: image/webp.

I am by no means an expert on this subject, this is just my best guess.

Apache is will execute a file ending in .jpg as a jpeg image. Running it with PHP will execute it regardless of it’s name.

If you plan to get a shell by requesting the page, making it .jpg is not a bypass

Ah. I should have looked at it in burp. I’m assuming if I send the request and change the header to a non-image content type at the time of upload I can probably still bypass this without even changing the extension.