[HFCTF2020]BabyUpload
直接给了源码
<?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;
}
?>
获取flag的思路很明确了
1.通过file_exists('/var/babyctf/success.txt')校验
2.admin身份
这里提供了上传和下载功能并且目录和session存放目录相同,因此我们可以自己上次sess文件伪造sess。upload方法中对上传的文件名进行了一些修改,在后面加上_+sha256值,刚好session文件名格式就是sess_+phpsessid。首先我们先利用下载功能查看服务器储存session的格式,提交参数direction=download&attr=.&filename=sess_+phpsessid,可以看到现有的session文件内容为usernames:5:"guest";,在burp中我们可以看到最前面是一个0x08字符,因此得知服务器生成session文件的处理器为php_binary,因此在本地生成一个session文件。
<?php
session_save_path("C:/xampp/ctf/var/babyctf/");
ini_set('session.serialize_handler', 'php_binary');
session_start();
$_SESSION['username'] ='admin';
?>
得到一个session文件,并且身份为admin,我们修改这个文件名为sess,然后计算其sha256值。上传这个文件即可伪造session,根据题目源码,服务器在储存文件时在文件名后加上了_+sha256,因此我们上次的sess文件在服务器端名为sess_432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4。然后我们用432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4作为phpsessid,即可实现伪造admin的身份。这里上传时还有一个attr参数,我们可以设置这个参数为.或者不设。
admin身份伪造成功后再尝试上次success.txt文件。这里使用的函数为file_exists()函数,这个函数可以判断一个文件或目录是否存在。因此我们可以令attr=success.txt,这样可以创造一个名为success.txt的目录。然后就可以直接get访问获取flag。
如果这里php版本小于5.3.4,还可以上传一个名为success.txt%00的文件。这样可以截断后面添加的部分,也能够通过file_exists()函数校验。