diff --git a/ExampleWAF.md b/ExampleWAF.md index 04e2a80..5d4c337 100644 --- a/ExampleWAF.md +++ b/ExampleWAF.md @@ -22,7 +22,7 @@ haproxy: # deny_dangerous_methods: true block_script_bots: true block_bad_crawler_bots: true - + block_script_kiddies: true routes: be_test: @@ -140,6 +140,10 @@ root@test-ag-haproxy-waf:/# cat /etc/haproxy/conf.d/backend.cfg > http-request deny status 425 default-errorfiles if { req.fhdr(User-Agent) -m sub -i curl wget Apache-HttpClient nmap Metasploit headless cypress go-http-client zgrab python httpx httpcore aiohttp httputil urllib GuzzleHttp phpcrawl Zend_Http_Client Wordpress Symfony-HttpClient cpp-httplib java perl axios ruby } > # block well-known bad-crawler-bots > http-request deny status 425 default-errorfiles if { req.fhdr(User-Agent) -m sub -i spider test-bot tiny-bot fidget-spinner-bot download scrapy } +> # block script-kiddy requests +> http-request deny status 425 default-errorfiles if { path_beg -i /cgi-bin/ /icons/ /manager/ /php /program/ /pwd/ /shaAdmin/ /typo3/ /admin/ /dbadmin/ /db/ /solr/ /weaver/ /joomla/ /App/ /webdav/ /xmlrpc /% /. /securityRealm/ /magmi/ /menu/ /etc/ /HNAP1 } +> http-request deny status 425 default-errorfiles if { path_end -i .php .asp .aspx .esp .lua .rsp .ashx .dll .bin .cgi .cs .application .exe .env .git/config .git/HEAD .git/index .DS_Store .aws/config .config .settings .zip .tar .tgz .gz .bz2 .rar .7z .sql .sqlite3 .bak } +> http-request deny status 425 default-errorfiles if { path_sub -i /../ } > > server srv-1 192.168.10.11:80 check > server srv-2 192.168.10.12:80 check diff --git a/defaults/main/1_main.yml b/defaults/main/1_main.yml index e33ad62..8630872 100644 --- a/defaults/main/1_main.yml +++ b/defaults/main/1_main.yml @@ -97,6 +97,7 @@ defaults_frontend: block_script_bots: false block_bad_crawler_bots: false + block_script_kiddies: false # you might have to define 'haproxy.waf.script_kiddy.excludes' for your use-cases flag_bots: false flag_bots_lines: [] # additional checks you want to append; you could p.e. check if a cookie set by JS exists @@ -154,6 +155,7 @@ defaults_backend: block_script_bots: false block_bad_crawler_bots: false + block_script_kiddies: false # you might have to define 'haproxy.waf.script_kiddy.excludes' for your use-cases flag_bots: false flag_bots_lines: [] # additional checks you want to append; you could p.e. check if a cookie set by JS exists diff --git a/defaults/main/2_waf.yml b/defaults/main/2_waf.yml index 8891a6f..2ebfee8 100644 --- a/defaults/main/2_waf.yml +++ b/defaults/main/2_waf.yml @@ -84,3 +84,71 @@ defaults_waf: - 'office' - 'facebook' - 'feed' + + script_kiddy: + excludes: [] # user-defined excludes + + path_beg: + - '/cgi-bin/' + - '/icons/' + - '/manager/' + - '/php' + - '/program/' + - '/pwd/' + - '/shaAdmin/' + - '/typo3/' + - '/admin/' + - '/dbadmin/' + - '/db/' + - '/solr/' + - '/weaver/' + - '/joomla/' + - '/App/' + - '/webdav/' + - '/xmlrpc' + - '/%' + - '/.' + - '/securityRealm/' + - '/magmi/' + - '/menu/' + - '/etc/' + - '/HNAP1' + + path_end: + # scripts etc + - '.php' + - '.asp' + - '.aspx' + - '.esp' + - '.lua' + - '.rsp' + - '.ashx' + - '.dll' + - '.bin' + - '.cgi' + - '.cs' + - '.application' + - '.exe' + # information gathering + - '.env' + - '.git/config' + - '.git/HEAD' + - '.git/index' + - '.DS_Store' + - '.aws/config' + - '.config' + - '.settings' + # data extraction + - '.zip' + - '.tar' + - '.tgz' + - '.gz' + - '.bz2' + - '.rar' + - '.7z' + - '.sql' + - '.sqlite3' + - '.bak' + + path_sub: + - '/../' diff --git a/filter_plugins/utils.py b/filter_plugins/utils.py index c3f2da5..2ca6948 100644 --- a/filter_plugins/utils.py +++ b/filter_plugins/utils.py @@ -11,6 +11,7 @@ def filters(self): "safe_key": self.safe_key, "ssl_fingerprint_active": self.ssl_fingerprint_active, "build_route": self.build_route, + "join_w_excludes": self.join_w_excludes, } @staticmethod @@ -49,6 +50,11 @@ def ssl_fingerprint_active(frontends: dict) -> bool: def is_truthy(v: (bool, str, int)) -> bool: return v in [True, 'yes', 'y', 'Yes', 'YES', 'true', 1, '1'] + @classmethod + def join_w_excludes(cls, v: list, excludes: list) -> str: + return ' '.join([v for v in cls.ensure_list(v) if v not in cls.ensure_list(excludes)]) + + @classmethod def build_route(cls, fe_cnf: dict, be_cnf: dict, be_name: str) -> list: lines = [] diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 1bc525d..91311f2 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -172,6 +172,7 @@ block_script_bots: true block_bad_crawler_bots: true flag_bots: true + block_script_kiddies: true routes: be_test: diff --git a/templates/etc/haproxy/conf.d/inc/security.j2 b/templates/etc/haproxy/conf.d/inc/security.j2 index 4958f85..5301c09 100644 --- a/templates/etc/haproxy/conf.d/inc/security.j2 +++ b/templates/etc/haproxy/conf.d/inc/security.j2 @@ -23,6 +23,12 @@ http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { req.fhdr(User-Agent) -m sub -i {{ HAPROXY_WAF.user_agents.bad_crawlers.sub | ensure_list | join(' ') }} } {% endif %} {% endif %} +{% if cnf.security.block_script_kiddies | bool %} + # block script-kiddy requests + http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { path_beg -i {{ HAPROXY_WAF.script_kiddy.path_beg | join_w_excludes(HAPROXY_WAF.script_kiddy.excludes) }} } + http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { path_end -i {{ HAPROXY_WAF.script_kiddy.path_end | join_w_excludes(HAPROXY_WAF.script_kiddy.excludes) }} } + http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { path_sub -i {{ HAPROXY_WAF.script_kiddy.path_sub | join_w_excludes(HAPROXY_WAF.script_kiddy.excludes) }} } +{% endif %} {% if cnf.security.flag_bots | bool %} # FLAG BOTS ## flag bots by common user-agent substrings