diff --git a/plugins/op_waf/waf/lua/common.lua b/plugins/op_waf/waf/lua/common.lua index 151b0112c..c262d75ea 100644 --- a/plugins/op_waf/waf/lua/common.lua +++ b/plugins/op_waf/waf/lua/common.lua @@ -25,8 +25,6 @@ function _M.setConfData( self, config, site_config ) self.config = config self.site_config = site_config - -- ngx.say(json.encode(self.config)) - -- ngx.exit(0) end @@ -35,8 +33,7 @@ function _M.setParams( self, params ) end -function _M.is_min(self, ip1,ip2) - +function _M.is_min(self, ip1, ip2) n = 0 for _,v in ipairs({1,2,3,4}) do @@ -66,6 +63,36 @@ function _M.is_max(self,ip1,ip2) return true end +function _M.split(self, str,reps ) + local resultStrList = {} + string.gsub(str,'[^'..reps..']+',function(w) + table.insert(resultStrList,w) + end) + return resultStrList +end + +function _M.arrip(self, ipstr) + if ipstr == 'unknown' then return {0,0,0,0} end + if string.find(ipstr,':') then return ipstr end + iparr = self:split(ipstr,'.') + iparr[1] = tonumber(iparr[1]) + iparr[2] = tonumber(iparr[2]) + iparr[3] = tonumber(iparr[3]) + iparr[4] = tonumber(iparr[4]) + return iparr +end + + +function _M.compare_ip(self,ips) + local ip = self.params["ip"] + local ipn = self.params["ipn"] + if ip == 'unknown' then return true end + if string.find(ip,':') then return false end + if not self:is_max(ipn,ips[2]) then return false end + if not self:is_min(ipn,ips[1]) then return false end + return true +end + function _M.return_message(self, status, msg) ngx.header.content_type = "application/json;" diff --git a/plugins/op_waf/waf/lua/init.lua b/plugins/op_waf/waf/lua/init.lua index 8aa3b0a1b..46a002dd5 100644 --- a/plugins/op_waf/waf/lua/init.lua +++ b/plugins/op_waf/waf/lua/init.lua @@ -12,16 +12,10 @@ local config = C:read_file_body_decode(cpath .. 'config.json') local site_config = C:read_file_body_decode(cpath .. 'site.json') C:setConfData(config, site_config) - -local get_html = C:read_file_body(config["reqfile_path"] .. '/' .. config["get"]["reqfile"]) -local args_rules = C:read_file_table('args') - -local ip_white_rules = C:read_file('ip_white') - - function initParams() local data = {} data['ip'] = C:get_client_ip() + data['ipn'] = C:arrip(data['ip']) data['request_header'] = ngx.req.get_headers() data['uri'] = ngx.unescape_uri(ngx.var.uri) data['server_name'] = string.gsub(C:get_server_name(),'_','.') @@ -32,6 +26,8 @@ end local params = initParams() C:setParams(params) + + -- function min_route() -- if ngx.var.remote_addr ~= '127.0.0.1' then return false end -- if uri == '/get_waf_drop_ip' then @@ -43,6 +39,8 @@ C:setParams(params) -- end -- end +local get_html = C:read_file_body(config["reqfile_path"] .. '/' .. config["get"]["reqfile"]) +local args_rules = C:read_file_table('args') function waf_args() if not config['get']['open'] or not C:is_site_config('get') then return false end if C:is_ngx_match(args_rules, params['uri_request_args'],'args') then @@ -53,9 +51,415 @@ function waf_args() return false end + + + +local ip_white_rules = C:read_file('ip_white') +function waf_ip_white() + for _,rule in ipairs(ip_white_rules) + do + if C:compare_ip(rule) then + return true + end + end + return false +end + +local ip_black_rules = C:read_file('ip_black') +function waf_ip_black() + for _,rule in ipairs(ip_black_rules) + do + if C:compare_ip(rule) then + ngx.exit(config['cc']['status']) + return true + end + end + return false +end + + +local url_white_rules = C:read_file('url_white') +function waf_url_white() + if C:is_ngx_match(url_white_rules,params['request_uri'],false) then + return true + end + if site_config[server_name] ~= nil then + if C:is_ngx_match(site_config[server_name]['url_white'], params['request_uri'],false) then + return true + end + end + return false +end + + +local url_black_rules = C:read_file('url_black') +function waf_url_black() + if C:is_ngx_match(url_black_rules,params['request_uri'],false) then + ngx.exit(config['get']['status']) + return true + end + return false +end + + +function waf_drop() + local count,_ = ngx.shared.drop_ip:get(ip) + if not count then return false end + if count > config['retry'] then + ngx.exit(config['cc']['status']) + return true + end + return false +end + + + +local user_agent_html = C:read_file_body(config["reqfile_path"] .. '/' .. config["user-agent"]["reqfile"]) +function waf_user_agent() + if not config['user-agent']['open'] or not C:is_site_config('user-agent') then return false end + if C:is_ngx_match(user_agent_rules,params['request_header']['user-agent'],'user_agent') then + C:write_log('user_agent','regular') + C:return_html(config['user-agent']['status'],user_agent_html) + return true + end + return false +end + +-- function cc() +-- local ip = params['ip'] +-- local request_uri = params['request_uri'] +-- local endtime = config['cc']['endtime'] + +-- if not config['cc']['open'] or not site_cc then return false end +-- local token = ngx.md5(ip .. '_' .. request_uri) +-- local count,_ = ngx.shared.limit:get(token) +-- if count then +-- if count > limit then +-- local safe_count,_ = ngx.shared.drop_sum:get(ip) +-- if not safe_count then +-- ngx.shared.drop_sum:set(ip,1,86400) +-- safe_count = 1 +-- else +-- ngx.shared.drop_sum:incr(ip,1) +-- end +-- local lock_time = (endtime * safe_count) +-- if lock_time > 86400 then lock_time = 86400 end +-- ngx.shared.drop_ip:set(ip,retry+1,lock_time) +-- C:write_log('cc',cycle..'秒内累计超过'..limit..'次请求,封锁' .. lock_time .. '秒') +-- C:write_drop_ip('cc',lock_time) +-- if not server_name then +-- insert_ip_list(ip,lock_time,os.time(),'1111') +-- else +-- insert_ip_list(ip,lock_time,os.time(),server_name) +-- end + +-- ngx.exit(config['cc']['status']) +-- return true +-- else +-- ngx.shared.limit:incr(token,1) +-- end +-- else +-- ngx.shared.limit:set(token,1,cycle) +-- end +-- return false +-- end + +--强制验证是否使用正常浏览器访问网站 +function waf_cc_increase() + + if not config['cc']['open'] or not site_cc then return false end + if not site_config[server_name] then return false end + if not site_config[server_name]['cc']['increase'] then return false end + local cache_token = ngx.md5(ip .. '_' .. server_name) + --判断是否已经通过验证 + if ngx.shared.btwaf:get(cache_token) then return false end + if cc_uri_white() then + ngx.shared.btwaf:delete(cache_token .. '_key') + ngx.shared.btwaf:set(cache_token,1,60) + return false + end + if security_verification() then return false end + send_check_heml(cache_token) +end + + +function waf_url() + if not config['get']['open'] or not C:is_site_config('get') then return false end + --正则-- + if C:is_ngx_match(url_rules,params["uri"],'url') then + C:write_log('url','regular') + C:return_html(config['get']['status'],get_html) + return true + end + return false +end + + +function waf_scan_black() + if not config['scan']['open'] or not C:is_site_config('scan') then return false end + if C:is_ngx_match(scan_black_rules['cookie'],request_header['cookie'],false) then + C:write_log('scan','regular') + ngx.exit(config['scan']['status']) + return true + end + if C:is_ngx_match(scan_black_rules['args'],request_uri,false) then + C:write_log('scan','regular') + ngx.exit(config['scan']['status']) + return true + end + for key,value in pairs(request_header) + do + if C:is_ngx_match(scan_black_rules['header'],key,false) then + C:write_log('scan','regular') + ngx.exit(config['scan']['status']) + return true + end + end + return false +end + +function get_boundary() + local header = request_header["content-type"] + if not header then return nil end + if type(header) == "table" then + header = header[1] + end + + local m = string.match(header, ";%s*boundary=\"([^\"]+)\"") + if m then + return m + end + return string.match(header, ";%s*boundary=([^\",;]+)") +end + +function waf_post_referer() + if method ~= "POST" then return false end + if C:is_ngx_match(referer_local, request_header['Referer'],'post') then + C:write_log('post_referer','regular') + rC:eturn_html(config['post']['status'],post_html) + return true + end + return false +end + +function waf_post() + if not config['post']['open'] or not is_site_config('post') then return false end + if method ~= "POST" then return false end + if waf_post_referer() then return true end + content_length = tonumber(request_header['content-length']) + max_len = 640 * 1020000 + if content_length > max_len then return false end + if get_boundary() then return false end + ngx.req.read_body() + request_args = ngx.req.get_post_args() + if not request_args then + return false + end + + --return return_message(200,request_args) + if C:is_ngx_match(post_rules,request_args,'post') then + C:write_log('post','regular') + C:return_html(config['post']['status'],post_html) + return true + end + return false +end + + +function post_data_chekc() + if method =="POST" then + if return_post_data() then return false end + request_args = ngx.req.get_post_args() + if not request_args then return false end + if not request_header['Content-Type'] then return false end + av=string.match(request_header['Content-Type'],"=.+") + if not av then return false end + ac=split(av,'=') + if not ac then return false end + list_list=nil + for i,v in ipairs(ac) + do + list_list='--'..v + end + if not list_list then return false end + aaa=nil + for k,v in pairs(request_args) + do + aaa=v + end + if not aaa then return false end + if tostring(aaa) == 'true' then return false end + if type(aaa) ~= "string" then return false end + data_len=split(aaa,list_list) + --return return_message(200,data_len) + if not data_len then return false end + if arrlen(data_len) ==0 then return false end + + if is_ngx_match(post_rules,data_len,'post') then + write_log('post','regular') + return_html(config['post']['status'],post_html) + return true + end + + end +end + + +function X_Forwarded() + if method ~= "GET" then return false end + if not config['get']['open'] or not is_site_config('get') then return false end + if is_ngx_match(args_rules,request_header['X-forwarded-For'],'args') then + write_log('args','regular') + return_html(config['get']['status'],get_html) + return true + end + return false +end + + +function post_X_Forwarded() + if not config['post']['open'] or not is_site_config('post') then return false end + if method ~= "POST" then return false end + if is_ngx_match(post_rules,request_header['X-forwarded-For'],'post') then + write_log('post','regular') + return_html(config['post']['status'],post_html) + return true + end + return false +end + + +function php_path() + if site_config[server_name] == nil then return false end + for _,rule in ipairs(site_config[server_name]['disable_php_path']) + do + --if string.find(uri,rule .. "/.*\\.php$") then + if ngx_match(uri,rule .. "/?.*\\.php$","isjo") then + write_log('php_path','regular') + return_html(config['other']['status'],other_html) + return return_message(200,uri) + end + end + return false +end + + +function url_path() + if site_config[server_name] == nil then return false end + for _,rule in ipairs(site_config[server_name]['disable_path']) + do + if ngx_match(uri,rule,"isjo") then + write_log('path','regular') + return_html(config['other']['status'],other_html) + return true + end + end + return false +end + +function url_ext() + if site_config[server_name] == nil then return false end + for _,rule in ipairs(site_config[server_name]['disable_ext']) + do + if ngx_match(uri,"\\."..rule.."$","isjo") then + write_log('url_ext','regular') + return_html(config['other']['status'],other_html) + return true + end + end + return false +end + +function url_rule_ex() + if site_config[server_name] == nil then return false end + if method == "POST" and not request_args then + content_length=tonumber(request_header['content-length']) + max_len = 640 * 102400000 + request_args = nil + if content_length < max_len then + ngx.req.read_body() + request_args = ngx.req.get_post_args() + end + end + for _,rule in ipairs(site_config[server_name]['url_rule']) + do + if ngx_match(uri,rule[1],"isjo") then + if is_ngx_match(rule[2],uri_request_args,false) then + write_log('url_rule','regular') + return_html(config['other']['status'],other_html) + return true + end + + if method == "POST" and request_args ~= nil then + if is_ngx_match(rule[2],request_args,'post') then + write_log('post','regular') + return_html(config['other']['status'],other_html) + return true + end + end + end + end + return false +end + +function url_tell() + if site_config[server_name] == nil then return false end + for _,rule in ipairs(site_config[server_name]['url_tell']) + do + if ngx_match(uri,rule[1],"isjo") then + if uri_request_args[rule[2]] ~= rule[3] then + write_log('url_tell','regular') + return_html(config['other']['status'],other_html) + return true + end + end + end + return false +end + ngx.header.content_type = "text/html" function waf() + + if waf_ip_white() then return true end + waf_ip_black() + + if waf_url_white() then return true end + waf_url_black() + + waf_drop() + waf_user_agent() + + waf_url() + + if method == "GET" then + -- waf_referer() + -- waf_cookie() + end + + + if method == "POST" then + -- ngx.req.read_body() + -- request_args111 = ngx.req.get_post_args() + -- waf_cookie() + end + + + waf_args() + waf_scan_black() + + waf_post() + -- post_data_chekc() + -- if site_config[server_name] then + -- X_Forwarded() + -- post_X_Forwarded() + -- php_path() + -- url_path() + -- url_ext() + -- url_rule_ex() + -- url_tell() + -- post_data() + -- end end waf() diff --git a/plugins/op_waf/waf/rule/ip_white.json b/plugins/op_waf/waf/rule/ip_white.json index 2fae3655e..0e7bf1e17 100755 --- a/plugins/op_waf/waf/rule/ip_white.json +++ b/plugins/op_waf/waf/rule/ip_white.json @@ -1 +1 @@ -[[[127, 0, 0, 1], [127, 0, 0, 255]]] \ No newline at end of file +[[[127, 0, 0, 2], [127, 0, 0, 255]]] \ No newline at end of file diff --git a/plugins/op_waf/waf/rule/url.json b/plugins/op_waf/waf/rule/url.json new file mode 100755 index 000000000..28604915d --- /dev/null +++ b/plugins/op_waf/waf/rule/url.json @@ -0,0 +1 @@ +[[1, "\\.(htaccess|mysql_history|bash_history|DS_Store|idea|user\\.ini)", "\u6587\u4ef6\u76ee\u5f55\u8fc7\u6ee41", 0], [1, "\\.(bak|inc|old|mdb|sql|php~|swp|java|class)$", "\u6587\u4ef6\u76ee\u5f55\u8fc7\u6ee42", 0], [1, "^/(vhost|bbs|host|wwwroot|www|site|root|backup|data|ftp|db|admin|website|web).*\\.(rar|sql|zip|tar\\.gz|tar)$", "\u6587\u4ef6\u76ee\u5f55\u8fc7\u6ee43", 0], [1, "/(hack|shell|spy|phpspy)\\.php$", "PHP\u811a\u672c\u6267\u884c\u8fc7\u6ee41", 0], [1, "^/(attachments|css|uploadfiles|static|forumdata|cache|avatar)/(\\w+).(php|jsp)$", "PHP\u811a\u672c\u6267\u884c\u8fc7\u6ee42", 0], [1, "(?:(union(.*?)select))", "SQL\u6ce8\u5165\u8fc7\u6ee41", 0], [1, "(?: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)\\(", "\u4e00\u53e5\u8bdd\u6728\u9a6c\u8fc7\u6ee41", 1]] \ No newline at end of file diff --git a/plugins/op_waf/waf/rule/url_black.json b/plugins/op_waf/waf/rule/url_black.json new file mode 100755 index 000000000..0637a088a --- /dev/null +++ b/plugins/op_waf/waf/rule/url_black.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/plugins/op_waf/waf/rule/url_white.json b/plugins/op_waf/waf/rule/url_white.json new file mode 100755 index 000000000..9769f9000 --- /dev/null +++ b/plugins/op_waf/waf/rule/url_white.json @@ -0,0 +1 @@ +["^/phpmyadmin_", "^/wp-content/themes/begin/timthumb\\.php", "^/web/index\\.php\\?c=cloud", "^/\\.well-known/"] \ No newline at end of file