web服务器在对外提供各种应用服务时,经常会遇到请求的payload经过混淆或者编码想要绕过web安全防火墙。
如果在http请求中添加编码很可能会绕过waf规则,甚至绕过语义分析。导致数据泄漏风险,本应该被拦截的请求,还是得到了请求对应的响应数据。
可以通过下面连接了解一下,想要绕过web安全防火墙经过处理的payload长什么样。这里是我参考owasp(感谢他们对web安全做出的贡献)整理的xss攻击方式,包含各种经过处理的payload绕过方法。
通常云waf厂商都会自研自己的解码引擎。针对不同使用场景(互联网/金融/政企)提供不同的解码组合方案。
我们以华为云waf为例,看看他们的编解码说明。
下面整理一下常见的基于nginx+lua实现针对http payload编解码操作。
以下代码提供参考:
--base64
local function _base64_decode(value)
local val = ngx.decode_base64(tostring(value))
if (val) then
return val
else
return value
end
end
local function _base64_encode(value)
local val = ngx.encode_base64(value)
return val
end
--命令行
local function _cmd_line(value)
local val = tostring(value)
val = ngx.re.gsub(val,[=[[\\'"^]]=], '',"oij")
val = ngx.re.gsub(val,[=[\s+/]=],'/',"oij")
val = ngx.re.gsub(val, [=[\s+[(]]=],'(', "oij")
val = ngx.re.gsub(val, [=[[,;]]=],' ', "oij")
val = ngx.re.gsub(val, [=[\s+]=],' ', "oij")
return string.lower(val)
end
--压缩空格
local function _compress_whitespace(value)
return ngx.re.gsub(value, [=[\s+]=], ' ', "oij")
end
--十六进制
local function _hex_decode(value)
if type(value) ~= "string" then return value end
local str
if (pcall(function()
str = value:gsub('..', function (cc)
return string.char(tonumber(cc, 16))
end)
end)) then
return str
else
return value
end
end
local function _hex_encode(value)
if type(value) ~= "string" then return value end
return (value:gsub('.', function (c)return string.format('%02x', string.byte(c))end))
end
--html处理
local function _html_decode(value)
if type(value) ~= "string" then return value end
local str = ngx.re.gsub(value, [=[<]=], '<', "oij")
str = ngx.re.gsub(str, [=[>]=], '>', "oij")
str = ngx.re.gsub(str, [=["]=], '"', "oij")
str = ngx.re.gsub(str, [=[']=], "'", "oij")
str = ngx.re.gsub(str, [=[&#(\d+);]=], function(n) return string.char(n[1]) end, "oij")
str = ngx.re.gsub(str, [=[&#x(\d+);]=], function(n) return string.char(tonumber(n[1],16)) end, "oij")
str = ngx.re.gsub(str, [=[&]=], '&', "oij")
return str
end
--字符串长度
local function _length(value)
return tostring(#tostring(value))
end
--转换小写
local function _lowercase(value)
return string.lower(tostring(value))
end
--MD5处理
local function _md5(value)
if not value then return nil end
return ngx.md5_bin(value)
end
--规范路径格式
local function _normalise_path(value)
while (ngx.re.match(value, [=[[^/][^/]*/\.\./|/\./|/{2,}]=], "oij")) do
value = ngx.re.gsub(value, [=[[^/][^/]*/\.\./|/\./|/{2,}]=], '/', "oij")
end
return value
end
--删除注释
local function _remove_comments(value)
if type(value) ~= "string" then return value end
return ngx.re.gsub(value, [=[\/\*(\*(?!\/)|[^\*])*\*\/]=], '', "oij")
end
--删除注释字符
local function _remove_comments_char(value)
if type(value) ~= "string" then return value end
return ngx.re.gsub(value, [=[\/\*|\*\/|--|#]=], '', "oij")
end
--删除空格
local function _remove_whitespace(value)
if type(value) ~= "string" then return value end
return ngx.re.gsub(value, [=[\s+]=], '', "oij")
end
--替换注释
local function _replace_comments(value)
if type(value) ~= "string" then return value end
--[=[ / *( *(?!/) | [^*])* *]=]
return ngx.re.gsub(value, [=[\/\*(\*(?!\/)|[^\*])*\*\/]=], ' ', "oij")
end
--字符串SHA-1摘要
local function _sha1(value)
if not value then return nil end
return ngx.sha1_bin(value)
end
--sql_hex解码
local function _sql_hex_decode(value)
if type(value) ~= "string" then return value end
if (string.find(value, '0x', 1, true)) then
value = string.sub(value, 3)
local str
if (pcall(function()
str = value:gsub('..', function (cc)
return string.char(tonumber(cc, 16))
end)
end)) then
return str
else
return value
end
else
return value
end
end
--去除不必要的字符
local function _trim(value)
if type(value) ~= "string" then return value end
return ngx.re.gsub(value, [=[^\s*|\s+$]=], '')
end
local function _trim_left(value)
if type(value) ~= "string" then return value end
return ngx.re.sub(value, [=[^\s+]=], '')
end
local function _trim_right(value)
if type(value) ~= "string" then return value end
return ngx.re.sub(value, [=[\s+$]=], '')
end
--uri转码
local function _uri_decode(value)
local value = tostring(value)
return ngx.unescape_uri(value)
end
local function _uri_encode(value)
local value = tostring(value)
return ngx.escape_uri(value)
end
更多的编解码操作,我们可以通过lua ffi调用已经编译好的so库来实现。
c语言有着非常多的编解码开源程序,我们把它们编译成so库通过lua ffi可以任意使用so里面的编解码函数。后面会重点介绍。