前置知识

gzcompress() — php的字符串压缩(压缩能力差不多10倍)
gzuncompress() — php的字符串解压缩
mysqli_fetch_array() 函数从结果集中取得一行作为关联数组,或数字数组,或二者兼有。
__wakeup()—执行unserialize()时,先会调用这个函数
unserialize()—反序列化

漏洞测试代码

  1. <?php
  2. // include 'common.php';
  3. $flag = 'xxxxxxxxxxxxxxx';
  4. $req = array_merge($_GET, $_POST);
  5. $con = mysqli_connect("localhost:23306","root","root","test");
  6. if(!$con){
  7. die('Could not connect: ' . mysqli_error());
  8. }
  9. //把一个或多个数组合并为一个数组
  10. class db
  11. {
  12. public $where;
  13. function __wakeup()
  14. {
  15. if(!empty($this->where))
  16. {
  17. $this->select($this->where);
  18. }
  19. }
  20. function select($con,$where)
  21. {
  22. $sql = mysqli_query($con,'select * from user where '.$where);
  23. //函数执行一条 MySQL 查询。
  24. return @mysqli_fetch_array($sql);
  25. //从结果集中取得一行作为关联数组,或数字数组,或二者兼有返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false
  26. }
  27. }
  28. if(isset($req['token']))
  29. //测试变量是否已经配置。若变量已存在则返回 true 值。其它情形返回 false 值。
  30. {
  31. $login = unserialize(gzuncompress(base64_decode($req['token'])));
  32. //gzuncompress:进行字符串压缩
  33. //unserialize: 将已序列化的字符串还原回 PHP 的值
  34. $db = new db();
  35. $row = $db->select($con,'user="'.mysqli_real_escape_string($con,$login['user']).'"');
  36. var_dump($row);
  37. //mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。
  38. if($login['user'] === 'xiaowu')
  39. {
  40. echo $flag;
  41. }else if($row['pass'] !== $login['pass']){
  42. echo 'unserialize injection!!';
  43. }else{
  44. echo "(╯‵□′)╯︵┴─┴ ";
  45. }
  46. }else{
  47. print('sfdf');
  48. // header('Location: index.php?error=1');
  49. }
  50. ?>

漏洞复现

  1. 发生以下任意请求,均可绕过限制拿到flag
    1. http://127.0.0.1/php/03-jiami.php?token=eJxLtDK0qi62MrFSKi1OLVKyLrYys1KqyEzMLy9Vsq4FAI7oCZE=
    2022-03-28_215314.png

    漏洞分析

    想要拿到flag需要满足两个条件,第一个条件isset($req[‘token’]),第二个条件$login[‘user’] === ‘xiaowu’。第一个条件加单,只要get或者post一个token参数就可以满足,第二个条件中的$login来自这里:
    $login =unserialize(gzuncompress(base64_decode($req[‘token’])))。这个是先对token参数内容进行base64解码,再gzuncompress解压缩,最后再反序列化得到的。这样的话我们可以反过来进行操作得到token值。因为我们最终需要得到$login为array(“user” => “xiaowu”),这样才能满足条件拿到flag,所以我们可以对array(“user” => “xiaowu”)先进行serialize(),再gzcompress(),再base64(),反向推导出token值。代码如下: ``` <?php

$arr = array(“user” => “xiaowu”); $token = base64_encode(gzcompress(serialize($arr))); print_r($token);

?> ``` 2022-03-28_220909.png
拿到的token值发送过去就可以拿到flag。
2022-03-28_215314.png