What's a good forum script that's actually secure?

Red Squirrel

[H]F Junkie
Joined
Nov 29, 2009
Messages
9,211
I was going through my server logs and saw some pretty scary stuff, one of my forums running phpbb2 has some SERIOUS flaws. A specially crafted URL was able to pull up all the information from the database on a specified user (in my case the admin account). I don't know exactly how it's even coded in such a way that it allows this, but basically it produces a print_r export of the specified user's database row.

I am looking at remaking all my forums into one single forum and I was looking into phpbb3, but I'm starting to wonder if I should perhaps look in other directions. It seems there's not that many choices these days in terms of free forum systems. There's phpbb, SMF (not a huge fan, I HATE their PM layout for one) and there's YABB. Anything else I may be missing?

Since I'll be modding this I can't be constantly updating it so I need something that is already secure out of the box.

Also can someone explain to me how this exploit even works, here is a sample URL, I changed some info because this is actually an exploit digging right into my database:

Code:
http://www.domain.com/forum/profile.php?mode=register&agreed=true+[PLM=0][R]+GET+http://www.domain.com/forum/profile.php?mode=register&agreed=true+[0,22775,73872]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24840,72900]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24786,73546]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24790,73924]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24787,71358]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24792,68993]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24810,71620]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24780,72478]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24780,73294]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24794,70450]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24788,72995]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24798,74102]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24770,74439]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,25043,72787]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24778,73611]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24784,74963]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24819,71517]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24783,73370]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24788,73592]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24794,72943]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,25024,72774]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24798,73530]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24791,69749]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,24803,73392]+-%3E+[R]+POST+http://www.domain.com/forum/profile.php+[0,0,27325]+-%3E+[N]+GET+http://www.domain.com/forum/viewtopic.php?t=6478+[12329,0,42900]

Somehow that URL pointed towards a phpbb forum will print out all the info for the admin account. I'm not sure where in the URL they specified the userID but I see lot of numbers and stuff guessing it has to do with that.

What exactly is all the square bracket stuff going on and the plus signs? I've never seen a URL like this before nor know how php would interpret or what it does with it.
 
vBulletin and SMF, possibly buddy press.

Never been a fan of phpbb...
 
vB is nice, but don't want to pay anything, especially not what they're asking. :eek: Never head of buddy press though, I'll have to give it a try, and give SMF a second go as well.

Do you know how that URL hack works? This is new to me. It looks like some form of injection but really not sure hot it works. What does the [PLM=0][R] stuff do? I also see a GET in there, is this actually exploiting the web server itself?
 
I don't know for sure, but every site I have ever setup with phpbb in the last 10yrs, no matter how many security mods and fixes, has eventually been compromised.

SMF I have never had an issue with.
 
What about Xenforo? Not free, but reasonably priced I think. It's made by former developers of vB and has a very good track record as far as security goes.

vB is complete trash these days if you ask me. Went downhill after the jump to 4.0, which is around the time the old devs left and created XenForo a few years later. There's a reason this forum still runs on 3.8 ;)
 
Hmm I suppose it's worth looking into, never liked the idea of paying but really 150 is not THAT much provided there's not some weird license agreement issues that would limit what I can do with it such as have a dev/test/prod environments (3 installs). I'll have to look into it.

I'm currently playing around with SMF. The thing I did not like about it is the PM interface, ti's just hard to follow because it just dumps all the PMs together instead of the typical setup where you see just the titles and click to view individual ones, but that is actually changeable in the user settings, so I can easily just change those defaults. PhpBB 3.0 is really nice too though but not sure what their security track record is for that particular version, everything I read in general about phpbb says it's a hacker magnet.

I'm still trying to figure out how that URL hack works though, because I want to ensure that the code I write myself can't be exploited with that type of technique.

Also I may end up having to fix that exploit on my phpbb 2 board because that board is an archive so I may want to keep it as is. Though I may also be able to do some kind of tweak to make the database read only and the file structure read only so that any kind of hack can't change anything anyway.
 
Well first thing's first. What's the source code of profile.php?

Second of all, actually try passing that URL into it yourself and try putting log points at various steps within the script. For instance, do a print_r of the $_GET with that passed in. That's a start.

