• 2007-04-15

    php-fusion的一个Xday分析

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

    php-fusion的一个Xday分析

    by Superhei@ph4nt0m
    2007-04-15
    http://www.ph4nt0m.org

    includes/update_profile_include.php

       ...
       $newavatar = $_FILES['user_avatar'];
        if ($userdata['user_avatar'] == "" && !empty($newavatar['name']) && is_uploaded_file($newavatar['tmp_name'])) {
            if (preg_match("/^[-0-9A-Z_\.\[\]]+$/i", $newavatar['name']) && $newavatar['size'] <= 30720) {
                               $avatarext = strrchr($newavatar['name'],".");
                if (eregi(".gif", $avatarext) || eregi(".jpg", $avatarext) || eregi(".png", $avatarext)) {
                    $avatarname = substr($newavatar['name'], 0, strrpos($newavatar['name'], "."));
                    $avatarname = $avatarname."[".$userdata['user_id']."]".$avatarext;
                    $set_avatar = "user_avatar='$avatarname', ";
                    move_uploaded_file($newavatar['tmp_name'], IMAGES."avatars/".$avatarname);
                    chmod(IMAGES."avatars/".$avatarname,0644);
                    if ($size = @getimagesize(IMAGES."avatars/".$avatarname)) {
                        if ($size['0'] > 100 || $size['1'] > 100) {
                            unlink(IMAGES."avatars/".$avatarname);
                            $set_avatar = "";
                        }
                    } else {
                        unlink(IMAGES."avatars/".$avatarname);
                        $set_avatar = "";

    判断的伪代码:
    <?
    $newavatar['name']= $_GET[a]; //提交 a=1.php.php.gifa
    print preg_match("/^[-0-9A-Z_\.\[\]]+$/i", $newavatar['name']); //名字里可以有.
    $avatarext = strrchr($newavatar['name'],".");//取后缀
    print eregi(".gif", $avatarext); //只要后缀里包含有.gif就ok了 那么我们可以提交1.php.php.gif

    $avatarname = substr($newavatar['name'], 0, strrpos($newavatar['name'], "."));取最文件名的前面的部分
    $avatarname = $avatarname."[".$userdata['user_id']."]".$avatarext;
    $set_avatar = "user_avatar='$avatarname', ";
    print $avatarname; //1.php.php.gifa==>1.php.php[id号].gifa
    //move_uploaded_file($newavatar['tmp_name'], IMAGES."avatars/".$avatarname);

    在apache下是可以利用了[1],那么下面的getimagesize()的判断:
    if ($size = @getimagesize(IMAGES."avatars/".$avatarname)) {
        if ($size['0'] > 100 || $size['1'] > 100) {
    //可以利用关于paas getimagesize()的帖子构造图片 [2]

    当时我是在官方下的v6.00.305测试的,不过无意中在milw0rm上已经有人发过了[3]。 :(

    于是又到官方逛,在一个角落里发现了新点的版本:v6.01.10的Code:
    ........
        if ($userdata['user_avatar'] == "" && !empty($newavatar['name']) && is_uploaded_file($newavatar['tmp_name'])) {
            $avatarext = strrchr($newavatar['name'],".");
            $avatarname = substr($newavatar['name'], 0, strrpos($newavatar['name'], "."));
            if (preg_match("/^[-0-9A-Z_\[\]]+$/i", $avatarname) && preg_match("/(\.gif|\.GIF|\.jpg|\.JPG|\.png|\.PNG)$/", $avatarext) && $newavatar['size'] <= 30720) {
                $avatarname = $avatarname."[".$userdata['user_id']."]".$avatarext;
                $set_avatar = "user_avatar='$avatarname', ";
                move_uploaded_file($newavatar['tmp_name'], IMAGES."avatars/".$avatarname);
                chmod(IMAGES."avatars/".$avatarname,0644);
                if ($size = @getimagesize(IMAGES."avatars/".$avatarname)) {
                    if ($size['0'] > 100 || $size['1'] > 100) {
                        unlink(IMAGES."avatars/".$avatarname);

    ........
    判断的伪代码:
    <?
    $newavatar['name']= $_GET[a];
    $avatarext = strrchr($newavatar['name'],".");
    $avatarname = substr($newavatar['name'], 0, strrpos($newavatar['name'], "."));
    print $avatarext."<br>";
    print $avatarname."<br>";
    print preg_match("/^[-0-9A-Z_\[\]]+$/i", $avatarname)."<br>"; //提取后缀的部分不可以有. [不可以提交1.php.gif这样的类型]
    print preg_match("/(\.gif|\.GIF|\.jpg|\.JPG|\.png|\.PNG)$/", $avatarext)."<br>";

    没戏了~~~

    一些sy的思考:
    如果preg_match("/^[-0-9A-Z_\[\]]+$/i", $avatarname)没有过滤.的话 是不是还有机会呢? code:
    <?
    $newavatar['name']= $_GET[a];
    $avatarext = strrchr($newavatar['name'],".");
    $avatarname = substr($newavatar['name'], 0, strrpos($newavatar['name'], "."));
    print $avatarext."<br>";
    print $avatarname."<br>";
    print preg_match(""/^[-0-9A-Z_\.\[\]]+$/i"", $avatarname)."<br>"; //我们使用v6.00.305的正则.
    print preg_match("/(\.gif|\.GIF|\.jpg|\.JPG|\.png|\.PNG)$/", $avatarext)."<br>";

    我们提交?a=1.php.php.gifa时
    preg_match(""/^[-0-9A-Z_\.\[\]]+$/i"", $avatarname) ===>1
    preg_match("/(\.gif|\.GIF|\.jpg|\.JPG|\.png|\.PNG)$/", $avatarext) ===>0
    失败了,不过如果你看过se大牛的blog [4] ,preg_match("/(\.gif|\.GIF|\.jpg|\.JPG|\.png|\.PNG)$/", $avatarext)这个还是可以过的:
    提交?a=1.php.php.gif%0a就可以绕过了,但是在
    move_uploaded_file($newavatar['tmp_name'], IMAGES."avatars/".$avatarname);
    win下是文件名1.php.php.gif\x0a 是不合法的,但是在*nix下是可以的 :).



    参考:
    [1]<系统特性与web安全>:http://www.4ngel.net/article/63.htm
    [2]<Bypass getimagesize()>:http://my.opera.com/pstgroup/blog/tips-bypass-getimagesize
    [3]http://www.milw0rm.com/exploits/1760
    [4]http://blog.php-security.org/archives/76-Holes-in-most-preg_match-filters.html

    收藏到:Del.icio.us