NSS-反序列化

[ZJCTF 2019]NiZhuanSiWei


此时我们进行代码审计;发现三个参数都是通过GET的方式来传递并且可控;接着往下看发现此时要求我们传入一个text文件并且次text的文件内容为welcome to the zjctf;此时我们可以想到php伪协议中的data://text/plain;base64进行文件的写
入,此时构造第一个payload为

1
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=


紧接着往下看看到一个文件包含include($file);并且对$file做了一个正则匹配要求$file中不能含有flag;此时我们再次利用php伪协议中的php://filter来进行对源代码的读取

1
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/read/convert.base64-encode/resource=useless.php



此时可以看到解密出来的内容的考点在与反序列化;然后继续代码审计可知 file_get_content($file)会将$file的文件输出
所以此时我们只需要使得Flag类中的file值为flag.php那么当password反序列化后即可触发_toString中的file_get_content()函数;把我们要的flag输出出来

1
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

因为此时的file如果还是使用伪协议进行读取的话会使得后面的flag无法显示出来;所以此时应该将file直接等于flag.php

[SWPUCTF 2021 新生赛]pop


此时依旧先进行一个代码审计此时当admin === ‘w44m’ && $this->passwd ===’08067’便会输出flag;此时这是一道pop链
的题目;对于pop链的题目我们首先要确定pop链中的每一个节点;因为此时Getflag()可以输出flag,则此时我们可以将它作为pop链的链尾;通过这个链尾向上寻找倒数第二个节点;此时可以看到在w33m类中$this->w00m->{$this->w22m}();;所以此
时我们只需要令w22m=Getflag即可触发Getflag()函数;那么此时的的w33m类就是我们的倒数第二个节点;此时我们看到w33m中有一个_toString(),我们想的就是如何让触发这个函数;因为在w22m类中有echo $this->w00m;所以当echo被触发时便会
接着触发_toString();那么我们接下来想的就是如何触发echo;因为在w22m类中有一个_destruct();当创建的对象被销毁时
便会触发此函数进而触发echo;所以此时整条pop已经很清楚了

1
w22m{_destruct} -> w33m{_toString} -> w44m{Getflag}

exp:

[NISACTF 2022]babyserialize



这次的源码相较于上两题的源码算是比较多的;此时我们依旧还是一步步的进行代码审计;先把pop链的链尾寻找出来;此时我们在NISA类中的_invoke魔术魔方中找到了一个eval执行;这里大概猜出本题需要使用RCE;使得eval执行;所以此时我们不妨
先将这个eval函数作为链尾;接着我们需要找到触发_invoke的魔术魔方;我们知道当一个对象被尝试以函数的形式调用时便会
触发_invoke;所以此时我们在Ilovetxw类中的_toString找到了return $bb();此时当_toString被触发时便会返回$bb();
所以此时我们可以将Ilovetxw中的_toString作为pop链的倒数第二位;接着我们需要找到触发_toString的条件;当一个对象被
当作字符串进行处理时便可以触发_toString;所以此时我们在four类中找到了strtolower()此时这个函数会把对象当作字符串
进行小写处理;所以此时我们可以把four类中的_set当作pop链的倒数第三节点;紧接着我们需要寻找触发_set的方法;当给一
个对象不存在的变量赋值或者在给一个不可访问的属性(protected或者private)赋值时便会触发_set魔术魔方;此时在Ilovetx
w中找到了一个不存在的变量fun;又因为$fun被赋值;所以此时便会触发_set魔术魔法;即Ilovetxw类中的_call会触发_set;
所以此时我们可以将Ilovetxw类中的_call当作pop链的倒数第四节;此时我们再次向上寻找触发_call的条件;当调用一个不可
访问的对象时便会触发_call;此时在TianXiWei类中的_wakeup魔术魔法中发现调用了一个不可访问的对象nisa;此时便会触发
call魔术魔方;而_wakeup在一个对象被创建时便会执行;至此整条pop链已经十分清楚了

1
TianXiWei{_wakeup} -> Ilovetxw{_call} -> four{_set} -> Ilovetxw{_toString} -> NISA{_invoke}

exp1:(注意这里的system被过滤了,可以大写绕过)

exp2:

