The bug was eval() being used inappropriately, but on a deeper level it had to do with the way that in php, string interpolation is done via the grammar of the language.
For the purposes of the example, I'll illustrate a non-stupid usage in PHP of string interpolation, SQL.
execute("UPDATE foo SET age = '$age' WHERE id = '$id'"); seems perfectly reasonable, except that in this instance, we have a situation where an attacker could 'inject' data into the sql query by crafting a string
?age=14',access_level='admin
Giving the attacker's user account admin access.
The solution is to use mysql_quote_string, making the code more verbose, i.e.
mysql_query("UPDATE foo SET age='".mysql_escape_string($age)."' WHERE id='$id'");
But this isn't right yet, there's a possibility that gpc_magic_quotes is on, in which case you have to remove those quotes before you do this, otherwise you\'ll get that nasty bug where \' quotes get a \\ infront of them when they\' retrieved from the database. (bash.org suffers this at the moment).
so:
if (get_magic_quotes_gpc())
$age = stripslashes($age);
mysql_query("UPDATE foo SET age='".mysql_escape_string($age)."' WHERE id='$id'");
But! that's wrong again. This can be attacked based on what combination of mysql character encoding is being used, so we have to change this again:
if (get_magic_quotes_gpc())
$age = stripslashes($age);
mysql_query("UPDATE foo SET age='".mysql_real_escape_string($age)."' WHERE id='$id'");
Okay, now that's all over, lets look at how a real language handles it. Instead of using string interpolation that requires the above series of backflips to escape each untrusted argument, and very_long_function_names and string concatenation to break the very feature that makes php string interpolation easy, this is how to do the same kind of thing in perl or python, two other scripting languages.
$db->execute("UPDATE foo SET age=? WHERE id=?", $age, $id);
and
db.execute("UPDATE foo SET age=%s WHERE id=%d", (age, id))
By doing string interpolation via a library call that is aware of the specific needs of the database, it's possible to moot the entire issue.
PHP is stupid, and so are its users for putting up with this crap.