Misstep 9: Trial by Fire, the Perfect Storm that Created Me

Welcome to the second misstep of 2020… A series of hindsight. Back in late 2006, I started a small forum site where I learned that building desktop applications != hosting web applications that other people use. The former may be breakable, but it won’t hurt myself or other people. The latter can devastate a business and other users.

It wasn’t that I didn’t care, it was that I didn’t know better. A lot of developers are in the same boat. They want to build systems well, but they simply do not have access to the training or tools to determine if it was built securely.

Today, I want to take you on an adventure back to 2006/2007. I was working overnight 12-hour shifts in a factory, convinced I wouldn’t find a career in information technology. I’d get off at 6:30am, drive home and usually hop online to my new creation. I didn’t have a smart phone, so I couldn’t check my sites and see if there was spam or other issues, so it would go all night long unwatched. Here’s what that site looked like:

My first forum site, with blogs and articles

First things first, it was a WATP site… Windows, Abyss, Text File, and PHP site. If I recall correctly, it was PHP 5.1.6. I was a bit apprehensive learning MySQL at the time, or Linux, and wanted to focus on PHP only. This is why PHP is called “insecure”, it is simple to get something up and running that is terrible. So, people like me write legit crap and publish it.

The forum posts were all stored in text files, as were sessions, user logins (plain text the first few weeks, then unsalted MD5!), etc. This works reasonably well since I would extort a handful of friends to use my site and, even then, nobody wanted to use it. There were no chances for a race condition to happen.

Race Conditions Happen

A race condition happens when two or more actions are applied to the same datastore at the same time. My file structure was simply a random number. PHP’s random number generator is awful, and this practice is awful. Let’s take a peek:

$titl = strip_tags($_POST["titl"]);
$dat = strip_tags($_POST["dat"]);
$msg = strip_tags($_POST["msg"]);
$poster = strip_tags($_POST["poster"]);
$rnd = rand(0,999999);

//Convert linefeeds to bbcode key and remove. Apo's are done in edittopic.php
$msg = nl2br($msg);
$msg = str_ireplace("<br />", "[br]",$msg);
$msg = str_replace("\r", "", $msg);
$msg = str_replace("\n", "", $msg);

$file = fopen("forum/" . $rnd . ".txt", 'x');
fwrite($file, $titl . "\r\n");
fwrite($file, $dat . "\r\n");
fwrite($file, $msg . "\r\n");
fwrite($file, $poster . "\r\n");

So, I’m taking a random number as a file name, and adding each “field” as a newline within the file.

PHP’s random number generator is seeded by the current timestamp, so within a few milliseconds of each other, two posts can create the same topic, throw off the posts, or cause some people’s posts to be overwritten. It was a hot mess.

Race Condition + c:\site\sessions.txt

I had committed several “sins” of secure development at the time:

  • I did not use atomic identifiers on files, meaning two writes would cause an error in some conditions.
  • I did not turn off error reporting, I was editing the site live on the server, so I thought I needed to display errors.
  • I stored the “sessions” file within the webroot.

So, I had a few folks who would use my site who also had some legit curiosity into a site’s inner workings. It so happened when this user, we’ll call him “Cody”, logged into the site and spotted a write error on the site. Something along the lines of:

Parse Error: Could not open c:\site\sessions.txt for writing.

Quite possibly, accessing this file via https://example.com/sessions.txt would be the next thing that was attempted. And, this Cody did. Inside this file, he seen my username, next to a session ID. He then copied that into the browser, and he was in my account.

I get home from work, to find I was online all night…

Several posts were made from my account, mostly about how I’ve been owned and such. I totally deserved it. As a matter of fact, here’s the forum topic that told me about the exploit. Sadly, I don’t have the site running, so I can only show you a (Somewhat anonymized) forum “file” instead:

Attention Bob
Fri, 16 Feb 2007 16:24:34 CST
This is why SQL > TXT.[br][br]Love,[br]Cody
Fri, 16 Feb 2007 21:17:06 CST
I been hacks.
Tue, 27 Feb 2007 20:25:48 CST
Should be fixed now.

This post was from my account, since my session ID was exposed. I was able to fix it, but even my fix was implemented somewhat poorly.

Security by Obscurity, and other hilarious antics

$cok = $_COOKIE["COOKIE"];
$filses = @fopen("http://example.com/site/online.bmp" , "r");
while ($names = @fscanf($filses, "%s\t%s\n")) 
	list ($userkey, $username) = $names;
	if ($userkey==$cok)

So, the sessions.txt file was changed to “online.bmp”. Still in the webroot, but a little less likely to be spotted. Also, I added the error control operator “@” before the file functions (fopen, fscanf, and fclose) to hide the errors. Now, we’re getting errors that nobody knows about, but hey, it’s not giving away my clever fix.

Later, I added in an additional check. You would have to have both the session ID, and be on the same IP address as you were at login to stay logged in. Hey, this sounds fantastic! And it would be, except for these two failures (which I don’t have the code for anymore):

  • I typo’d… I used a single equals sign (=) or assignment, instead of a double (or triple) equals sign (== or ===), comparison and strict comparison, respectively. So, what ended up happening is that sessions were now disregarded, and the IP address is what was important. So, living at home with the rents at the time, my younger brother kept accidentally posting from my account and didn’t know why. This took an embarrassingly long time to correct.
  • Some users were using AOL’s custom-wrapped Internet Explorer, which proxies requests via different cache servers. This means that the IP wasn’t steady, and some users were randomly being logged off as a result.

I remember sharing my IP-following idea with Michael Coates at THOTCon 0x1 back in 2010, who liked the idea, but was immediately familiar with the issue you’d face with transient IP addresses.

Over time, I’ve improved how I’ve handled sessions, using PHP’s in-built session mechanism, ensuring proper entropy, proper cookie flags, and so on. This resolved my authentication woes, but there were still plenty more problems I was tempered by.

Join me again next Tuesday to see what other awful mistakes I’ve made.

About Author

Robert Lerner

Leave a Reply

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