Finally there's a lot of stuff going on in that. For instance I think + signs in the URL get translated to spaces. All of the square bracket things become arrays. Though I'm not even sure how that works, because if they're after what's considered a space, they would be... anonymous arrays?

Yeah, you need to do a print_r of the $_GET with that passed in. I suggest something like exec("echo \"". print_r($_GET) ."\" >> LOGFILEOFCHOICE.LOG") and then show us what happens. It's hard getting to the bottom of this just staring at a URL, unless someone in here is very knowledgeable in URL encoding and decoding standards for PHP... but again the script source can affect that anyway.
 
This is profile.php:

Yes it's an old forum script but that's beside the point, I want to learn how this type of URL manipulation exploit works so I can write code that can't be exploited by it when I code my own stuff or fix existing stuff.

Code:
<?php
/***************************************************************************
 *                                profile.php
 *                            -------------------
 *   begin                : Saturday, Feb 13, 2001
 *   copyright            : (C) 2001 The phpBB Group
 *   email                : [email protected]
 *
 *   $Id: profile.php,v 1.193.2.7 2006/04/09 16:17:27 grahamje Exp $
 *
 *
 ***************************************************************************/

/***************************************************************************
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 ***************************************************************************/

 
 echo("GET:".print_r($_GET)."<br><br>POST:".print_r($_POST));//TEST CODE
 
define('IN_PHPBB', true);
$phpbb_root_path = './';
include($phpbb_root_path . 'extension.inc');
include($phpbb_root_path . 'common.'.$phpEx);

//
// Start session management
//
$userdata = session_pagestart($user_ip, PAGE_PROFILE);
init_userprefs($userdata);

//
// End session management
//

// session id check
if (!empty($HTTP_POST_VARS['sid']) || !empty($HTTP_GET_VARS['sid']))
{
	$sid = (!empty($HTTP_POST_VARS['sid'])) ? $HTTP_POST_VARS['sid'] : $HTTP_GET_VARS['sid'];
}
else
{
	$sid = '';
}

//
// Set default email variables
//
$script_name = preg_replace('/^\/?(.*?)\/?$/', '\1', trim($board_config['script_path']));
$script_name = ( $script_name != '' ) ? $script_name . '/profile.'.$phpEx : 'profile.'.$phpEx;
$server_name = trim($board_config['server_name']);
$server_protocol = ( $board_config['cookie_secure'] ) ? 'https://' : 'http://';
$server_port = ( $board_config['server_port'] <> 80 ) ? ':' . trim($board_config['server_port']) . '/' : '/';

$server_url = $server_protocol . $server_name . $server_port . $script_name;

// -----------------------
// Page specific functions
//
function gen_rand_string($hash)
{
	$rand_str = dss_rand();

	return ( $hash ) ? md5($rand_str) : substr($rand_str, 0, 8);
}
//
// End page specific functions
// ---------------------------

//
// Start of program proper
//
if ( isset($HTTP_GET_VARS['mode']) || isset($HTTP_POST_VARS['mode']) )
{
	$mode = ( isset($HTTP_GET_VARS['mode']) ) ? $HTTP_GET_VARS['mode'] : $HTTP_POST_VARS['mode'];
	$mode = htmlspecialchars($mode);

	if ( $mode == 'viewprofile' )
	{
		include($phpbb_root_path . 'includes/usercp_viewprofile.'.$phpEx);
		exit;
	}
	else if ( $mode == 'editprofile' || $mode == 'register' )
	{
		if ( !$userdata['session_logged_in'] && $mode == 'editprofile' )
		{
			redirect(append_sid("login.$phpEx?redirect=profile.$phpEx&mode=editprofile", true));
		}

		include($phpbb_root_path . 'includes/usercp_register.'.$phpEx);
		exit;
	}
	else if ( $mode == 'confirm' )
	{
		// Visual Confirmation
		if ( $userdata['session_logged_in'] )
		{
			exit;
		}

		include($phpbb_root_path . 'includes/usercp_confirm.'.$phpEx);
		exit;
	}
	else if ( $mode == 'sendpassword' )
	{
		include($phpbb_root_path . 'includes/usercp_sendpasswd.'.$phpEx);
		exit;
	}
	else if ( $mode == 'activate' )
	{
		include($phpbb_root_path . 'includes/usercp_activate.'.$phpEx);
		exit;
	}
	else if ( $mode == 'email' )
	{
		include($phpbb_root_path . 'includes/usercp_email.'.$phpEx);
		exit;
	}
}





