Contracting can be really hit or miss. Sometimes, you're given a desk and equipment and treated just like an employee, except better paid and exempt from team-building exercises. Sometimes, however, you're isolated in your home office, never speaking to anyone, working on tedious, boring crap they can't convince their normal staff to do.
Eric was contracted to perform basic website updating tasks for a government agency. Most of the work consisted of receiving documents, uploading them to the server, and adding them to a page. There were 4 document categories, each one organized by year. Dull as dishwater, but easy.
The site was hosted by a third party in a shared hosting environment. It ran on a CMS produced by another party. WTFCMS was used in many high-profile sites, so the agency figured it had to be good. Eric was given login credentials and—in the way of techies given boring tasks everywhere—immediately began automating the task at hand.
Step 1 of this automation was to get a list of articles with their IDs. Eric was pleased to discover that the browser-based interface for the CMS used a JSON request to get the list of pages. With the help of good old jq
, he soon had that running in a BASH shell script. To get the list of children for an article, he passed the article's ID to the getChildren
endpoint.
Usually, in a heirarchy like this, there's some magic number that means "root element." Eric tried sending a series of likely candidates, like 0, -1, MAX_INT, and MIN_INT. It turned out to be -1 ... but he also got a valid list when he passed in 0.
Curious, he thought to himself. This appears to be a list of articles ... and hey, here's the ones I got for this site. These others ...? No way.Sure enough, passing in a parent ID of 0 had gotten Eric some sort of super-root: every article across every site in the entire CMS system. Vulnerability number 1.
Step 2 was to take the ID list and get the article data so he could associate the new file with it. This wasn't nearly as simple. There was no good way to get the text of the article from the JSON interface; the CMS populated the articles server-side.
Eric was in too deep to stop now, though. He wrote a scraper for the edit page, using an XML parser to handle the HTML form that held the article text. Once he had the text, he compared it by hand to the POST request sent from his Firefox instance to ensure he had the right data.
And he did ... mostly. Turns out, the form was manipulated by on-page Javascript before being submitted: fields were disabled or enabled, date/time formats were tweaked, and the like. Eric threw together some more scripting to get the job done, but now he wasn't sure if he would hit an edge case or somehow break the database if he ran it. Still, he soldiered on.
Step 3 was to upload the files so they could be linked to the article. With Firebug open, Eric went about adding an upload.
Now, WTFCMS seemed to offer the usual flow: enter a name, select a file, and click Upload to both upload the file and save it as the given name. When he got to step 2, however, the file was uploaded immediately—but he still had to click the Upload button to "save" it.
What happens if I click Cancel? Eric wondered. No, never mind, I don't want to know. What does the POST look like?
It was a mess of garbage. Eric was able to find the file he uploaded, and the name he'd given it ... and also a bunch of server-side information the user shouldn't be privvy to, let alone be able to tamper with. Things like, say, the directory on the server where the file should be saved. Vulnerability number 2.
The response to the POST contained, unexpectedly, HTML. That HTML contained an iframe. The iframe contained an iframe. iframe2 contained iframe3; iframe3 contained a form. In that form were two fields: a submit button, reading "Upload", and a hidden form field containing the path of the uploaded file. In theory, he could change that to read anything on the server. Now he had both read and write access to any arbitrary destination in the CMS, maybe even on the server itself. Vulnerability number 3.
It was at this point that Eric gave up on his script altogether. This is the kind of task that Selenium IDE is perfect for. He just kept his head down, hoped that the server had some kind of validation to prevent curious techies like himself from actually exploiting any of these potential vulnerabilities, and served out the rest of his contract.