
注意,本文讨论的仅仅是如何绕过禁止规则进入 cgi 。
nginx fastcgi/fpm 禁止上传目录 php 执行 因为上传目录是具备写入权限的,写入权限很恐怖。
1.最常见的,完全错误的方法 来源,百度下最常见的
location ~ ^/upload/.*\.(php|php5)$ { deny all; } 正则解释 ^/upload/.*.(php|php5)$ 开头匹配^ 中间任意匹配.* 点匹配\. (php|php5)php扩展名选择器 结尾匹配$
绕过方法, 1.php 可以是个目录 /1.php/3038714494.png
2.Nginx 下多站点正确限制目录 php 执行权限 来源 http://www.freebuf.com/articles/system/49428.html
location ~ /(ups)/.*\.(php|php5)?$ { deny all; } 正则解释 比上面的多了一个?问号,问号代表零次或一次。
绕过方式,同上 /1.php/3038714494.png
3.nginx+cgi 解析 php 容易出现的漏洞 来源 http://www.nginx.cn/316.html
location ~* ^/upload/.*.(php|php5)($|/) { deny all; } 正则解释 ^/upload/.*.(php|php5)($|/) 开始匹配^ 匹配任意多字符.* 匹配任意字符请输入代码.(怀疑此处其实他想匹配点,但是正则中的点代表任意字符) 结束或者目录符号($|/)
此方式解决了上面的绕过问题
缺点,封杀太严格,目录中包含 php 三个字母就会封杀 /php/3038714494.png
4.关于 lnmp 目录禁止执行的绕过与正确方法 来源地址 http://zone.wooyun.org/content/16213 谷歌缓存 http://webcache.googleusercontent.com/search?q=cache:g3B4qj-SGXgJ:zone.wooyun.org/content/16213+&cd=1&hl=zh-CN&ct=clnk&gl=cn
location ^~ /upload/ { default_type text/plain; expires 30d; } 解释,这不是正则,却更加有趣 ^~表示匹配一次,不再让其他 location 处理,从而不会匹配到 cgi 中 这种方式需要的判断最少,是非常好的方式
他只有一点点瑕疵,那就是源码泄漏 如果 config.php 文件被复制到这个目录下,源码一览无余。因为这个方法只是不让 php 进入 cgi ,但是却没有禁止他的解析。
5.我的方法 当你看完上面的内容,相信你已经有了自己的方法了 我这里稍微修改下第三种方法,就是括号里的部分
location ~ (/usr/uploads/|/admin/).*\.(php|php5)($|/) { deny all; } 增加的部分 location ~ (/usr/uploads/|/admin/).*\.(php|php5)($|/) 将从匹配任意字符调整为匹配点,这样就不会拦截包含 php 字符的目录了,但是依然会拦截 如果出现.php目录,我们可以默认认为他是黑客行为。
小提示,注意(/usr/uploads/|/admin/)和(/usr/uploads|/admin)的区别
1 GG668v26Fd55CP5W 2016-07-29 13:40:14 +08:00 第 3 条的 规则并不会 “封杀目录中包含 php 三个字母的文件如 /php/3038714494.png ” , 正则解释 ^/upload/.*.(php|php5)($|/) 开始匹配^ 匹配任意多字符.* 匹配任意字符请输入代码.(怀疑此处其实他想匹配点,但是正则中的点代表任意字符) 结束或者目录符号($|/) nginx 的 “.” 单独用的话它就是代表 “.” ,不用转义。 |
2 strwei 2016-07-29 13:55:34 +08:00 via iPhone 建议还是用 apache 跑 php |
3 lhbc 2016-07-29 14:58:17 +08:00 via Android 正确的做法不是 try_files 吗? |
4 tntsec OP @falcon05 测试会封杀,再次测试依然被封杀 不转移, nginx 仅仅识别一个目录下而不能识别目录的目录下 /php/3038714494.png 404 /1/php/3038714494.png 403 |
6 ryd994 2016-07-29 18:37:25 +08:00 问题是……对于 nginx 来说,只要没有 fastcgi |
7 ryd994 2016-07-29 18:43:26 +08:00 问题是……对于 nginx 来说,只要没有 fastcgi_pass 就不会交给 php 来处理 于是你为什么要给上传目录加 fastcgi_pass 呢? 只需要用^~ 防止上传路径被匹配到 php 的 block 里就可以了 同时,对于 php 的 block ,用 try_files 检查文件是否存在 或者用 fastcgi_split_path_info 代替 php 自己的 path_info 检测 这些都是性能很好而且简单清晰的解法 |
9 ysc3839 2016-07-29 18:49:27 +08:00 via Android 我之前配置过一个,是把全部都重写到 index.php 的,也不知道怎么配置的,上传目录里面 php 那些全都不能执行,访问的话就变文件下载 |
11 ryd994 2016-07-29 20:31:54 +08:00 |
12 GG668v26Fd55CP5W 2016-07-29 20:51:47 +08:00 @tntsec 我不知道你怎么测试的,还会有 404 的? 然后我写了一个,打开浏览器: <https://www.cellmean.com/upload/hello.php> 403 <https://www.cellmean.com/upload/php/a.jpg> 200 事实上我觉得用第一条就可以了,后面的几条包括你给的反而画蛇添足,把 php 的目录也排过滤掉了,比如网站头像图片存在 /usr/uploads/用户名目录下,当注册用户恰好叫 php ,如果用你那一条,那这个用户的头像 /usr/uploads/php/avatar.jpg 就无法显示了。 你举的反例我觉得很奇怪。 “绕过方法, 1.php 可以是个目录 /1.php/3038714494.png ” 绕过了什么? nginx 在这里并不是用来阻止上传文件上传到 /1.php 这个目录的,你要证明的是这个 png 会被当成 php 文件执行。 |
13 GG668v26Fd55CP5W 2016-07-29 20:57:28 +08:00 |
14 z5864703 2016-07-30 00:30:09 +08:00 上传文件不放公开目录,必须通过程序转发直接输出下载。 不过好像楼主说的是通过 nginx 正则...文不对题了... |
15 tntsec OP @ryd994 /etc/passwd 受 chroot 限制,不会越出网站目录,顶多访问 tmp 和网站根目录。当然 chroot 是自己配的 需要考虑,但是在目前只能称他为瑕疵,因为改后缀名就变得不好考虑了 很低是有多低,很高效又是多高效 |
18 kiwi95 2016-07-30 09:47:11 +08:00 后面的正则确实太过复杂,效率太低,其实防止上传的文件保存为 .php 后缀到 upload 文件夹就没事了,将上传的文件 hash 一下不带后缀或者后缀检查 nginx 配置讲究简洁高效,程序和 nginx 配合做到高效安全就好了 |
19 aprikyblue 2016-07-30 09:52:12 +08:00 via Android 这是在讨论正则吗。。 |
20 jarlyyn 2016-07-30 09:52:44 +08:00 via Android 理论上述说,还是代码和其他文件分开比较好 |
22 tntsec OP @aprikyblue 那里不是正则? |
23 kiwi95 2016-07-30 09:57:35 +08:00 我觉得第一条就是最直观最好的方法,后面确实是吹毛求疵,不知道文章种说的第一条绕过方法有什么问题,且不说一般不会用 .php 作为文件夹名,就算用 .php 做文件夹名也不会有什么问题 |
25 tntsec OP |