This site graciously hosted
by our friends at




Analysis of Topical Vulnerabilities

23 July 2003

It's probably not one of our more admirable personality traits, but once in a while a vulnerability comes along that makes us laugh out loud. To tell the truth, we enjoy watching (from a safe vantage point) while a fellow practitioner takes a good slip on the old banana peel. More soberly, such blunders remind us how distant our world is today from true "software engineering". How are we ever going to build virtual bridges that don't fall down with the crude tools in use today? (And we mean to include that piece of meat between our ears as one of the "crude tools".)

Last week, Arnaud Jacques of Securiteinfo.com reported a vulnerability in digi-FX's "digi-news", a PHP script that allows you to easily add, edit, and delete news. (SecurityTracker followed up the announcement with an analysis. You can find all of the details at http://securitytracker.com/alerts/2003/Jul/1007218.html.) We can't shake a grin as we think about it. See if you can do any better.

The script is supposed to authenticate the user by verifying the user name (taken from a cookie) and the password, supplied at run time. Here is the check.

if ((@$HTTP_COOKIE_VARS['user'] != $digiNews['user']) &&
(@$HTTP_COOKIE_VARS['pass'] != md5($digiNews['pass']))) {
   login();
   exit;
   ...


Do you see the bug? It's timeless: one of your authors admits to making this exact mistake over 30 years ago. In pseudo-code, we might paraphrase the buggy line as, "IF the username is not right AND the password is not right THEN reject the login attempt." The effect, of course, is that an attacker only needs to get one or the other correct in order to pass the authentication check!

A COGNITIVE ERROR

What we have here is a beautiful example of the sort of mistakes that occur when we try to translate our everyday thinking to computerese.

We guess the programmer was probably thinking, "If the username is right and the password is right, we're OK." But the way that most block-oriented languages work, it's usually easier to place the "reject" code first in lexical order--that is, as it lies "on the page". That's the way to avoid the use of the horrific GOTO (remember those?). In order to make that happen, we need to create a logical converse by negating the two conditions and then changing the AND to an OR. "If the username is not right", you want to say, "or the password is not right, we're not OK." The logical error was introduced when the programmer forgot to flip the binary operator. We guess the reason the result seems so silly to us is that it's the kind of authentication error a human being isn't likely to make.

A TRICKY POINT OF LANGUAGE

There's another point of substance here. If the statement had been written in natural human language, the proof-reading programmer (with a little luck) might well have found it.

Take another look at the code. The problem arises in those two concatenated ampersands ("&&"). Were they vertical bars instead ("||"), the bug would not exist.

It really comes down to a usability (or "human factors") argument. Even though many of our friends are so experienced with those very Boolean operators that they casually drop them into conversational jokes, we will argue that including them in a language raises the cognitive burden on the programmer, and makes semantic mistakes more likely. It may be for this reason that some languages (including PERL, but not, we think, PHP) allow the use of the English words "AND" and "OR" in addition to the Boolean symbols. (As you know, the proper use of all these operators is a very complicated topic, especially when one considers possible side effects resulting from the order in which conditions are expressed or evaluated--so we'll travel no further down that road.)

In general, we're fans of simple code. After all, if the programmer had written the equivalent of "If the username is wrong, exit; if the password is wrong, exit", the code might execute slightly slower, or take up a little more memory; but we wouldn't be discussing it in a security analysis.

A QUESTION OF TESTING

Bringing the discussion back to security, we have a final point. We think the failure here is not purely one of implementation. We'll also point the finger at inadequate testing.

Just because simple logical errors like the one under the microscope here are so easy, it's critical to devise and use testing methods that verify correct operation. In this case, while it may be possible to find a static tester that could catch this error, we think it's unlikely. This seems to us to be a case for "black box" testing, in which applications are fed myriad input streams and the results checked predicted results. Precisely because this is the sort of logical error that a human is unlikely to commit, if the check was being performed manually, it's important to use automated test harnesses and idiosyncratic input streams to look for "illogical" logic errors. Chapter 6 covers this ground well.

Automating your testing won't always keep you from being the object of fun and finger-pointing. It can keep you from slipping on the same banana peel twice, though. Some days, that's all the dignity we can hope for.

Mark G. Graff
Kenneth R. van Wyk
23 July 2003

Copyright (C) 2003, Mark G. Graff and Kenneth R. van Wyk. Permission granted to reproduce and distribute in entirety with credit to authors.


Site Contents Copyright (C) 2002, 2003 Mark G. Graff and Kenneth R. van Wyk. All Rights Reserved.
webmaster@securecoding.org