• 2007-01-10

    WordPress wp-trackback.php漏洞分析

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://superhei.blogbus.com/logs/4255503.html

    WordPress wp-trackback.php漏洞分析
          文/Superhei 2007/1/9
    1.Stefan Esser大牛2007/01/05发布的WordPress Trackback Charset Decoding SQL Injection Vulnerability [1]

    Code:wp-trackback.php

    $tb_url    = $_POST['url'];
    $title     = $_POST['title'];
    $excerpt   = $_POST['excerpt'];
    $blog_name = $_POST['blog_name'];
    $charset   = $_POST['charset'];
    .......
    if ( function_exists('mb_convert_encoding') ) { // For international trackbacks
     $title     = mb_convert_encoding($title, get_settings('blog_charset'), $charset);
     $excerpt   = mb_convert_encoding($excerpt, get_settings('blog_charset'), $charset);
     $blog_name = mb_convert_encoding($blog_name, get_settings('blog_charset'), $charset);
    }
    .......
    $dupe = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$comment_post_ID' AND comment_author_url = '$comment_author_url'");

    变量$charset编码post--->mb_convert_encoding()转换为get_settings('blog_charset') [utf-8]---->select

    se大牛的exp[2] 是用的uf7编码:'==>+-ACc- 饶过gpc,然后通过mb_convert_encoding转化为utf-8 '<==+-ACc-

    其实这个就是编码引起的2次攻击[3]饶过gpc引起的SqlInj。

    官方发布的补丁:2.0.6 wp-trackback.php

    // These three are stripslashed here so that they can be properly escaped after mb_convert_encoding()
    $title     = stripslashes($_POST['title']);
    $excerpt   = stripslashes($_POST['excerpt']);
    $blog_name = stripslashes($_POST['blog_name']);
    .........
    // Now that mb_convert_encoding() has been given a swing, we need to escape these three
    $title     = $wpdb->escape($title);
    $excerpt   = $wpdb->escape($excerpt);
    $blog_name = $wpdb->escape($blog_name);

    变量经过stripslashes()--->mb_convert_encoding()--->escape()--->select

    我们看看escape() :wp-includes\wp-db.php

     function escape($string) {
      return addslashes( $string ); // Disable rest for now, causing problems
      if( !$this->dbh || version_compare( phpversion(), '4.3.0' ) == '-1' )
       return mysql_escape_string( $string );
      else
       return mysql_real_escape_string( $string, $this->dbh );
     }

    mysql_real_escape_string()在一定的条件下是可以绕过的:
    The addslashes() Versus mysql_real_escape_string() Debate  http://shiflett.org/archive/184
    村雨牛牛在xcon也说过,但是mysql支持gbk的情况还是比较少的。有兴趣的可以自己测试下 :)

    2.rgod于2007/01/08发布的WordPress <= 2.0.6 wp-trackback.php Zend_Hash_Del_Key_Or_Index / sql injection exploit [4]

    Code:wp-settings.php

    function unregister_GLOBALS() {
     if ( !ini_get('register_globals') )
      return;

     if ( isset($_REQUEST['GLOBALS']) )
      die('GLOBALS overwrite attempt detected');

     // Variables that shouldn't be unset
     $noUnset = array('GLOBALS', '_GET', '_POST', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES', 'table_prefix');
     
     $input = array_merge($_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());
     foreach ( $input as $k => $v )
      if ( !in_array($k, $noUnset) && isset($GLOBALS[$k]) )
       unset($GLOBALS[$k]);
    }

    unregister_GLOBALS();

    这里unset了$_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, $_SESSION 等提交的变量。

    Code:wp-trackback.php

    if ( !intval( $tb_id ) ) //注意这个
     trackback_response(1, 'I really need an ID for this to work.');

    .................

    if ( !empty($tb_url) && !empty($title) && !empty($tb_url) ) {
     header('Content-Type: text/xml; charset=' . get_option('blog_charset') );

     $pingstatus = $wpdb->get_var("SELECT ping_status FROM $wpdb->posts WHERE ID = $tb_id");
    ......

    $tb_id没有’ 通过unset后存在end_Hash_Del_Key_Or_Index漏洞,导致注射。在分析时候提交:tb_id='&1740009377=1&496546471=1
    返回:I really need an ID for this to work 原来是在 :
    if ( !intval( $tb_id ) ) //这里拦住了。
     trackback_response(1, 'I really need an ID for this to work.');
     
    提交tb_id=1'&1740009377=1&496546471=1 成功触发,这里引发了一比较有意思的问题 :
    //test.php
    print intval($_REQUEST["id"]);
    ?>
    提交test.php?id=a1 得到 0,提交test.php?id=12a 得到 12。
    可以看出 intval是根据第1个字符来判断的,这样如果是像wp这样的判断:if ( !intval( $_ ) ) 还是有安全隐患的。

    参考:
    [1]:http://www.hardened-php.net/advisory_022007.141.html
    [2]:http://www.milw0rm.com/exploits/3095
    [3]:http://superhei.blogbus.com/files/1157120596.ppt
    [4]:http://retrogod.altervista.org/wordpress_206_zhdkoi_sql.html


    收藏到:Del.icio.us




    评论