|
============================================- Release date: December 4th, 2009
- Discovered by: Dawid Golunski
- Severity: Moderately High
============================================
I. VULNERABILITY
-------------------------
Invision Power Board <= 3.0.4 Local PHP File Inclusion and SQL Injection
Invision Power Board <= 2.3.6 SQL Injection
II. BACKGROUND
-------------------------
Invision Power Board (IPB) is a professional forum system that has
been built
from the ground up with speed and security in mind, taking advantage
of object
oriented code, highly-optimized SQL queries, and the fast PHP engine. A
comprehensive administration control panel is included to help you
keep your
board running smoothly. Moderators will also enjoy the full range of
options
available to them via built-in tools and moderators control panel.
Members
will appreciate the ability to subscribe to topics, send private
messages, and
perform a host of other options through the user control panel.
III. INTRODUCTION
-------------------------
For a good understanding of the vulnerabilities it is necessary to be
familiar
with the way IPB handles input data. Below is a quick trace of input
validation process. The code snippets come from IPB version 3.0.4.
line | file: admin/sources/base/ipsRegistry.php
352 | static public function init()
353 | {
... |
... |
462 | IPSLib::cleanGlobals( $_GET );
463 | IPSLib::cleanGlobals( $_POST );
464 | IPSLib::cleanGlobals( $_COOKIE );
465 | IPSLib::cleanGlobals( $_REQUEST );
466 |
467 | # GET first
468 | $input = IPSLib::parseIncomingRecursively( $_GET, array() );
469 |
470 | # Then overwrite with POST
471 | self::$request = IPSLib::parseIncomingRecursively( $_POST,
$input );
... |
The init() function cleans the input data passed via methods like GET,
POST or
others at the start of each request to the forum before any of the input
variables are processed.
Let's look into sanitization performed by cleanGlobals function:
line | file: admin/sources/base/core.php
1644 | static public function cleanGlobals( &$data, $iteration = 0 )
1645 | {
... |
1654 | foreach( $data as $k => $v )
1655 | {
1656 | if ( is_array( $v ) )
1657 | {
1658 | self::cleanGlobals( $data[ $k ], ++
$iteration );
1659 | }
1660 | else
1661 | {
1662 | # Null byte characters
1663 | $v = str_replace( chr('0') , '', $v );
1664 | $v = str_replace( "\0" , '', $v );
1665 | $v = str_replace( "\x00" , '', $v );
1666 | $v = str_replace( '%00' , '', $v );
1667 |
1668 | # File traversal
1669 | $v = str_replace( "../", "../",
$v );
1670 |
1671 | $data[ $k ] = $v;
1672 | }
1673 | }
1674 | }
As we can see the function removes null characters and "../" sequences
from
incoming data to prevent unwanted file inclusion.
The next function that affects the input is:
line | file: admin/sources/base/core.php
1573 | static public function parseIncomingRecursively( &$data,
$input=array(), $iteration = 0 )
1574 | {
... |
1583 | foreach( $data as $k => $v )
1584 | {
1585 | if ( is_array( $v ) )
1586 | {
1587 | $input[ $k ] =
self::parseIncomingRecursively( $data[ $k ], array(), ++$iteration );
1588 | }
1589 | else
1590 | {
1591 | $k = IPSText::parseCleanKey( $k );
1592 | $v = IPSText::parseCleanValue( $v,
false );
1593 |
1594 | $input[ $k ] = $v;
1595 | }
1596 | }
1597 |
1598 | return $input;
1599 | }
The purpose of this function is to clean the key/value pairs of an array
passed to it with help of the parseCleanKey and parseCleanValue
functions. The
first one can be skipped as neither of the attacks described later on
require
special characters inside variable names. The other looks as follows:
line | file: admin/sources/base/core.php
4100 | static public function parseCleanValue( $val, $postParse=true )
4101 | {
4102 | if ( $val == "" )
4103 | {
4104 | return "";
4105 | }
4106 |
4107 | $val = str_replace( " ", " ",
IPSText::stripslashes($val) );
4108 |
4109 | # Convert all carriage return combos
4110 | $val = str_replace( array( "\r\n", "\n\r", "\r" ), "\n",
$val );
4111 |
4112 | $val = str_replace( "&", "&", $val );
4113 | $val = str_replace( "", "-->", $val );
4115 | $val = str_ireplace( "