黑帽联盟

 找回密码
 会员注册
查看: 1402|回复: 0
打印 上一主题 下一主题

[系统安全] ngx_lua_waf防火墙针对性改写完善

[复制链接]

895

主题

38

听众

3329

积分

管理员

Rank: 9Rank: 9Rank: 9

  • TA的每日心情
    难过
    昨天 22:31
  • 签到天数: 1652 天

    [LV.Master]伴坛终老

    当初选择ngx_lua_waf作为自己的WAF,主要原因就是因为其可扩展性与性能上有一个很好的平衡。

    lua语言的灵活性与效率是很多脚本层WAF无可匹及的。

    ngx_lua_waf自身是比较简单的,而且存在很多误报、漏报、绕过的现象,我整理如下,来改进自己的waf。

    1.debug函数
    预备一个debug函数,方便以后调试。因为waf运行在后台,所以看不到输出,最好以日志的形式写到文件中。
    1. function debug(info)
    2.   local file = io.open("/tmp/debug.log","a")
    3.   file:write(info.."\n")
    4.   file:close()
    5. end
    复制代码
    2.waf可以用hpp进行绕过
    后面会发出来的,待定

    3.利用白名单绕过
    wafconf/whiteurl中,白名单URL直接是/123/
    然后在函数whiteurl中
    1. function whiteurl()
    2.     if WhiteCheck then
    3.         if wturlrules ~=nil then
    4.             for _,rule in pairs(wturlrules) do
    5.                 if ngxmatch(ngx.var.request_uri,rule,"ijom") then
    6.                     return true
    7.                  end
    8.             end
    9.         end
    10.     end
    11.     return false
    12. end
    复制代码
    用的是ngx.var.request_uri和这个"/123/"进行比较,只要uri中存在/123/就作为白名单不进行检测,这样我们可以通过/waf.php?a=/123/&b=../etc/passwd 绕过防御规则。

    所以,将/123/改成^/123/
    这样只有以/123/开头的uri才能进入白名单。

    4.正则是m还是s
    WAF绕的多的人一定知道正则里“.”代表什么意义。
    正常情况下,.匹配的是“不含换行”的所有字符。所以有些WAF用这样的正则:
    1. union.*select
    复制代码
    来拦截注入。我们就可以通过union%0aselect,中间一个换行来绕过。
    所以,现在一般的WAF都会用s来修饰正则。s的意思就是single,也就是单行模式。
    说白了,加了s修饰,则“.”就会匹配换行了。
    而我们的ngx_lua_waf中,所有的正则都用的m来修饰的,m的意思是multiple,多行的意思,也就是默认的.不匹配换行。~~ (注:这样理解是错的,详见评论。)
    而我们的ngx_lua_waf中,并没有使用i修饰正则,所以默认.是匹配多行的,也就是默认的.不匹配换行。
    比如对GET变量的拦截:
    1. function args()
    2.     for _,rule in pairs(argsrules) do
    3.         local args = ngx.req.get_uri_args()
    4.         for key, val in pairs(args) do
    5.             if type(val)=='table' then
    6.                 if val == false then
    7.                     data=table.concat(val, " ")
    8.                 end
    9.             else
    10.                 data=val
    11.             end
    12.             if data and type(data) ~= "boolean" and rule ~="" and ngxmatch(unescape(data),rule,"imjo") then
    13.                 log('GET',ngx.var.request_uri,"-",rule)
    14.                 say_html()
    15.                 return true
    16.             end
    17.         end
    18.     end
    19.     return false
    20. end
    复制代码
    可见ngxmatch(unescape(data),rule,"imjo"),用的是imjo来修饰。我们用union%0aselect就能绕过WAF:
    2.png
    5.误杀误杀!上传文件的误杀。
    对HTTP协议了解的同学一定心里清楚,POST的类型是分两种的:application/x-www-form-urlencoded和multipart/form-data
    前一种是默认POST数据的时候使用的,服务器获取了数据后会进行url解码。后一种一般是上传的时候才会使用,服务器获取数据后不会进行url解码,所以我们能直接上传二进制文件。
    php在上传过程中,上传文件的表单会放进$_FILES变量,其他POST表单会放进$_POST变量,和直接application/x-www-form-urlencoded的效果一样。
    这部分POST变量在lua中需要特殊处理,原ngx_lua_waf的作者也考虑了,具体拦截代码可见waf.lua。
    但作者处理的太草率,直接把整个数据包,一点一点丢进body函数里检测。这样造成了两个问题:
    • 数据包一部分一部分发过来,他就一部分一部分丢进body里检测。那么如果union、select两个连在一起的关键词正好从中间某位置分开,比如"unio"和"n select",这两个包分别检测都是正常的。但实际发送到php里的时候是连在一起的,导致绕过WAF。
    • 文件里的特殊字符也被拦截了,所谓的误杀。有时候我们要上传一些文件,文件里可能会有html标签,或SQL语句,这里他将上传表单的内容也放入body检测了,导致很多文件上传不了。
    我对上述问题做了修改与处理,不过代码太多我就不写在文章里了。思路就是这样:
    首先将完整的数据包获取下来,并用boundary将他们分割成数组。遍历数组,只对进入POST变量的值进行拦截,不拦截FILE内容。但需要拦截FILE表单中的"filename=xxx"的部分。

    6.人性化提示信息
    虽然我的WAF拦截的80%是攻击者,但也可能有正常访客。这时候我就需要告诉访客,你输入了哪些东西不合理被我拦截(误杀)了,你可以换个方式输入或通知我。
    我在init.lua靠前的位置加入如下代码:
    1. local fd = io.open(file403,"r")
    2. if fd == nil then
    3.     html = [[403 error!!]]
    4. else
    5.     html = fd:read("*a")
    6.     fd:close()
    7. end
    复制代码
    file403是我自己写的403页面,读取之。并将say_html函数改成这个:
    1. function say_html(reason)
    2.     if Redirect then
    3.         local nowhtml = html
    4.         ngx.header.content_type = "text/html"
    5.         nowhtml = string.gsub(nowhtml, "{ip}", ngx.var.remote_addr)
    6.         nowhtml = string.gsub(nowhtml, "{host}", ngx.var.host)
    7.         nowhtml = string.gsub(nowhtml, "{reason}", reason)
    8.         ngx.say(nowhtml)
    9.         ngx.exit(200)
    10.     end
    11. end
    复制代码
    将html里的{ip}、{host}、{reason}改成具体的信息。即可在用户被拦截后发出提示:
    1.png
    如果需要优化SEO,我将ngx.exit(200)改成403,避免搜索引擎收录这个页面。但后来发现status code并没有改变。
    研究了一会,发现如果在ngx.exit之前输出了内容,则这个exit里的参数403就会失效。需要在exit前,先用ngx.status = ngx.HTTP_FORBIDDEN,将status设置成ngx.HTTP_FORBIDDEN,也就是403才可。

    7.利用lua_ngx_waf防御盗链
    以前防盗链都是用nginx自己的模块进行配置,但有时候灵活性不高。
    有了lua waf,就可以灵活地防御盗链了。
    我大概写了个简陋的雏形,需要更精细化的配置,就得各位日后再慢慢修改了。
    1. function check_referer()
    2.     local referer = ngx.var.valid_referers
    3.     local ua = string.lower(ngx.var.http_user_agent)
    4.     local exts = [[\.(gif|jpg|jpeg|png|bmp|js|css|swf)$]]
    5.     local http = "http"
    6.     if ngx.var.https == "on" then http = "https" end
    7.     local white_referer = {[0] = [[^]]..http..[[://[^/]*]]..ngx.var.host..[[[^/]*/.*]], [1] = [[^https?://[^/]*google\.com[^/]*/.*]], [2] = [[^https?://[^/]*baidu\.com[^/]*/.*]]}
    8.     local white_ua = {[0] = "googlebot", [1] = "spider"}
    9.     if referer ~= nil and ngxmatch(ngx.var.request_filename, exts,"ijos") then
    10.         for rex in white_referer do
    11.             if ngxmatch(referer, rex, "ijos") then return true end
    12.         end
    13.         for rex in white_ua do
    14.             if ngxmatch(ua, rex, "ijos") then return true end
    15.         end
    16.         ngx.exit(403)
    17.     end
    18. end
    复制代码
    8.尾声
    通过这几日对ngx_lua_waf的研究,WAF这块的攻击与防御,我也初步接触到了。我也知道有时候我们研究者说绕过WAF,似乎总在指责WAF的开发者,某某没考虑到,某某可以绕过了。实际上做WAF也不容易,往往是因为要考虑到业务效率、兼容性等各种原因,写出来的代码才被绕过去。

    安全有时候不得不为业务让道,有时候明知这么写是不安全的,但某些用户就需要这样的数据包,我们不能抛弃这部分用户,那么只能尽全力改变这些用户的习惯,写出兼容性更好的代码。

    我希望的是,通过自己的研究,让更多人知道WAF都是怎么做出来的,会遇到哪些问题,有哪些绕过方法。

    攻防,也不过就是那句老话:知己知彼,百战不殆。

    帖子永久地址: 

    黑帽联盟 - 论坛版权1、本主题所有言论和图片纯属会员个人意见,与本论坛立场无关
    2、本站所有主题由该帖子作者发表,该帖子作者与黑帽联盟享有帖子相关版权
    3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和黑帽联盟的同意
    4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
    5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
    6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
    7、黑帽联盟管理员和版主有权不事先通知发贴者而删除本文

    勿忘初心,方得始终!
    您需要登录后才可以回帖 登录 | 会员注册

    发布主题 !fastreply! 收藏帖子 返回列表 搜索
    回顶部