Misstep 8: How I Was Almost Hacked

An attempt to upload a PHP script as an image

Very many moons ago (ca. 2008 or so), I built an online image resizer. This was before HTML5 Canvas, so I leveraged PHP and GD Library to handle the resize. Nothing too special really, but I looking to learn image manipulation and provide a tool for people to use when they use forums.

Recently, I was digging out some old server harddisks I had in a crate and decided to start pulling files off before I destroy them. Inside the /imageresize/ directory, I found a few images that were not being correctly rendered by my OS. I made a guess that these would either be empty files, or a corrupt file due to an injection attack. So, I opened these up in Bless Hex Editor and had a peek, and I wasn’t surprised what I found.

People were uploading these files and including the following code:

echo 'oh dear.';

If the above code were executed, it would be relatively harmless, simply disclosing some server information (global variables and paths being the worst), and then say “oh dear” on the screen. This would be a great way to test if my server was allowing uploads that could later be executed, as it would give visible output and would not leverage a restricted function. Once this was determined, some file or other web shell would probably be uploaded, my sites taken offline or defaced, and the server used for malicious purposes.

Why this didn’t cause a breach:

  • My script removed the submitted file extension and replaced it with the appropriate file extension according to the browser’s suggested MIME (Media) type
  • My server was only configured to allow *.php and *.html (I know, whatever) to execute as a PHP script, so .jpg/.jpeg/.png/.gif etc would not have ever executed
  • I dropped the file name as well, replacing it with an MD5 of a current timestamp with a salt

What I did wrong:

  • I was storing the file uploads directly in the root directory. Files should not have been uploaded to any web accessible directory.
  • I was relying on the provided MIME (Media) type to determine file extension, this did not mean the file was what it said it was.
  • By using the current timestamp instead of an atomic value, I introduced a race condition where several people may receive the wrong image if they’re uploaded within the same second.

The important thing is learning what you did that was so wrong. Regardless, I’ve removed some domain references and empty lines, and threw a die() at the beginning, but here’s the terrible source if you’re interested:


Over the next few weeks, I’ll share a new series Missteps: Trial by Fire detailing terrible development decisions I’ve made, what I’ve learned from them, and how I’ve become an actual software engineer, and a consultant in the security industry.

About Author

Robert Lerner

Leave a Reply

Your email address will not be published. Required fields are marked *