[第五空间 2021]pklovecloud


此时还是老套路先找我们需要的链尾;此时发现 return file_get_contents($file); 所以此时我们若是满足$file=flag.php则
可以成功获得flag;所以此时我们把ace类中的echo_name属性作为我们的链尾;然而此时我们注意到若是要触发echo_name则需要

1
if($this->openstack->neutron === $this->openstack->nova)

neutron的值和属性都要和nova相等;此时我们再次注意到$this->openstack->neutron = $heat;此时的$heat是一个不存在的属
性;所以此时我们只需要令nova=””或者nova=”NULL”即可成功绕过;此时我们需要做的是成功触发echo_name方法;此时我们继续
注意到在acp类中有一个_toString可以触发echo_name;此时我们又继续在最后的代码中看到了echo $logData;此时可以利用这个
echo来触发这个_toString;所以此时整条pop链已经被我们捋清楚了

1
echo $logData -> acp{_toString} -> ace{echo_name}

此时我们需要注意到$cinder属性是受到保护类型的(protected)所以此时我们无法在其类的外部进行重构和继承;所以此时我们的
exp为:

运行后得

prize_p5


此时这题涉及到了php原生类利用-文件操作;这是一个之前没有碰到过的知识点:php的__toString原生类进行文件操作
一、读取文件名
DirectoryIterator

1
2
3
4
5
<1>版本:
PHP 5, PHP 7, PHP 8

<2>用法:$dir = new DirectoryIterator(dirname(__FILE__));
得到的这个$dir对象,就是__FILE__所在目录下的所有文件和子文件夹的一个综合体,每一个项目对应对象的一个属性

需要注意的是DirectoryIterator这个类中内置了很多有关于文件相关操作的函数这里我们重点关注的是_toString

此时我们可以通过__toString函数来得到文件名(默认是第一个属性的对应的文件名),要想得到所有的文件名,可以用foreach函数遍历对象的所有属性.
Filesystemlterator

1
2
3
<1>版本:
PHP 5 >= 5.3.0, PHP 7, PHP 8
<2>用法:和Directorylterator一样,$dir = new Filesystemlterator(dirname(__FILE__));

这个类的_toString是继承了DirectoryIterator;但是这个类到读取文件名时会以绝对路径来显示出文件名

Globlterator

1
2
<1>版本:PHP 5 >= 5.3.0, PHP 7, PHP 8
<2>用法:$dir=new Globlterator(文件路径);

这个类继续继承Filesystemlterator;也是以绝对路径的方法来显示文件名;并且这个类的遍历类似于glob();所以此时不需要配合glob://协议进行遍历

glob://协议:
glob协议用于匹配文件;此时glob://协议和filter://协议最大的不同就是filter://协议可以用于读取文件前提是知道文件的绝对路径;但是glob://协议是只匹配文件并不打开文件

1
2
glob://*.php就是查看当前目录下所有的php文件
glob:///flag[a-z0-9]*.php,就是匹配所有名为flag.xxx.php的文件(xxx为小写a-z或者数字0-9)


二、读取文件
SplFile0bject

1
2
3
4
<1>版本:
PHP 5 >= 5.1.0, PHP 7, PHP 8
<2>用法:
$context = new SplFileObject('文件路径');

这个类的__toString函数原来是这个类current函数的别名,后来更新成了这个类fgets函数的别名;current函数是返回文件当前所在行的内容,而fgets就是返回文件一行的内容,需要搭配foreach这样的函数来获取不同行的内容.

那么什么时候可以利用原生类来读取文件呢;就比如我们这一题;此时我们发现看完了整段代码;发现并没有像之前一样找到很明显可以被我们所利用的链尾;并且此时又多了好几个过滤;所以此时继续注意观察代码会发现有一个

1
2
3
4
public function __destruct()
{
echo new $this->class($this->data);
}

因为上述代码可以实现任意化类并且配合了echo输出;所以此时我们便可以想到用原生类来读文件;那么此时我们来找一下入口在哪

1
2
3
4
if(isset($_GET['cata'])){
if(!preg_match('/object/i',$_GET['cata'])){
unserialize($_GET['cata']);
}

此时若是在参数$cata中没有匹配到object的内容便将$cata进行反序列化