diff --git a/README.md b/README.md index f37bb1a..e472929 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ngx_lua_waf是我刚入职趣游时候开发的一个基于ngx_lua的web应用 现在开源出来.其中包含我们的过滤规则。如果大家有什么建议和想fa,欢迎和我一起完善。 ###用途: - + 防止sql注入,本地包含,部分溢出,fuzzing测试,xss,SSRF等web攻击 防止svn/备份之类文件泄漏 防止ApacheBench之类压力测试工具的攻击 @@ -18,29 +18,60 @@ ngx_lua_waf是我刚入职趣游时候开发的一个基于ngx_lua的web应用 ###效果图如下: -![sec](http://www.sectop.org/wp-content/uploads/2013/03/QQ截图20130323150826.jpg) +![sec](http://i.imgur.com/DqU30au.png) ###推荐安装: -请自行给nginx安装ngx_lua模块,需要lujit做lua支持 +请自行给nginx安装ngx_lua模块,推荐使用lujit做lua支持 -请提前新建/data/logs/hack/目录攻击日志,并赋予nginx用户对该目录的写入权限。 +###使用说明: -###配置部分: +nginx安装路径假设为:/usr/local/nginx/conf/ - 编辑init.lua配置部分 - logpath='/data/logs/hack/' - rulepath='/usr/local/nginx/conf/wafconf/' - syslogserver='127.0.0.1' - 如果需要开启syslog传输,请取消掉log函数部分的注释 - filext是限制上传的文件后缀名 +把ngx_lua_waf下载到conf目录下,解压命名为waf - 在nginx.conf的http段添加 - init_by_lua_file /usr/local/nginx/conf/init.lua; - access_by_lua_file /usr/local/nginx/conf/waf.lua; - - 注意:第一次安装配置好需要重启nginx +在nginx.conf的http段添加 + + lua_package_path "/usr/local/nginx/conf/waf/?.lua"; + lua_shared_dict limit 10m; + init_by_lua_file /usr/local/nginx/conf/waf/init.lua; + access_by_lua_file /usr/local/nginx/conf/waf/waf.lua; + +配置config.lua里的waf规则目录(一般在waf/conf/目录下) + + RulePath = "/usr/local/nginx/conf/waf/wafconf/" + +绝对路径如有变动,需对应修改 + +###配置文件详细说明: + + RulePath = "/usr/local/nginx/conf/waf/wafconf/" + --规则存放目录 + attacklog = "off" + --是否开启攻击信息记录,需要配置logdir + logdir = "/usr/local/nginx/logs/hack/" + --log存储目录,该目录需要nginx用户的可写权限 + UrlDeny="on" + --是否拦截url访问 + Redirect="on" + --是否拦截后重定向 + CookieMatch = "on" + --是否拦截cookie攻击 + postMatch = "on" + --是否拦截post攻击 + whiteModule = "on" + --是否开启白名单 + ipWhitelist={"127.0.0.1"} + --ip白名单,多个ip用逗号分隔 + CCDeny="on" + --是否开启拦截cc攻击(需要nginx.conf的http段增加lua_shared_dict limit 10m;) + CCrate = "100/60" + --设置cc攻击频率,单位为秒. + --默认1分钟同一个IP只能请求同一个文件(request_filename)100次 + html=[[Please go away~~]] + --警告内容,可在中括号内自定义 + 备注:不要乱动双引号,区分大小写 ###规则更新: diff --git a/config.lua b/config.lua new file mode 100644 index 0000000..26f2b1d --- /dev/null +++ b/config.lua @@ -0,0 +1,12 @@ +RulePath = "/usr/local/nginx/conf/waf/wafconf/" +attacklog = "off" +logdir = "/usr/local/nginx/logs/hack/" +UrlDeny="on" +Redirect="on" +CookieMatch="on" +postMatch="on" +whiteModule="on" +ipWhitelist={"127.0.0.1"} +CCDeny="off" +CCrate="100/60" +html=[[Please go away~~ ]] diff --git a/init.lua b/init.lua index 14ba94b..f9d693e 100644 --- a/init.lua +++ b/init.lua @@ -1,68 +1,53 @@ ---配置部分 -logpath='/data/logs/hack/' -rulepath='/usr/local/nginx/conf/wafconf/' -syslogserver='127.0.0.1' -filext='' ---如果需要开启syslog传输,请取消掉log函数部分的注释 ---syslog函数和本地日志记录函数 -local bit = require "bit" -local ffi = require "ffi" -local C = ffi.C -local bor = bit.bor -ffi.cdef[[ -int write(int fd, const char *buf, int nbyte); -int open(const char *path, int access, int mode); -int close(int fd); -]] - -local O_RDWR = 0X0002; -local O_CREAT = 0x0040; -local O_APPEND = 0x0400; -local S_IRUSR = 0x0100; -local S_IWUSR = 0x0080; -function write(logfile,msg) - local logger_fd = C.open(logfile, bor(O_RDWR, O_CREAT, O_APPEND), bor(S_IRUSR,S_IWUSR)); - local c = msg; - C.write(logger_fd, c, #c); - C.close(logger_fd) -end -function syslog(msg) - ngx.header.content_type = "text/html" -local sock = ngx.socket.udp() -local ok, err = sock:setpeername(syslogserver, 514) ---上面的ip和端口就是syslog server的ip和端口地址,可自行修改 -if not ok then - ngx.say("failed to connect to syslog server: ", err) - return +require 'config' +local match = string.match +local ngxmatch=ngx.re.match +local unescape=ngx.unescape_uri +local get_headers = ngx.req.get_headers +local optionIsOn = function (options) return options == "on" and true or false end +logpath = logdir +rulepath = RulePath +UrlDeny = optionIsOn(UrlDeny) +PostCheck = optionIsOn(postMatch) +CookieCheck = optionIsOn(cookieMatch) +WhiteCheck = optionIsOn(whiteModule) +PathInfoFix = optionIsOn(PathInfoFix) +attacklog = optionIsOn(attacklog) +CCDeny = optionIsOn(CCDeny) +CCrate = CCrate +Redirect=optionIsOn(Redirect) +ipWhitelist=ipWhitelist +function getClientIp() + IP = ngx.req.get_headers()["X-Real-IP"] + if IP == nil then + IP = ngx.var.remote_addr + end + if IP == nil then + IP = "unknown" + end + return IP end -ok, err = sock:send('<30>'..msg) -sock:close() +function write(logfile,msg) + local fd = io.open(logfile,"ab") + if fd == nil then return end + fd:write(msg) + fd:flush() + fd:close() end -function log(method,url,data) - if data then - if ngx.var.http_user_agent then - -- syslog(ngx.var.remote_addr.." ".." ["..ngx.localtime().."] \""..method.." "..url.."\" \""..data.."\" \""..ngx.status.."\" \""..ngx.var.http_user_agent.."\"\n") - write(logpath..'/'..ngx.var.server_name.."_sec.log",ngx.var.remote_addr.." ".." ["..ngx.localtime().."] \""..method.." "..url.."\" \""..data.."\" \""..ngx.status.."\" \""..ngx.var.http_user_agent.."\"\n") - else - -- syslog(ngx.var.remote_addr.." ".." ["..ngx.localtime().."] \""..method.." "..url.."\" \""..data.."\" \"-\"\n") - write(logpath..'/'..ngx.var.server_name.."_sec.log",ngx.var.remote_addr.." ".." ["..ngx.localtime().."] \""..method.." "..url.."\" \""..data.."\" \"-\"\n") - end - else - if ngx.var.http_user_agent then - -- syslog(ngx.var.remote_addr.." ".." ["..ngx.localtime().."] \""..method.." "..url.."\" \"-\" \""..ngx.var.http_user_agent.."\"\n") - write(logpath..'/'..ngx.var.server_name.."_sec.log",ngx.var.remote_addr.." ".." ["..ngx.localtime().."] \""..method.." "..url.."\" \"-\" \""..ngx.var.http_user_agent.."\"\n") - else - -- syslog(ngx.var.remote_addr.." ".." ["..ngx.localtime().."] \""..method.." "..url.."\" \"-\" \"".."-\"\n") - write(logpath..'/'..ngx.var.server_name.."_sec.log",ngx.var.remote_addr.." ".." ["..ngx.localtime().."] \""..method.." "..url.."\" \"-\" \"".."-\"\n") - end +function log(method,url,data,ruletag) + if attacklog then + local realIp = getClientIp() + local ua = ngx.var.http_user_agent + local servername=ngx.var.server_name + local time=ngx.localtime() + if ua then + line = realIp.." ["..time.."] \""..method.." "..servername..url.."\" \""..data.."\" \""..ua.."\" \""..ruletag.."\"\n" + else + line = realIp.." ["..time.."] \""..method.." "..servername..url.."\" \""..data.."\" - \""..ruletag.."\"\n" + end + local filename = logpath..'/'..servername.."_"..ngx.today().."_sec.log" + write(filename,line) end end ---------------------------------------响应函数-------------------------------------------------------------------------------- -function check() - ngx.header.content_type = "text/html" - ngx.print("just a joke hehe~ !!") - ngx.exit(200) -end ------------------------------------规则读取函数------------------------------------------------------------------- function read_rule(var) file = io.open(rulepath..'/'..var,"r") @@ -71,10 +56,149 @@ function read_rule(var) table.insert(t,line) end file:close() - return(table.concat(t,"|")) + return(t) +end + +urlrules=read_rule('url') +argsrules=read_rule('args') +uarules=read_rule('user-agent') +wturlrules=read_rule('whiteurl') +postrules=read_rule('post') +ckrules=read_rule('cookie') + + +function say_html() + if Redirect then + ngx.header.content_type = "text/html" + ngx.say(html) + ngx.exit(200) + end +end + +function whiteurl() + if WhiteCheck then + for _,rule in pairs(wturlrules) do + if ngxmatch(ngx.var.request_uri,rule,"isjo") then + return true + end + end + end + return false +end + +function args() + for _,rule in pairs(argsrules) do + local args = ngx.req.get_uri_args() + for key, val in pairs(args) do + if type(val)=='table' then + data=table.concat(val, " ") + else + data=val + end + if data and type(data) ~= "boolean" and ngxmatch(unescape(data),rule,"isjo") then + log('GET',ngx.var.request_uri,"-",rule) + say_html() + return true + end + end + end + return false +end + + +function url() + if UrlDeny then + for _,rule in pairs(urlrules) do + if ngxmatch(ngx.var.request_uri,rule,"isjo") then + log('GET',ngx.var.request_uri,"-",rule) + say_html() + return true + end + end + end + return false +end + +function ua() + local ua = ngx.var.http_user_agent + for _,rule in pairs(uarules) do + if ngxmatch(ua,rule,"isjo") then + log('UA',ngx.var.request_uri,"-",rule) + return true + end + end + return false +end +function body(data) + for _,rule in pairs(postrules) do + if ngxmatch(unescape(data),rule,"isjo") then + log('POST',ngx.var.request_uri,data,rule) + say_html() + return true + end + end + return false +end +function cookie() + local ck = ngx.var.http_cookie + if CookieCheck and ck then + for _,rule in pairs(ckrules) do + if ngxmatch(ck,rule,"isjo") then + log('Cookie',ngx.var.request_uri,"-",rule) + say_html() + return true + end + end + end + return false +end + +function denycc() + if CCDeny then + CCcount=tonumber(string.match(CCrate,'(.*)/')) + CCseconds=tonumber(string.match(CCrate,'/(.*)')) + local token=getClientIp()..ngx.var.request_filename + local limit = ngx.shared.limit + local req,_=limit:get(token) + if req then + if req > CCcount then + ngx.exit(503) + return true + else + limit:incr(token,1) + end + else + limit:set(token,1,CCseconds) + end + end + return false +end + +function get_boundary() + local header = get_headers()["content-type"] + if not header then + return nil + end + + if type(header) == "table" then + header = header[1] + end + + local m = match(header, ";%s*boundary=\"([^\"]+)\"") + if m then + return m + end + + return match(header, ";%s*boundary=([^\",;]+)") +end + +function whiteip() + if next(ipWhitelist) ~= nil then + for _,ip in pairs(ipWhitelist) do + if getClientIp()==ip then + return true + end + end + end + return false end -regex=read_rule('global') -get=read_rule('get') -post=read_rule('post') -agent=read_rule('user-agent') -whitelist=read_rule('whitelist') diff --git a/upload.lua b/upload.lua new file mode 100644 index 0000000..9854ba5 --- /dev/null +++ b/upload.lua @@ -0,0 +1,267 @@ +-- Copyright (C) Yichun Zhang (agentzh) + + +local sub = string.sub +local req_socket = ngx.req.socket +local null = ngx.null +local match = string.match +local setmetatable = setmetatable +local error = error +local get_headers = ngx.req.get_headers +local type = type + local print = print + + +local _M = { _VERSION = '0.08' } + + +local MAX_LINE_SIZE = 512 + +local STATE_BEGIN = 1 +local STATE_READING_HEADER = 2 +local STATE_READING_BODY = 3 +local STATE_EOF = 4 + + +local mt = { __index = _M } + +local state_handlers + + +local function get_boundary() + local header = get_headers()["content-type"] + if not header then + return nil + end + + if type(header) == "table" then + header = header[1] + end + + local m = match(header, ";%s*boundary=\"([^\"]+)\"") + if m then + return m + end + + return match(header, ";%s*boundary=([^\",;]+)") +end + + +function _M.new(self, chunk_size) + local boundary = get_boundary() + + print("boundary: ", boundary) + + if not boundary then + return nil, "no boundary defined in Content-Type" + end + + print('boundary: "', boundary, '"') + + local sock, err = req_socket() + if not sock then + return nil, err + end + + local read2boundary, err = sock:receiveuntil("--" .. boundary) + if not read2boundary then + return nil, err + end + + local read_line, err = sock:receiveuntil("\r\n") + if not read_line then + return nil, err + end + + return setmetatable({ + sock = sock, + size = chunk_size or 4096, + read2boundary = read2boundary, + read_line = read_line, + boundary = boundary, + state = STATE_BEGIN + }, mt) +end + + +function _M.set_timeout(self, timeout) + local sock = self.sock + if not sock then + return nil, "not initialized" + end + + return sock:settimeout(timeout) +end + + +local function discard_line(self) + local read_line = self.read_line + + local line, err = self.read_line(MAX_LINE_SIZE) + if not line then + return nil, err + end + + local dummy, err = self.read_line(1) + if dummy then + return nil, "line too long: " .. line .. dummy .. "..." + end + + if err then + return nil, err + end + + return 1 +end + + +local function discard_rest(self) + local sock = self.sock + local size = self.size + + while true do + local dummy, err = sock:receive(size) + if err and err ~= 'closed' then + return nil, err + end + + if not dummy then + return 1 + end + end +end + + +local function read_body_part(self) + local read2boundary = self.read2boundary + + local chunk, err = read2boundary(self.size) + if err then + return nil, nil, err + end + + if not chunk then + local sock = self.sock + + local data = sock:receive(2) + if data == "--" then + local ok, err = discard_rest(self) + if not ok then + return nil, nil, err + end + + self.state = STATE_EOF + return "part_end" + end + + if data ~= "\r\n" then + local ok, err = discard_line(self) + if not ok then + return nil, nil, err + end + end + + self.state = STATE_READING_HEADER + return "part_end" + end + + return "body", chunk +end + + +local function read_header(self) + local read_line = self.read_line + + local line, err = read_line(MAX_LINE_SIZE) + if err then + return nil, nil, err + end + + local dummy, err = read_line(1) + if dummy then + return nil, nil, "line too long: " .. line .. dummy .. "..." + end + + if err then + return nil, nil, err + end + + -- print("read line: ", line) + + if line == "" then + -- after the last header + self.state = STATE_READING_BODY + return read_body_part(self) + end + + local key, value = match(line, "([^: \t]+)%s*:%s*(.+)") + if not key then + return 'header', line + end + + return 'header', {key, value, line} +end + + +local function eof() + return "eof", nil +end + + +function _M.read(self) + local size = self.size + + local handler = state_handlers[self.state] + if handler then + return handler(self) + end + + return nil, nil, "bad state: " .. self.state +end + + +local function read_preamble(self) + local sock = self.sock + if not sock then + return nil, nil, "not initialized" + end + + local size = self.size + local read2boundary = self.read2boundary + + while true do + local preamble, err = read2boundary(size) + if not preamble then + break + end + + -- discard the preamble data chunk + -- print("read preamble: ", preamble) + end + + local ok, err = discard_line(self) + if not ok then + return nil, nil, err + end + + local read2boundary, err = sock:receiveuntil("\r\n--" .. self.boundary) + if not read2boundary then + return nil, nil, err + end + + self.read2boundary = read2boundary + + self.state = STATE_READING_HEADER + return read_header(self) +end + + +state_handlers = { + read_preamble, + read_header, + read_body_part, + eof +} + + +return _M diff --git a/waf.lua b/waf.lua index 4318f7f..003b1e8 100644 --- a/waf.lua +++ b/waf.lua @@ -1,24 +1,60 @@ -ngx.req.read_body() -if ngx.re.match(ngx.var.request_uri,whitelist,"isjo") then - return -else - if ngx.re.match(ngx.unescape_uri(ngx.var.request_uri),regex.."|"..get,"isjo") then - log('GET',ngx.unescape_uri(ngx.var.request_uri)) - check() - elseif ngx.var.http_user_agent and ngx.re.match(ngx.var.http_user_agent,regex.."|"..agent,"isjo") then - log('USER-AGENT',ngx.unescape_uri(ngx.var.request_uri)) - check() - elseif ngx.req.get_body_data() and ngx.re.match(ngx.unescape_uri(ngx.req.get_body_data()),regex.."|"..post,"isjo") then - log('POST',ngx.unescape_uri(ngx.var.request_uri),ngx.unescape_uri(ngx.req.get_body_data())) - check() - elseif ngx.req.get_headers()["Cookie"] and ngx.re.match(ngx.unescape_uri(ngx.req.get_headers()["Cookie"]),regex,"isjo")then - log('COOKIE',ngx.unescape_uri(ngx.var.request_uri),ngx.unescape_uri(ngx.req.get_headers()["Cookie"])) - check() - elseif ngx.req.get_headers()['Acunetix-Aspect'] then - ngx.exit(400) - elseif ngx.req.get_headers()['X-Scan-Memo'] then - ngx.exit(400) - else - return + local upload = require "upload" + local content_length=tonumber(ngx.req.get_headers()['content-length']) +local method=ngx.req.get_method() +if whiteip() then +elseif denycc() then +elseif ngx.var.http_Acunetix_Aspect then + ngx.exit(444) +elseif ngx.var.http_X_Scan_Memo then + ngx.exit(444) +elseif whiteurl() then +elseif ua() then +elseif url() then +elseif args() then +elseif cookie() then +elseif PostCheck then + if method=="POST" then + local boundary = get_boundary() + if boundary then + local form = upload:new(500) + if not form then + return + end + form:set_timeout(1000) -- 1 sec + while true do + local typ, res, err = form:read() + if not typ then + return + end + if typ=="body" then + body(res) + end + + if typ == "eof" then + break + end + end + +-- local typ, res, err = form:read() + -- body(res) + else + ngx.req.read_body() + local args = ngx.req.get_post_args() + if not args then + return + end + for key, val in pairs(args) do + if type(val) == "table" then + data=table.concat(val, ", ") + else + data=val + end + if data and type(data) ~= "boolean" and body(data) then + return true + end + end + end end +else + return end diff --git a/wafconf/args b/wafconf/args new file mode 100644 index 0000000..30554ca --- /dev/null +++ b/wafconf/args @@ -0,0 +1,20 @@ +\.\./ +\:\$ +\$\{ +select.+(from|limit) +(?:(union(.*?)select)) +having|rongjitest +sleep\((\s*)(\d*)(\s*)\) +benchmark\((.*)\,(.*)\) +base64_decode\( +(?:from\W+information_schema\W) +(?:(?:current_)user|database|schema|connection_id)\s*\( +(?:etc\/\W*passwd) +into(\s+)+(?:dump|out)file\s* +group\s+by.+\( +xwork.MethodAccessor +(?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|preg_\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\( +xwork\.MethodAccessor +(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/ +java\.lang +\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[ diff --git a/wafconf/cookie b/wafconf/cookie new file mode 100644 index 0000000..30554ca --- /dev/null +++ b/wafconf/cookie @@ -0,0 +1,20 @@ +\.\./ +\:\$ +\$\{ +select.+(from|limit) +(?:(union(.*?)select)) +having|rongjitest +sleep\((\s*)(\d*)(\s*)\) +benchmark\((.*)\,(.*)\) +base64_decode\( +(?:from\W+information_schema\W) +(?:(?:current_)user|database|schema|connection_id)\s*\( +(?:etc\/\W*passwd) +into(\s+)+(?:dump|out)file\s* +group\s+by.+\( +xwork.MethodAccessor +(?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|preg_\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\( +xwork\.MethodAccessor +(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/ +java\.lang +\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[ diff --git a/wafconf/global b/wafconf/global index 253cb43..c1047a6 100644 --- a/wafconf/global +++ b/wafconf/global @@ -37,6 +37,16 @@ phpinfo\( (?:\b(?:\.(?:ht(?:access|passwd|group)|www_?acl)|global\.asa|httpd\.conf|boot\.ini)\b|\/etc\/) (gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data|expect)\:\/ \$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[ -java\.lang\.Process -java\.io\.File +\/proc\/(\d+|self)\/environ +\<(iframe|script|body|img) +javascript\: +onmouseover\= +ewebe +jmx-console +javascript\: +phpmyadmin \$\{ +java\.lang +\)\.exec\( +\(\' +\"\= diff --git a/wafconf/post b/wafconf/post index d1127d1..30554ca 100644 --- a/wafconf/post +++ b/wafconf/post @@ -1 +1,20 @@ -\)\.exec\( \ No newline at end of file +\.\./ +\:\$ +\$\{ +select.+(from|limit) +(?:(union(.*?)select)) +having|rongjitest +sleep\((\s*)(\d*)(\s*)\) +benchmark\((.*)\,(.*)\) +base64_decode\( +(?:from\W+information_schema\W) +(?:(?:current_)user|database|schema|connection_id)\s*\( +(?:etc\/\W*passwd) +into(\s+)+(?:dump|out)file\s* +group\s+by.+\( +xwork.MethodAccessor +(?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|preg_\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\( +xwork\.MethodAccessor +(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/ +java\.lang +\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[ diff --git a/wafconf/url b/wafconf/url new file mode 100644 index 0000000..4309d21 --- /dev/null +++ b/wafconf/url @@ -0,0 +1,6 @@ +\.(svn|htaccess|bash_history) +\.(bak|inc|old|mdb|sql|backup|java|class)$ +(vhost|bbs|host|wwwroot|www|site|root|hytop|flashfxp).*.rar +(phpmyadmin|jmx-console|jmxinvokerservlet) +java\.lang +/(attachments|upimg|images|css|uploadfiles|html|uploads|templets|static|template|data|inc|forumdata|upload|includes|cache|avatar)/(\\w+).(php|jsp) diff --git a/wafconf/user-agent b/wafconf/user-agent index d80daa5..b3edddf 100644 --- a/wafconf/user-agent +++ b/wafconf/user-agent @@ -1 +1 @@ -.*(LWP::Simple|winhttp|clshttp|HTTrack|harvest|nsauditor|dirbuster|pangolin|nmap|sqlninja|grendel-scan|hydra|perl|HTMLParser|libwww|BBBike|sqlmap|w3af|owasp|Nikto|fimap|havij|PycURL|sae|zmeu|BabyKrokodil|python|netsparker|httperf|ApacheBench|webbench).* +(HTTrack|harvest|audit|dirbuster|pangolin|nmap|sqln|-scan|hydra|Parser|libwww|BBBike|sqlmap|w3af|owasp|Nikto|fimap|havij|PycURL|zmeu|BabyKrokodil|netsparker|httperf|bench) diff --git a/wafconf/whiteurl b/wafconf/whiteurl new file mode 100644 index 0000000..3cd0864 --- /dev/null +++ b/wafconf/whiteurl @@ -0,0 +1 @@ +/123/