redirect(append_sid("index.$phpEx", true));

?>


And here is the output:

Code:
Array ( [mode] => register [agreed] => true [0,22775,73872] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24840,72900] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24786,73546] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24790,73924] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24787,71358] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24792,68993] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24810,71620] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24780,72478] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24780,73294] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24794,70450] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24788,72995] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24798,74102] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24770,74439] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,25043,72787] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24778,73611] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24784,74963] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24819,71517] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24783,73370] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24788,73592] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24794,72943] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,25024,72774] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24798,73530] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24791,69749] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,24803,73392] -> [R] POST http://uovalor.com.d.appdev.loc/forum/profile.php [0,0,27325] -> [N] GET http://uovalor.com.d.appdev.loc/forum/viewtopic.php?t=6478 [12329,0,42900] ) GET:1

POST:1

What I'm confused about is how is this putting variables into $_GET without actually using a query string in the url and how is it performing a HTTP POST via URL and not an actual POST request to the server?


Oh and another thing, the original print_r output that the exploit produced was actually triggering an old debug point and that is something I had added myself and since removed. So not actually sure what this exploit is aiming to do or if it even did exploit anything after all, but it's obviously doing something if it was able to get to that point in the code.
 
Last edited:
This is profile.php:

Yes it's an old forum script but that's beside the point, I want to learn how this type of URL manipulation exploit works so I can write code that can't be exploited by it when I code my own stuff or fix existing stuff.


And here is the output:

What I'm confused about is how is this putting variables into $_GET without actually using a query string in the url and how is it performing a HTTP POST via URL and not an actual POST request to the server?

Oh and another thing, the original print_r output that the exploit produced was actually triggering an old debug point and that is something I had added myself and since removed. So not actually sure what this exploit is aiming to do or if it even did exploit anything after all, but it's obviously doing something if it was able to get to that point in the code.


It _is_ putting stuff into $_GET - everything following the first ? becomes GET variables. There are two of them, "mode=register" and "agreed=true [PLM=0]... " (the plus signs represent spaces, the %3E represents '>').

I'll take a look at it in a bit, probably a known exploit (this is phpBB 2.0.21?), but it would be fun to read through it and figure it out.
 
What is the significance of adding stuff after a boolean value though, it's either true, or it's false no? Unless the code is extremly badly written where it's taking that literal value and trusting it. I checked and it seems to just use isset to see if it's been set or not so it's value should not be significant.

Though is there any reason to use $HTTP_GET_VARS over $_GET? That's what they use in their code.

Or are the -> and [] andn spaces doing something weird and php actually does not take it as literal text string and does something weird with it?
 
What is the significance of adding stuff after a boolean value though, it's either true, or it's false no? Unless the code is extremly badly written where it's taking that literal value and trusting it. I checked and it seems to just use isset to see if it's been set or not so it's value should not be significant.

Though is there any reason to use $HTTP_GET_VARS over $_GET? That's what they use in their code.

Or are the -> and [] andn spaces doing something weird and php actually does not take it as literal text string and does something weird with it?

If you look at the files, you'll see that $HTTP_GET_VARS is an alias for $_GET:

Code:
common.php:
	$HTTP_GET_VARS = $_GET;

In php, -> is dereference, and [] is an array. Not sure the point of the [R] and such, unless it is an attack that actually goes after apache's mod_rewrite or similar.
 
Only thing I don't understand though, shouldn't that string just be treated as a regular string anyway? Or is there an exploit in php where it starts to actually parse stuff in it instead of treating it literally?
 
Back to the original topic, SMF's permission system is GARBAGE. It's way too complicated and restrictive, and it's hard to know what's going on,there's no easy way to know who has permission to what, it's just a jumbled up mess and have to jump through all sorts of hoops to set permissions and nothing is in one spot or easy to overview. What a mess.

I may have to go back to phpbb just for this reason alone. Once I can figure out how these type of URL exploits work I'll just have to audit the whole code and fix them.
 
