Web Security: Practicing what you, I, Preach

Everyone loves to make their own web apps. And it's very common for senior computer security undergraduates to design a web app for their capstone project. A couple of weeks ago some friends of mine were finishing their's, and asked me to take a look. Like all others interested in infosec, I instantly turned to input validation with a bit of header manipulation. I'd say it's common for most infosec students to do the same. It's ironic that I am still hosting legacy web apps (that I've built) which have not gone through similar critiques. Like most others, I designed them a while back, they worked, and I was proud; so I called them completed and moved on.

Fortunately for myself I don't advertise or link to my proof-of-concept web applications. But that's not the type of security I advise or would like. Let's take a look at one application called "The Ultimate Pokemon Card Database". Do I need to explain myself? I might as well anyways... So a few years ago I was cleaning my basement and found my old Pokemon card collection. I wanted to find out how much I could sell them for so I went about searching for a list. I was a somewhat hardcore collector, interested in not only rare-marked cards but misprints and promotional cards. Unfortunately most of the cards I thought would be big-ticket items had no listed prices. Turning to eBay I found the same, most we not listed, others were outrageous.  Now, being a kid at the time of collecting, I did not have my cards PSA marked. This also hurt my price estimation. More importantly, I could not find a holistic catalog of all printed, misprinted, and promotional Pokemon cards. The time had come to make one!

Now here I sit in the present, having given up in my search for ALL POKEMON CARDS EVER, with a semi-complete database. Most of the cards I had were scanned, and their images are in semi-high quality (the best my now 6 year old HP scanner could scan). The database is online, and one can browse most of the card sets. However, the database was not designed to be secure, dun dun dun.

The database is now hosted at (removed, no longer available).

Skipfish's results show possible issues for the load.php file.

Let's take a look at some of the security holes! I used a few tools to help with the analysis (more to familiarize myself with how they work than to trust in their authority and holistic evaluation). For a general scan I used Google's skipfish. It alerted me to a few directory traversal vulnerabilities and file/variable combinations which generated different warning and error messages. I then used RIPS, a PHP static code analyzer, to highlight sensitive commands used. I've included a screenshot to the Skipfish results for a file on the right. Here's what I found:

1. Viewable PHP source: When I designed the card site I was very into AJAX and had designed a small wrapper for AJAX requests given a page identifier. So if you wanted to load a style sheet you would issue load(styling.main) where the actual file was ./styling/main. Kind of cheesy, but I thought it was neat. Well the actual content pages (which included PHP code) were loaded in a similar fashion, without file extensions. This worked fine since I had the correct permissions (not world readable) on the host I designed the app on. However moving the app (or restoring it from a backup from an NTFS filesystem) required me to change the directory permissions. It's easy to forget this so to enhance security I added to the wrapper and appended a '.php' to all loads. (I also created a startup script, which takes a user as input, and sets the right permissions.)

2. Remote File Inclusion/Directory Traversal. I was stripping slashes (since my wrapper did not use them) and I was concatenating a script path but I was not decoding the URL. Now to get the periods to go through my wrapper I had to add a GET variable implemented to include files with extensions (such as CSS and JS). Or, since I wasn't decoding the URL in the PHP script %2E worked just fine.  Add a few %2F's and you have yourself the output of my /etc/passwd. I also included a check using PHP's realpath. I now make sure every included file's realpath exists below the statically defined application path.

RIPS highlights the use of EXEC to implement chmod.3. Code Injection. As easy as it was to create a remote file inclusion exploit, one could also inject code. Although they would first need my credentials to log in to the site. (I have a small administration interface for adding and editing cards.) Although SSL is not enabled for the app, I've never logged in. Though I'm sure there are some vulnerabilities with the login page. :) Anyways, when adding a card one can upload a picture, that picture is then renamed and saved. For some reason I thought it was appropriate to use PHP's exec to change the permissions of the uploaded card. It's more appropriate to use PHP's chmod function instead of exec.



RIPS highlights the use a GET variable in an SQL statement.

4. SQL Injection. Ah yes, good old SQL injection. I've known about SQL injection since high school but had always implemented it with some MySQL class wrapper. Since this app was very small I had chosen to sanitize my queries within the code. Thanks to RIPS I was able to spot a query which contained input that was not sanitized. This could have been exploited very easily, but it's fixed now locally. And, I decided to add that wrapper just in case I decide to make any small changes in the future. Since all of the page calls are filtered through the AJAX wrapper it was easy for me to overlook the values sent as GET variables. Skipfish also overlooked this since I implemented custom code to make the page calls (Skipfish performs an active analysis).

5. XSS. My custom AJAX wrapper adds an interface for submitting via POST by scraping form input fields. It also allows an included page to inject javascript. Bear with me on this explanation, I was young when I designed the hack. One can add javascript by embedding it into <ssjs>javascript here</ssjs>tags.

&lt;ssjs&gt;alert(document.cookie)&lt;/ssjs&gt;

Interesting, right? Well although no input is outputted on the page, the search outputs your last query back into the input field.

&lt;input type="text" id="name" value="' . $name . '" size="20" /&gt;

This would work fine normally, but with my custom code (which scrapes custom javascript before adding the AJAX result to the DOM), javascript can be injected.
Previous sanitation:

$name = urldecode($_POST['name']);<br />$name = $DATA-&gt;$name; //MySQL escaping

Simply stripping tags from the input fixes the vulnerability, something which should have been implemented before.
New Sanitation:

$name = strip_tags(urldecode($_POST['name']));<br />$name = $DATA-&gt;$name; //MySQL escaping

There's also an interface for loading the search page directly by calling /?load=search.

Moral of the story, implement security controls! Even if you feel your code is not used in a way where it could be exploited (as in my case), CODE IS UPDATED. I guarantee that if you do not implement security controls, updates will make you vulnerable. Now during the next few weeks I'll be examining an unfinished online game I wrote in high school. The code base is gigantic and I'm sure I'll find some fun code. I plan on highlighting the various hacks I used and associated security concerns.