0%

[HFCTF2020] BabyUpload

[HFCTF2020]BabyUpload

学习了PHP的session机制,感觉还学到了点东西,懂了session是怎么存的这个题就很简单了

源码

好长一串

<?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的条件是我们的身份是admin且存在一个success.txt
由于文件名不可控,我们就无法上传一个名为success.txt的文件,但是file_exists函数的作用的是检查文件或目录是否存在,所以我们用attr创建一个名为success.txt的目录就可以过这个判断

php session

session文件名以 sess_ 为前缀,后跟 SESSION ID(即cookie中PHPSESSID的值),存放的数据为序列化的session信息。
我们发起访问的时候就通过这个cookie的值去查询对应的session文件,这里就还可以牵扯出PHP的session反序列化导致的各种漏洞,不在这里赘述了
所以我们可以先用download功能下载一下自己的session文件看一看,下载出来的内容为\x08usernames:5:"guest";,\x08为不可见字符,要注意
我们把guest改成admin,文件名就叫做sess,上传上去,这样子服务端就获得了一个名为sess_(file_hash(sess))的文件,我们自己计算一下文件的哈希值,得到文件叫sess_f567e441819b3b6408c99607cd47e41b4c3167e9a46f82cb5c14b843ad5a25d0,然后将cookie替换为该哈希值进行访问,这样子服务端就将身份替换为了admin,并且也已经创建好了success.txt这个目录,可以通过检测,获取flag了