V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NGINX
NGINX Trac
3rd Party Modules
Security Advisories
CHANGES
OpenResty
ngx_lua
Tengine
在线学习资源
NGINX 开发从入门到精通
NGINX Modules
ngx_echo
tntsec
V2EX  ›  NGINX

吹毛求疵还是精益求精?再谈 nginx fastcgi/fpm 正则禁止上传目录 php 执行

  •  
  •   tntsec · 2016-07-29 11:55:57 +08:00 · 6237 次点击
    这是一个创建于 2799 天前的主题,其中的信息可能已经有所发展或是发生改变。

    注意,本文讨论的仅仅是如何绕过禁止规则进入 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)的区别

    28 条回复    2016-07-30 10:28:02 +08:00
    falcon05
        1
    falcon05  
       2016-07-29 13:40:14 +08:00
    第 3 条的 规则并不会 “封杀目录中包含 php 三个字母的文件如 /php/3038714494.png ” ,

    正则解释 ^/upload/.*.(php|php5)($|/) 开始匹配^ 匹配任意多字符.* 匹配任意字符请输入代码.(怀疑此处其实他想匹配点,但是正则中的点代表任意字符) 结束或者目录符号($|/)

    nginx 的 “.” 单独用的话它就是代表 “.” ,不用转义。
    strwei
        2
    strwei  
       2016-07-29 13:55:34 +08:00 via iPhone
    建议还是用 apache 跑 php
    lhbc
        3
    lhbc  
       2016-07-29 14:58:17 +08:00 via Android   ❤️ 1
    正确的做法不是 try_files 吗?
    tntsec
        4
    tntsec  
    OP
       2016-07-29 17:38:19 +08:00
    @falcon05 测试会封杀,再次测试依然被封杀
    不转移, nginx 仅仅识别一个目录下而不能识别目录的目录下
    /php/3038714494.png 404
    /1/php/3038714494.png 403
    tntsec
        5
    tntsec  
    OP
       2016-07-29 17:38:51 +08:00
    @lhbc 啥?
    ryd994
        6
    ryd994  
       2016-07-29 18:37:25 +08:00
    问题是……对于 nginx 来说,只要没有 fastcgi
    ryd994
        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 检测

    这些都是性能很好而且简单清晰的解法
    ryd994
        8
    ryd994  
       2016-07-29 18:44:48 +08:00
    @Livid Duplicate with: /t/295225
    ysc3839
        9
    ysc3839  
       2016-07-29 18:49:27 +08:00 via Android
    我之前配置过一个,是把全部都重写到 index.php 的,也不知道怎么配置的,上传目录里面 php 那些全都不能执行,访问的话就变文件下载
    tntsec
        10
    tntsec  
    OP
       2016-07-29 19:06:29 +08:00
    @ryd994 你这跟第四种方法有啥不一样的吗?
    ryd994
        11
    ryd994  
       2016-07-29 20:31:54 +08:00
    @tntsec 天……那我是不是可以说我把 /etc/passwd 拷贝到任意文件夹下会泄漏?
    是不是还要考虑 config.php 复制后被改名?
    你知道 regex 很低效么?
    falcon05
        12
    falcon05  
       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 文件执行。
    falcon05
        13
    falcon05  
       2016-07-29 20:57:28 +08:00
    z5864703
        14
    z5864703  
       2016-07-30 00:30:09 +08:00
    上传文件不放公开目录,必须通过程序转发直接输出下载。
    不过好像楼主说的是通过 nginx 正则...文不对题了...
    tntsec
        15
    tntsec  
    OP
       2016-07-30 09:34:25 +08:00
    @ryd994 /etc/passwd 受 chroot 限制,不会越出网站目录,顶多访问 tmp 和网站根目录。当然 chroot 是自己配的
    需要考虑,但是在目前只能称他为瑕疵,因为改后缀名就变得不好考虑了
    很低是有多低,很高效又是多高效
    tntsec
        16
    tntsec  
    OP
       2016-07-30 09:40:42 +08:00
    @falcon05 禁止上传目录 php 执行,不是禁止上传
    tntsec
        17
    tntsec  
    OP
       2016-07-30 09:41:24 +08:00
    @z5864703 禁止上传目录 php 执行 请连起来看
    kiwi95
        18
    kiwi95  
       2016-07-30 09:47:11 +08:00
    后面的正则确实太过复杂,效率太低,其实防止上传的文件保存为 .php 后缀到 upload 文件夹就没事了,将上传的文件 hash 一下不带后缀或者后缀检查

    nginx 配置讲究简洁高效,程序和 nginx 配合做到高效安全就好了
    aprikyblue
        19
    aprikyblue  
       2016-07-30 09:52:12 +08:00 via Android
    这是在讨论正则吗。。
    jarlyyn
        20
    jarlyyn  
       2016-07-30 09:52:44 +08:00 via Android
    理论上述说,还是代码和其他文件分开比较好
    tntsec
        21
    tntsec  
    OP
       2016-07-30 09:53:02 +08:00
    @kiwi95 并不是为了防止.php 后缀,而是为了防止图片马用.php 前缀绕过
    tntsec
        22
    tntsec  
    OP
       2016-07-30 09:53:52 +08:00
    @aprikyblue 那里不是正则?
    kiwi95
        23
    kiwi95  
       2016-07-30 09:57:35 +08:00
    我觉得第一条就是最直观最好的方法,后面确实是吹毛求疵,不知道文章种说的第一条绕过方法有什么问题,且不说一般不会用 .php 作为文件夹名,就算用 .php 做文件夹名也不会有什么问题
    tntsec
        24
    tntsec  
    OP
       2016-07-30 09:58:02 +08:00
    @jarlyyn 现在的一些基于 tp 的网站,连根目录都必须有写入权限,已经不明白他怎么想的了
    tntsec
        25
    tntsec  
    OP
       2016-07-30 10:01:40 +08:00
    @kiwi95 http://www.2cto.com/Article/201502/378373.html
    第一种方法是典型的解析漏洞
    jarlyyn
        26
    jarlyyn  
       2016-07-30 10:03:02 +08:00 via Android
    @tntsec

    所以我一直觉得 PHP 程序的部署是很难的
    kiwi95
        27
    kiwi95  
       2016-07-30 10:18:32 +08:00
    @tntsec try_files 加 =404 就好了,并且现在 cgi.fix_pathinfo 配置都是(推荐)关掉的,并且现在的 php-fpm 的默认配置应该也能避免它;总之这种漏洞很容易用其他方法避免
    tntsec
        28
    tntsec  
    OP
       2016-07-30 10:28:02 +08:00
    @kiwi95 那就请看文章第一句话
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3257 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 11:52 · PVG 19:52 · LAX 04:52 · JFK 07:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.