Ils l’ont fait
Ca y est, enfin Mr BlueCoat qui vend quand même des jouets dit “Professionnels” de reverse et proxy HTTP (Comprenez de 3000 à 100000€ la boite) vient de sortir ses premières “fonctions” WAF. Et oui il aura fallu attendre fin 2012 pour que ce magnifique “player” du Magic Quadrant de mes couilles se hisse au rang de Web Application Firewall. Qui ne se rappelle pas des merveilleuses promesses que laisse présager la plaquette commerciale:
Web Application Firewall For Untrusted Web Environments-Aug11-finalv2
Si les fonctionnalités de la chose en tant que reverse proxy sont indéniables, son ‘WAF’ flambant neuf est affligeant, pathétique, grotesque, lamentable, immature… Les adjectifs me manquent. Mais regardons plutôt la release note.
Et oui, vous ne revez pas en gise de “WAF” on vous préconise d’aller voir 2 articles de la knownledge base. Et voila, le “WAF” tant attendu vous promettant de bloquer XSS et SQLi n’est qu’un pauvre jeu de regex à installer soi-même dans la configuration de la bête.
https://kb.bluecoat.com/index?page=content&id=FAQ2147
https://kb.bluecoat.com/index?page=content&id=FAQ2148
Mais regardons déja celles préconisées pour les SQLi.
SQLi (Super Qualitée du Layer Inefficace)
<proxy>
condition=sql_injection_attack_pattern deny
allow
define condition sql_injection_attack_pattern
; Simple detection of main 4 keywords followed by a space character
url.query.regex="(select|insert|update|delete)\+"
; Use of > or < operators (for attacks like "or 2 > 1") (hex or ascii)
url.query.regex="(>|<)"
; Use of the hex "=" (for attacks like "or 1 = 1"). We only check hex for now
; as we don't want to fail on normal query strings like "www.google.com?search=sql"
raw_url.query.regex="%3D" ; hex encoded "="
; Detect References to well known database names
url.query.regex="(Tempdb|master|Model|MsDB|dbo)\."
url.query.regex="\[(Tempdb|master|Model|MsDB|dbo)\]\."
; Detect SQL ALTER commands
; ";" + zero or more spaces + "ALTER" + 1 or more spaces + known parameters + space:
url.query.regex=";\+*ALTER\++(CLUSTER|DATABASE|DIMENSION|DISKGROUP|FUNCTION|INDEX|INDEXTYPE|JAVA|MATERIALIZED\++VIEW|OPERATOR|OUTLINE|PACKAGE|PROCEDURE|PROFILE|RESOURCE\++COST|ROLE|ROLLBACK\++SEGMENT|SEQUENCE|SESSION|SYSTEM|TABLE|TABL ESPACE|TRIGGER|TYPE|USER|VIEW)\+"
; Detect SQL CREATE commands
; ";" + zero or more spaces + "CREATE" + 1 or more spaces + known parameters + space:
url.query.regex=";\+*CREATE\++(CLUSTER|CONTEXT|CONTROLFILE|DATABASE|DIMENSION|DISKGROUP|DIRECTORY|FUNCTION|INDEX|INDEXTYPE|JAVA|LIBRARY|MATERIALIZED\++VIEW|OPERATOR|OUTLINE|PACKAGE|PFILE|PROCEDURE|PROFILE|ROLE|ROLLBACK\++SEGMENT|SCHEM A|SEQUENCE|SPFILE|SYNONYM|TABLE|TABLESPACE|TEMPORARY\++TABLESPACE|TRIGGER|TYPE|USER|VIEW)\+"
; Detect SQL DROP commands
; ";" + zero or more spaces + "DROP" + 1 or more spaces + known parameters + space:
url.query.regex=";\+*DROP\++(CLUSTER|CONTEXT|DATABASE|DIMENSION|DIRECTORY|DISKGROUP|FUNCTION|INDEX|INDEXTYPE|JAVA|LIBRARY|MATERIALIZED\++VIEW|OPERATOR|OUT LINE|PACKAGE|PROCEDURE|PROFILE|ROLE|ROLLBACK\++SEGMENT|SEQUENCE|SYNONYM|TABLE|TA BLESPACE|TRIGGER|TYPE|USER|VIEW)\+"
; Detect SQL GRANT commands
; ";" + zero or more spaces + "GRANT" + 1 or more spaces + any characters (except & which would be the start of a new url query parameter) + 1 or more spaces + "TO"
url.query.regex=";\+*GRANT\+[^&]+\++TO\+"
; Detect SQL LOCK commands
; Lock table is the only lock command I found in Oracle (and I found no mention in SQL Server).
url.query.regex=";\+*LOCK\++TABLE\+"
; Detect SQL EXEC/EXECUTE commands
; Prevent exec statements
url.query.regex=";\+*EXEC(UTE)?\++"
; Prevent commonly named stored procedures (which don't always require the EXECUTE statement to run)
url.query.regex=";\+*(sp|xp)_"
; Detect SQL RELOAD commands
; RELOAD - have the database engine re-read the grant tables. Note: Unable to find syntax in online search just a mention of the command
url.query.regex=";\+*RELOAD\+"
; Detect SQL Server SHUTDOWN commands
url.query.regex=";\+*SHUTDOWN"
; Need to get rid of SQL declare/set statements or it is easy to bypass our protection
; i.e.
; DECLARE @x varchar(1000);
; DECLARE @y varchar(1000);
; DECLARE @stmt varchar(1000);
; SET @x="dro";
; SET @y="p table users";
; SET @stmt="@x@y";
; exec (@stmt)
url.query.regex=";\+*(DECLARE|SET)\++@"
end
Première chose à signaler, mais la knowledge base le signale bien et ne le cache pas. Cela ne protège ni les posts, ni les cookies. En fait le test qui est fait est “url.query.regex=” cela veut dire que cela ne testera que la partie query d’une requête. Un petit exemple
http://thanatos.trollprod.org/dummy.php?param=1
Seule la partie en gras (?param=1) sera passée à la moulinette. le Constat est accablant, en plus des POSTs non traités, cela ne protège pas non plus les sites web RESTFull et bien sur aucun des headers présentés par le client.
Mais continuons, peut-on en plus quand même injecter dans un GET normal avec des paramêtres ?
Déjà, la majeure partie de la policy de sécurité se focalise sur du MSSQL, car tout est fait pour l’inclusion de commande après une requête ( première requête…; et seconde SQLi) et des commandes MSSQL, si vous avez autre chose, passez votre chemin. Ça ne marche pas sur MySQL ou ORACLE cela.
url.query.regex=";\+*RELOAD\+"
Premier TIPS certes un Bluecoat fait de base ses regexes de facons insensible à la casse, mais il ne connais pas l’UTF, ni L’UNICODE, il ne sait décoder que l’URL Encoding. Je vois déja les premiers qui sourient dans le fond. Et oui pour injecter dans du MSSQL cela ne sert pas a grand chose, on a l’embarras du choix pour contourner toutes ces regexes.
%S%i% %v%o%u%s% %v%o%y%e%z% %c%e% %q%u%e% %j%e% %v%e%u%x %d%i%r%e% %!
Mais, ensuite, pour les autre gens, ceux avec du MySQL ou autre, il reste que 2 éceuils.
Premièrement tout “=” URL encodé en ‘%3D’ est bloqué ainsi que < et > dans toutes ses formes.
; Use of the hex "=" (for attacks like "or 1 = 1"). We only check hex for now
; as we don't want to fail on normal query strings like "www.google.com?search=sql"
raw_url.query.regex="%3D" ; hex encoded "="
; Use of > or < operators (for attacks like "or 2 > 1") (hex or ascii)
url.query.regex="(>|<)"
Je rappelle qu’il est possible de placer une SQLi sans utiliser un ‘=’ ou un ‘<‘ et ‘>’. Rappellez vous toujours les alternatives qui existent pour blind SQLi, soyez inventifs:
and 1
or 1
and 1=1
and 2<3
and 'a'='a'
and 'a'<>'b'
and char(32)=' '
and 3<=2
and 5<=>4
and 5<=>5
and 5 is null
or 5 is not null
Et en plus, un opérateur comme “LIKE” “RLIKE” fait aussi l’affaire, il y a même un tamper SQLMap pour cela.
https://github.com/sqlmapproject/sqlmap/blob/master/tamper/equaltolike.py
Deuxième souci, le select est attrapé:
define condition sql_injection_attack_pattern
; Simple detection of main 4 keywords followed by a space character
url.query.regex="(select|insert|update|delete)\+"
Pour comprendre le ‘+’ à la fin de la première regex, il faut savoir qu’un BlueCoat SG n’url décode pas tout comme il devrait (Pour des raisons de sécurité dit-on, moi je pense à de la fénéantise du coder qui n’aime pas parser)
un ‘%20’ (Espace) est converti en ‘%2B’ (+) dans son preprocesseur. Ce qui rend la regex plus compréhensible.
select(space) ou select(+) ou insert(space) etc...
Malheureusement c’est sans compter ce fameux décodeur d’URL.. il fait d’autres choses plus drôles encore, de ‘%00’ à ‘%1F’ ce n’est pas décodé du tout, ni transformé en ‘+’ d’ailleur. On se retrouve donc avec des url soit disant décodée mais pas complêtement.
Par exemple un ‘select%0A’ (CR) ne matche plus la patterne et pourtant c’est bien une requête MySQL valide.
il y a aussi un tamper SQLMap pour cela
https://github.com/sqlmapproject/sqlmap/blob/master/tamper/space2mysqlblank.py
Allez on a constaté l’affligeance du filtre SQLi, regardons les filtres XSS
XSS (Cross Site Stupide)
<proxy>
condition=xss_attack_pattern_in_url deny
allow
define condition xss_attack_pattern_in_url
; Pattern: "<" + zero or more spaces + "known xss attack tags" + optional attributes + ">"
; This will detect:
; "<script>", "< script >", "<script src="...">"
; But will not consider the following as attacks:
; "<scripty>", "script", "<script", "script>"
url.query.regex="<\+*(script|object|applet|embed)(\++[^>]*)?>"
end
Ha bin non, pas les filtres, mais LE filtre ! Là aussi dans la Knowledge base est rigolote, il disent au hacker qu’il peut utiliser “body onload”. Mais je trouve qu’ils pourraient donner plus de tips. Quid de Iframe, OnError, OnMouse…, et là aussi dans certain navigateurs <‘TAB’script çà gagne, et ‘TAB’ c’est ‘%09’, merde ca matche pas non plus la patterne ;) Si on active SQLi avec il y a aussi une rêgle qui bloque les ‘<‘ et ‘>’ et qui complique. Cela sera certes efficace pour votre petit site wordpress, mais je défie quiconque de tenir cette rêgle sur un environnement un peu plus velus (et je rappelle c’est pas donné ce joujou donc c’est pas pour un pauvre wordpress).
Mot Maux final
Pensons aussi à tous ceux non spécialistes qui vont implémenter ces rêgles telles quelles. Dans une rule proxy bluecoat DENY laissera passer si un autre layer proxy dispose d’une rêgle qui accepte quelque chose. C’est FORCE_DENY qu’il faudrait mettre pour parer à toute erreur de configuration.
Bref, on est quasiment en 2013, les commerciaux vont commencer à vous dire que BlueCoat intègre désormais un WAF. Ne les croyez pas ! Premièrement parce que le niveau est largement bien en dessous de ce qu’on peut attendre d’un WAF dit ‘Professionnel’. Secundo parce que c’est à des années lumières de ce que fait la concurrence, et même pour un WAF négatif; Tertio parce que en production dans la vraie vie, je ne souhaite à personne de devoir gérer les faux positifs et les exceptions dans des conditions aussi spartiates.
Et rappelez vous l’adage “Ce n’est pas en les bloquant qu’on les arrêtera !”
A+