Well how do you even know that the hackers actually did get in? Do you have some kind of tracking mechanism in place?

Or what, you tried that URL and then you saw the print_r yourself?

Anyway this is a bit of a shot in the dark, but maybe see what happens when you start shortening the URL. It has a pretty repetitive pattern, so maybe try taking off a chunk at a time and see what results it's outputting. You might get it down to a pattern that shows what's going on.
 
Well it was a false alarm as that print_r was actually some debug code I had added at one point and forgot to remove and it only showed the details on the currently logged in user. Though obviously that url is trying to do something, so I'm still wondering how it works and whether or not php is treating the stuff as plain text or if it somehow interprets it.
 
mybb is another message board to consider. we use it for our car forum and haven't had any problems with it in the last 4 years or so. pretty active community, nice features. we had some problems with human spam but after implementing some creative registration requirements ("what car is in our banner?" those have mostly been solved.
 
I am running IP.Board and if I had to launch a new forum today I'd buy IP.Board again.

I do have a Xenforo license and it's just too meh at this point. I wouldn't put Xenforo into production. Of course the XF fanbois will point out that there are forums with millions of posts that run on XF, but you have to consider that those forums have dedicated staff that can monkey with it all day and make it fit their every need.

If you are just one guy who has to have something running with decent mod support out of the box then XF is not the right choice at this time.
 
Well it was a false alarm as that print_r was actually some debug code I had added at one point and forgot to remove and it only showed the details on the currently logged in user. Though obviously that url is trying to do something, so I'm still wondering how it works and whether or not php is treating the stuff as plain text or if it somehow interprets it.

It's showing you stuff from the database. Of course PHP is executing it - you were directly printing unsanitized variables. At this point, I'm guessing it's spam that is looking for some old exploit, not sure what. The particular string format seems to appear a bit around the net. E.g. the spam on this message board: http://www.womanicer.nl/page8.php?category=1&post=1&messagepage=33&messagePage=44
 
So php will actually execute variables and not just treat as text? So could somebody change any internal variable value through the url using special strings? Like something like "?[special percent string]$internalvar=value" so php will actually execute that and not just treat it as plain text? This sounds like a serious php flaw if yes.

It may be possible that particular string was trying to bypass the captcha too because I used to get TONS of spam on that forum till I put 3 separate captchas just to throw off bots. Now registrations are closed altogether as it's just an archive forum.

After lot of aggravation I figured out the SMF permission system, it's really weirdly designed but come to think of it it's not like I'll be in there all the time so once I have it working how I want I probably wont need to go in that often. So think I'll probably end up sticking with SMF.
 
So php will actually execute variables and not just treat as text? So could somebody change any internal variable value through the url using special strings? Like something like "?[special percent string]$internalvar=value" so php will actually execute that and not just treat it as plain text? This sounds like a serious php flaw if yes.

I'm guessing it's your code doing the expansion (not execution) of variables. If you were calling "print" somewhere, it will expand the variables in the string. For example:

Code:
$foo = "test";
$testGet = array("test" => urldecode("true+$foo"));
print(print_r($testGet));

will produce

Array
(
[test] => true test
)
 
I'm looking at myBB right now, but smf v2 looks much better than the last time I used it and phpBB was a hack magnet...

I'd like to hear more about IP.board please!
 
I ended up sticking with SMF. Got it mostly setup how I want short of making a custom skin, now I just have to merge my other forums into it and make sure the old URLs will still work. Basically merging a bunch of forums I manage into one for easier maintenance. Should be a nice fun project.
 
I ended up sticking with SMF. Got it mostly setup how I want short of making a custom skin, now I just have to merge my other forums into it and make sure the old URLs will still work. Basically merging a bunch of forums I manage into one for easier maintenance. Should be a nice fun project.

Good call I think. I've always liked SMF myself and the easy updating from within the admin panel. I always grabbed a free SSL cert from StartSSL.com and used them on all of my forums too. You can use the repair_settings.php file to correct URL related issues after applying the SSL cert then remove it. Do it at the first stages or you can compromise your forums with that repair settings file.
 
Good to know there are free SSL certs. I'll have to check that out. I've never really bothered to use SSL for my forums but been thinking it may be a good idea.
 
Back
Top