你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

nginx lua针对请求编码绕过进行解码防护

2021/12/1 9:37:33

常见解码

某些商业waf解码类型,通常支持解码类型如下:

URL解码

JSON解析

base64解码

十六进制hex解码

斜杠转义

xml解析

php序列化对象解析

UTF7解码

本文解码格式

base64_decode 
css_decode
hex_decode 
cmd_line 
compress_whitespace 
html_decode 
js_decode 
lowercase 
normalise_path 
normalise_path_win 
remove_comments 
remove_comments_char 
remove_nulls 
remove_whitespace 
replace_comments 
replace_nulls 
sql_hex_decode 
uri_decode
trim  
trim_left 
trim_right 

绕过waf的一些帖子

大小写绕过

简单编码绕过

注释绕过

分隔重写绕过

例如检测union,%55为U的16进制编码替代U,union写成%55nION,结合大小写可以绕过一些WAF。

解码代码

解码部分,nginx lua可以实现一部分。

另一部分需要调用c动态库实现,我们可以编写多种解码so提供给nginx lua调用。

local _M = {}

local base   = require "owasp.base"
local hdec   = require "resty.htmlentities"
local ffi    = require "ffi"
local logger = require "owasp.log"
local util   = require "owasp.util"
local Util   = require "util"
require "resty.core.regex"
require "resty.core.hash"
require "resty.core.uri"

local ffi_cpy    = ffi.copy
local ffi_new    = ffi.new
local ffi_str    = ffi.string
local c_buf_type = ffi.typeof("char[?]")



ffi.cdef[[
int js_decode(unsigned char *input, long int input_len);
int css_decode(unsigned char *input, long int input_len);
]]

_M.version = base.version

hdec.new() 

local loadlib = function()
    local so_name = 'libdecode.so'
    local cpath = package.cpath

    for k, v in string_gmatch(cpath, "[^;]+") do
        local so_path = string_match(k, "(.*/)")
        if so_path then
            so_path = so_path .. so_name

            local f = io.open(so_path)
            if f ~= nil then
                io.close(f)
                return ffi.load(so_path)
            end
        end
    end
end
local decode_lib = loadlib()

local function decode_buf_helper(value, len)
    local buf = ffi_new(c_buf_type, len)
    ffi_cpy(buf, value)
    return buf
end

_M.lookup = {
    base64_decode = function(value)
        if not value then return end
        --判断是否是base64编码
        --if ngx_re_match(value,"^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$") ~= nil then
            if type(value) == "string" then 
                local t_val = ngx_decode_base64(value)
                if t_val then
                    return t_val
                else
                    return value
                end
            end
            return value
    end,
    css_decode = function(value)
        if not value then return end

            if not value then return end

            local len = #value
            local buf = decode_buf_helper(value, len)

            local n = decode_lib.css_decode(buf, len)

            return (ffi_str(buf, n))
    end,
    cmd_line = function(value)
        if not value then return end
            local str = tostring(value)
            str = ngx_re_gsub(str, [=[[\\'"^]]=], '',  'oij')
            str = ngx_re_gsub(str, [=[\s+/]=],    '/', 'oij')
            str = ngx_re_gsub(str, [=[\s+[(]]=],  '(', 'oij')
            str = ngx_re_gsub(str, [=[[,;]]=],    ' ', 'oij')
            str = ngx_re_gsub(str, [=[\s+]=],     ' ', 'oij')
            return string_lower(str)
    end,
    compress_whitespace = function(value)
        if not value then return end
            return ngx_re_gsub(value, [=[\s+]=], ' ', 'oij')
    end,
    hex_decode = function(value)
        if not value then return end
            return util.hex_decode(value)
    end,
    html_decode = function(value)
        if not value then return end
            local str = hdec.decode(value)
            return str
    end,
    js_decode = function(value)
        if not value then return end

        local len = #value
        local buf = decode_buf_helper(value, len)

        local n = decode_lib.js_decode(buf, len)

        return (ffi_str(buf, n))
    end,
    lowercase = function(value)
        if not value then return end
        return string_lower(tostring(value))
    end,
    normalise_path = function(value)
        if not value then return end
        while (ngx.re.match(value, [=[[^/][^/]*/\.\./|/\./|/{2,}]=], 'oij')) do
            value = ngx_re_gsub(value, [=[[^/][^/]*/\.\./|/\./|/{2,}]=], '/', 'oij')
        end
        return value
    end,
    normalise_path_win = function(value)
        if not value then return end
        value = string_gsub(value, [[\]], [[/]])
        return _M.lookup['normalise_path'](value)
    end,
    remove_comments = function(value)
        if not value then return end
        return ngx_re_gsub(value, [=[\/\*(\*(?!\/)|[^\*])*\*\/]=], '', 'oij')
    end,
    remove_comments_char = function(value)
        if not value then return end
        return ngx_re_gsub(value, [=[\/\*|\*\/|--|#]=], '', 'oij')
    end,
    remove_nulls = function(value)
        if not value then return end
        return ngx_re_gsub(value, [[\0]], '', 'oij')
    end,
    remove_whitespace = function(value)
        if not value then return end
        return ngx_re_gsub(value, [=[\s+]=], '', 'oij')
    end,
    replace_comments = function(value)
        if not value then return end
        return ngx_re_gsub(value, [=[\/\*(\*(?!\/)|[^\*])*\*\/]=], ' ', 'oij')
    end,
    replace_nulls = function(value)
        if not value then return end
        return ngx_re_gsub(value, [[\0]], ' ', 'oij')
    end,
    sql_hex_decode = function(value)
        if not value then return end
        if string_find(value, '0x', 1, true) then
            value = string_sub(value, 3)
            return util.hex_decode(value)
        else
            return value
        end
    end,
    trim = function(value)
        if not value then return end
        return ngx_re_gsub(value, [=[^\s*|\s+$]=], '')
    end,
    trim_left = function(value)
        if not value then return end
        return ngx_re_sub(value, [=[^\s+]=], '')
    end,
    trim_right = function(value)
        if not value then return end
        return ngx_re_sub(value, [=[\s+$]=], '')
    end,
    uri_decode = function(value)
        if not value then return end
        return ngx_unescape_uri(value)
    end,
}

return _M