Blocage dynamique des attaques web
Frédéric VANNIÈRE - Homere
f.vanniere@planet-work.com
Sysadmin #6 - 18-19 février 2016
Présentation
- responsable technique depuis 2001
- hébergement mutualisé
- serveurs infogérés
Contexte
- 10 000 sites
- 30% Wordpress
- nombreuses attaques, sites piratés
- NetApp qui rame (NFSv3, GETATTR)
Besoin d'une protection
ww.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:42 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.344
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:43 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.329
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:43 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 0.805
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:43 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.329
alteora.machin-virtuel.biz 86.66.32.177 - - [18/Feb/2016:02:54:43 +0100] "POST /modules/screensaver/rech_next_media.php HTTP/1.1" 200 289 "http://alteora.machin-virtuel.biz/modules/screensaver/index_ssaver.php" "Mozilla/5.0 (Windows NT 6.1; rv:41.0) Gecko/20100101 Firefox/41.0" 0.045
www.monsite.com 82.146.44.148 - - [18/Feb/2016:02:54:44 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57012 "-" "-" 1.487
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:44 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.329
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:44 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 1.054
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:44 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.336
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:45 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 0.792
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:45 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.335
lamar2.machinvirtuel.fr 83.204.221.120 - - [18/Feb/2016:02:54:45 +0100] "POST /modules/screensaver/rech_next_media.php HTTP/1.1" 200 301 "http://lamar2.machinvirtuel.fr/modules/screensaver/index_ssaver.php" "Mozilla/5.0 (Windows NT 6.1; rv:44.0) Gecko/20100101 Firefox/44.0" 0.045
solutruc.ci 66.249.66.64 - - [18/Feb/2016:02:54:45 +0100] "POST /fr/produits/EasyVistaITServiceManager/gestion-des-changements HTTP/1.1" 200 6331 "http://solutruc.ci/fr/produits/EasyVistaITServiceManager/gestion-des-changements" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" 0.087
www.monsite.com 82.146.44.148 - - [18/Feb/2016:02:54:46 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57012 "-" "-" 1.911
lamaranmaiaf.fr 80.11.89.63 - - [18/Feb/2016:02:54:46 +0100] "POST /calcul.php HTTP/1.1" 200 438 "http://lamaranmaiaf.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36" 0.048
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:46 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.427
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:46 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 0.912
liguepacadessportsdeglisse.fr 195.154.240.184 - - [18/Feb/2016:02:54:46 +0100] "POST /wp-login.php HTTP/1.1" 200 5252 "http://liguepacadessportsdeglisse.fr/wp-login.php" "Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1; 125LA; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)" 0.097
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:47 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.404
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:47 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 0.974
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:47 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.404
www.monsite.com 82.146.44.148 - - [18/Feb/2016:02:54:47 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57012 "-" "-" 1.559
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:48 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 0.967
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:48 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.333
www.xxaagroup.com 195.182.94.10 - - [18/Feb/2016:02:54:48 +0100] "POST / HTTP/1.0" 200 19988 "http://www.xxaagroup.com/" "Mozilla/5.0 (Windows NT 6.1; rv:40.0) Gecko/20100101 Firefox/40.0" 0.444
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:49 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.365
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:49 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 1.031
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:49 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.340
www.monsite.com 82.146.44.148 - - [18/Feb/2016:02:54:50 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57012 "-" "-" 1.998
lamaranmaiaf.fr 80.11.89.63 - - [18/Feb/2016:02:54:50 +0100] "POST /calcul.php HTTP/1.1" 200 438 "http://lamaranmaiaf.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36" 0.057
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:50 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.331
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:50 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 0.960
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:51 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.365
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:51 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 0.882
www.monsite.com 82.146.44.148 - - [18/Feb/2016:02:54:51 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57012 "-" "-" 1.485
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:51 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.475
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:52 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 1.143
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:52 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.363
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:53 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 0.831
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:53 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.365
www.monsite.com 82.146.44.148 - - [18/Feb/2016:02:54:53 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57012 "-" "-" 1.845
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:54 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.334
alteora.machin-virtuel.biz 86.66.32.177 - - [18/Feb/2016:02:54:54 +0100] "POST /modules/screensaver/rech_next_media.php HTTP/1.1" 200 156 "http://alteora.machin-virtuel.biz/modules/screensaver/index_ssaver.php" "Mozilla/5.0 (Windows NT 6.1; rv:41.0) Gecko/20100101 Firefox/41.0" 0.044
www.site44.com 109.228.22.171 - - [18/Feb/2016:02:54:54 +0100] "POST /xmlrpc.php HTTP/1.0" 200 57292 "-" "-" 0.941
grep POST /var/log/nginx.access.log
Nginx
- reverse-proxy partout
- gestion directe du contenu statique
Apache / PHP
Apache / PHP
Apache / PHP
Apache / PHP
Nginx
NFS (NetApp)
OpenResty
- = Nginx + modules
- Lua
- soutenu par Cloudflare
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 80;
location / {
default_type text/html;
access_by_lua_block {
-- check the client IP address is in our black list
if ngx.var.remote_addr == "132.5.72.3" then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
-- check if the URI contains bad words
if ngx.var.uri and
string.match(ngx.var.request_body, "evil")
then
return ngx.redirect("/terms_of_use.html")
end
-- tests passed
}
content_by_lua '
ngx.say("<p>hello, world</p>")
';
}
}
}
Blocage DNSBL
- base clé/valeur simple
- ajout d'entrée par API PW
- blocage site piraté et IP cliente
lua_shared_dict blacklist_cache 50m;
lua_package_path "/usr/local/openresty/lib/?.lua;;";
access_by_lua_file /etc/nginx/lua/blacklist.lua;
/etc/nginx/conf.d/blacklist.conf
local blacklist_cache = ngx.shared.blacklist_cache
function check_access()
ngx.var.blacklisted = ''
-- Avoid DNS lookups when address in cache
if blacklist_cache:get(ngx.var.remote_addr .. "_hostwl") then
return
end
if blacklist_cache:get(ngx.var.remote_addr .. "_wl") and blacklist_cache:get(ngx.var.host .. "_wl") then
return
end
if blacklist_cache:get(ngx.var.remote_addr .. "_bl") then
blacklist(false)
end
if blacklist_cache:get(ngx.var.host .. "_bl") then
blacklist_host(false)
end
local blacklisted_host = dnsbl_check_host(ngx.var.host,"web.dnsbl.pw")
if blacklisted_host == 0 then
whitelist_host()
else
blacklist_host(true)
end
local blacklisted_ip = dnsbl_check(ngx.var.remote_addr,"web.dnsbl.pw")
if blacklisted_ip == 0 then
whitelist()
else
blacklist(true)
end
end
check_access()
Gestion des logs
- rsyslog
- centralisation
- authentification source et destination
/var/log/nginx.log
rsyslog
rsyslog
/home/syslog/host/jerry/2016/02/16/nginx-access.log
rfc5424 / TLS
$InputFile
HekaD
- 1er essai avec Logstash 1.4 ... nope !
- projet Mozilla
- gestion de messages
- go + sandboxes en Lua
# Lecture fichier log de rsyslog
[SyslogFileInput]
type = "LogstreamerInput"
log_directory = "/home/syslog/global/"
file_match = 'syslog-(?P<Year>\d+)-(?P<Month>\d+)-(?P<Day>\d+)-(?P<Hour>\d+)\.log'
priority = ["Year", "Month", "Day","Hour"]
decoder = "JsonSyslogDecoder"
# Décodage
[JsonSyslogDecoder]
type = "SandboxDecoder"
memory_limit = 64384000
instruction_limit = 100000000000
filename = "lua_decoders/syslog_pw.lua"
[JsonSyslogDecoder.config]
type = "syslog"
# Encodage pour ES
[ESJsonEncoder]
index = "%{Type}-%{%Y.%m.%d}"
es_index_from_timestamp = true
type_name = "%{Type}"
fields = ["Timestamp","Payload","Severity","Fields","Hostname"]
[ESJsonEncoder.field_mappings]
Timestamp = "timestamp"
Severity = "severity"
Payload = "message"
Hostname = "hostname"
[ElasticSearchOutput]
message_matcher = "Type == 'syslog'"
server = "http://es1:9200"
flush_interval = 10000
flush_count = 1000
http_timeout = 10000
connect_timeout = 10000
encoder = "ESJsonEncoder"
function process_message()
local payload = read_message("Payload")
json = syslog_grammar:match(payload)
msg.EnvVersion = 1
msg.Severity = json.pri.severity
msg.Hostname = json.hostname
msg.Timestamp = json.timestamp
tenant, tenant_id = pwlog.get_tenant(msg.Hostname)
msg.Fields["tenant"] = tenant
msg.Fields["appname"] = json["app-name"]
msg.Fields["syslog_pid"] = tonumber(json["procid"])
http_access.extract_fields(msg)
apache_error.extract_fields(msg)
mail_dovecot.extract_fields(msg)
mail_exim.extract_fields(msg)
mail_spamd.extract_fields(msg)
cron.extract_fields(msg)
ssh_shell.extract_fields(msg)
ftpd.extract_fields(msg)
if msg.Fields["remoteAddr"] then
res = geoip_country_lookup:query_by_addr(msg.Fields["remoteAddr"])
msg.Fields["remoteCountry"] = res["code"]
end
inject_message(msg)
end
- message immuable une fois décodé
- communication par injection de messages
www.aabb.fr 189.89.5.120 - - [18/Feb/2016:02:54:46 +0100] "POST /administrator/index.php HTTP/1.1" 200 5005 "-" "-" 0.427<134>1 2016-02-17T17:00:19.792783+01:00 jerry nginx-access - -
[7edd1926-ad91-414b-b09e-8bbb1880c08a@34200 tag="nginx"] www.monclub-forme.com
213.62.432.223 - - [17/Feb/2016:17:00:12 +0100] "GET /faq-monclub.htm
HTTP/1.1" 200 9834 "http://www.monclub-forme.com/contacts-monclub.htm"
"Mozilla/5.0 (Windows NT 6.1; rv:44.0) Gecko/20100101 Firefox/44.0" 0.066
{
"timestamp":"2016-02-17T16:00:12",
"message":"www.monclub-forme.com 213.62.432.223 - - [17/Feb/2016:17:00:12 +0100] \u0022GET /faq-monclub.htm HTTP/1.1\u0022 200 9834 \u0022http://www.monclub-forme.com/contacts-monclub.htm\u0022 \u0022Mozilla/5.0 (Windows NT 6.1; rv:44.0) Gecko/20100101 Firefox/44.0\u0022 0.066",
"severity":6,
"http_userAgent":"Mozilla/5.0 (Windows NT 6.1; rv:44.0) Gecko/20100101 Firefox/44.0",
"http_vhost":"www.monclub-forme.com",
"http_uri":"/faq-monclub.htm",
"http_status":200,
"http_outBytes":9834,
"http_referer":"http://www.monclub-forme.com/contacts-monclub.htm",
"user":"sitemonclub",
"remoteAddr":"213.62.432.223",
"appname":"nginx-access",
"http_protocol":"HTTP/1.1",
"http_host":"www.monclub-forme.com",
"tenant":"mutupw",
"http_method":"GET",
"remotePort":0,
"http_requestTime":0.066,
"http_remoteUser":"",
"remoteCountry":"FR",
"hostname":"jerry"
}
Detection et blocage
[CheckWebActivity]
type = "SandboxFilter"
ticker_interval = 5
message_matcher = "Fields[appname] == 'nginx-access'"
filename = "lua_filters/check_web_activity.lua"
preserve_data = true
[CheckWebActivity.config]
preservation_version = 6
max_duration = 7200
### Heka ne gère pas les tableaux dans la conf des sandbox Lua !! codé en dur dans le script lua
countries_good = ['BE','LU','GP','TN','RE','CH','DZ','IT']
countries_bad = ['TR','RU','UA','CN','AR','BY','NL']
whitelist_ips = ['79.99.16','82.223.72.5', '78.64.152.153', '83.153.215.142', '78.273.72.25']
protect_uri = ['/wp-login.php', '/wp-comments-post.php', 'ucp.php?mode=login','/administrator/','/login.php?action=in', '/xmlrpc.php', 'plugin_googlemap2_proxy.php']
function process_message()
local limit = 50
for i,v in ipairs(countries_bad) do
if v == ip_country then
limit = limit / 2
end
end
if http_protocol == "HTTP/1.0" then
limit = limit / 10
end
if numpost >= limit then
add_to_payload(" BLOCKED")
pwlog.blacklist_ip(ip,"web")
end
inject_payload("txt")
return 0
end
Openresty <3
- Software Defined Server
- gestion HTTP et maintenant TCP
- validation automatique Let's Encrypt
- SSL dynamique
function get_cert()
local certs = ngx.shared.certs
local certs_data = ngx.shared.certs_data
ssl.clear_certs()
key = certs:get(server_name .. "_k")
cert = certs:get(server_name .. "_c")
if key ~= nil and cert ~= nil then
else
key = "/etc/ssl/private/default.key"
cert = "/etc/ssl/certs/default.pem"
end
local ok, err = ssl.set_der_cert(ssl.cert_pem_to_der(certs_data:get(cert)))
local ok, err = ssl.set_der_priv_key(certs_data:get(key))
end
function load_certs()
local json = require "cjson"
local certs = ngx.shared.certs
local certs_data = ngx.shared.certs_data
f = io.popen("/etc/nginx/lua/get_certs.py","r")
line = f:read ("*l")
local data = json.decode(line)
for server_name,v in pairs(data.hosts) do
local success, err, forcible = certs:set(server_name .. "_k", data.hosts[server_name].key_file,0)
success, err, forcible = certs:set(server_name .. "_c", data.hosts[server_name].cert_file,0)
end
for cert_file,der in pairs(data.certs) do
local success, err, forcible = certs_data:set(cert_file, ngx.decode_base64(der),0)
end
end
if ngx.get_phase() == "init" then
load_certs()
else
get_cert()
end
Hekad ++
Questions
On recrute aussi !
Paris / Rennes
→ http://pw.fr
Blocage dynamique d'URLs à base de NGINX, HekaD et Lua
By Frédéric VANNIÈRE
Blocage dynamique d'URLs à base de NGINX, HekaD et Lua
Firewall HTTP déporté basé sur une blacklist pour bloquer les sites et clients suivant des motifs dans les logs.
- 2,121