Exploit-DB #10304
Description: Invision Power Board 2.3.6/3.0.4 - Local File Inclusion / SQL Injection
Type: webapps
Platform: php
Date: 2009-12-04
Author: Dawid Golunski

Exploit:
=============================================
- 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( "<!--", "&#60;&#33;--", $val );
4114 |     $val = str_replace( "-->", "--&#62;", $val );
4115 |     $val = str_ireplace( "<script", "&#60;script", $val );
4116 |     $val = str_replace( ">", ">", $val );
4117 |     $val = str_replace( "<", "<", $val );
4118 |     $val = str_replace( '"', """, $val );

4119 | $val = str_replace( "\n", "<br />", $val ); // Convert literal newlines

4120 |     $val = str_replace( "$", "$", $val );
4121 |     $val = str_replace( "!", "!", $val );

4122 | $val = str_replace( "'", "'", $val ); // IMPORTANT: It helps to increase sql query safety.

4123 |
4124 |     if ( IPS_ALLOW_UNICODE )
...  |


The function cleans input data from characters used typically in XSS and SQL

attacks.


The resulting array containing sanitized input data from GET/POST methods is stored in ipsRegistry::$request array (as we can see on the first code

listing).

IV. LOCAL FILE INCLUSION VULNERABILITY
-------------------------

1. Description.


It is possible to include an arbitrary php file stored on the server in any

location (accessible by the php/web server process) by exploiting the
following code of IPB 3.0.4:

line | file: admin/sources/base/ipsController.php
142  |public function getCommand( ipsRegistry $registry )
143  |{
144  |         $_NOW = IPSDebug::ge