前言
关键字:[file_exists|postman|文件上传|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为admin就能拿到flag。
先测试了下上传,没有反馈,奇怪。
再试试下载
php的session默认存储文件名是sess_+PHPSESSID的值
session保存路径和上传路径一致,所以可以直接上传自己构造的session。
direction=download&filename=sess_d7bd37299f06fbba3ab39c8a8008aad2
根据之前做的一道题里的关于php系列化引擎知识点刷题笔记:bestphp’s revenge
php_binary引擎:键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值
可知这用的是php_binary引擎
<?php
ini_set('session.serialize_handler', 'php_binary');
session_save_path("D:\\phpstudy_pro\\WWW\\localhost");
session_start();
$_SESSION['username'] = 'admin';
生成后,把文件名改为sess,用hash_file()
函数计算下
432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4
这样,按照这两行代码,上传的文件名就是sess_432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4
上传需要BODY参数,所以拿postman整吧
再试试有没有上传成功。
direction=download&filename=sess_432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4
然后还需要验证success.txt
先查查file_exists()
有没有漏洞,百度到可以/file/**/../checkfile这样利用,可惜在这用不上。
file_exists()函数检查文件或目录是否存在。
该函数是检查文件或目录,所以利用attr上传个success.txt文件夹即可。
上传后把cookie里的PHPSESSID改为432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4
即可拿到flag。