iisurl-rewritingsql-injectionrequestfiltering

Using IIS url rewrite to protect against SQL injection


We use a variety of CMS (Wordpress, Joomla) on our server and therefore a lot of the code is third-party so we have no control over quality. Whilst we always keep everything up-to-date we recently had a website that was hacked.

To try and prevent this from happening in the future i've added the URL rewrite rules into web.config files. But I have a couple of questions:

  1. Should I use RequestFiltering instead of Query_String to detect injections? Or is there no difference?

  2. Is web.config cached by IIS or is it like Apache .htaccess and queried on every single request? If so, will this be a performance drain?

  3. Have I missed any possible attacks?

CODE...

<rule name="Injection Blocking">
     <match url="^(.*)$" ignoreCase="false" />
     <conditions logicalGrouping="MatchAny">
       <add input="{HTTP_USER_AGENT}" pattern="(havij|libwww-perl|wget|python|nikto|curl|scan|java|winhttp|clshttp|loader)" />
       <add input="{HTTP_USER_AGENT}" pattern="(%0A|%0D|%27|%3C|%3E|%00)" />
       <add input="{HTTP_USER_AGENT}" pattern="(;|&lt;|>|'|&quot;|\)|\(|%0A|%0D|%22|%27|%28|%3C|%3E|%00).*(libwww-perl|wget|python|nikto|curl|scan|java|winhttp|HTTrack|clshttp|archiver|loader|email|harvest|extract|grab|miner)" />
       <add input="{THE_REQUEST}" pattern="(\?|\*|%2a)+(%20+|\\s+|%20+\\s+|\\s+%20+|\\s+%20+\\s+)HTTP(:/|/)" />
       <add input="{THE_REQUEST}" pattern="etc/passwd" />
       <add input="{THE_REQUEST}" pattern="cgi-bin" />
       <add input="{THE_REQUEST}" pattern="(%0A|%0D|\\r|\\n)" />
       <add input="{URL}" pattern="owssvr\.dll" />
       <add input="{HTTP_REFERER}" pattern="(%0A|%0D|%27|%3C|%3E|%00)" />
       <add input="{HTTP_REFERER}" pattern="\.opendirviewer\." />
       <add input="{HTTP_REFERER}" pattern="users\.skynet\.be.*" />
       <add input="{QUERY_STRING}" pattern="[a-zA-Z0-9_]=http://" />
       <add input="{QUERY_STRING}" pattern="[a-zA-Z0-9_]=(\.\.//?)+" />
       <add input="{QUERY_STRING}" pattern="[a-zA-Z0-9_]=/([a-z0-9_.]//?)+" />
       <add input="{QUERY_STRING}" pattern="\=PHP[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" />
       <add input="{QUERY_STRING}" pattern="(\.\./|%2e%2e%2f|%2e%2e/|\.\.%2f|%2e\.%2f|%2e\./|\.%2e%2f|\.%2e/)" />
       <add input="{QUERY_STRING}" pattern="ftp\:" />
       <add input="{QUERY_STRING}" pattern="http\:" />
       <add input="{QUERY_STRING}" pattern="https\:" />
       <add input="{QUERY_STRING}" pattern="\=\|w\|" />
       <add input="{QUERY_STRING}" pattern="^(.*)/self/(.*)$" />
       <add input="{QUERY_STRING}" pattern="^(.*)cPath=http://(.*)$" />
       <add input="{QUERY_STRING}" pattern="(\&lt;|%3C).*script.*(\>|%3E)" />
       <add input="{QUERY_STRING}" pattern="(&lt;|%3C)([^s]*s)+cript.*(>|%3E)" />
       <add input="{QUERY_STRING}" pattern="(\&lt;|%3C).*embed.*(\>|%3E)" />
       <add input="{QUERY_STRING}" pattern="(&lt;|%3C)([^e]*e)+mbed.*(>|%3E)" />
       <add input="{QUERY_STRING}" pattern="(\&lt;|%3C).*object.*(\>|%3E)" />
       <add input="{QUERY_STRING}" pattern="(&lt;|%3C)([^o]*o)+bject.*(>|%3E)" />
       <add input="{QUERY_STRING}" pattern="(\&lt;|%3C).*iframe.*(\>|%3E)" />
       <add input="{QUERY_STRING}" pattern="(&lt;|%3C)([^i]*i)+frame.*(>|%3E)" />
       <add input="{QUERY_STRING}" pattern="base64_encode.*\(.*\)" />
       <add input="{QUERY_STRING}" pattern="base64_(en|de)code[^(]*\([^)]*\)" />
       <add input="{QUERY_STRING}" pattern="GLOBALS(=|\[|\%[0-9A-Z]{0,2})" ignoreCase="false" />
       <add input="{QUERY_STRING}" pattern="_REQUEST(=|\[|\%[0-9A-Z]{0,2})" ignoreCase="false" />
       <add input="{QUERY_STRING}" pattern="^.*(\(|\)|&lt;|>|%3c|%3e).*" />
       <add input="{QUERY_STRING}" pattern="^.*(\x00|\x04|\x08|\x0d|\x1b|\x20|\x3c|\x3e|\x7f).*" />
       <add input="{QUERY_STRING}" pattern="(NULL|OUTFILE|LOAD_FILE)" ignoreCase="false" />
       <add input="{QUERY_STRING}" pattern="(\.{1,}/)+(motd|etc|bin)" />
       <add input="{QUERY_STRING}" pattern="(localhost|loopback|127\.0\.0\.1)" />
       <add input="{QUERY_STRING}" pattern="(&lt;|>|'|%0A|%0D|%27|%3C|%3E|%00)" />
       <add input="{QUERY_STRING}" pattern="concat[^\(]*\(" />
       <add input="{QUERY_STRING}" pattern="union([^s]*s)+elect" />
       <add input="{QUERY_STRING}" pattern="union([^a]*a)+ll([^s]*s)+elect" />
       <add input="{QUERY_STRING}" pattern="\-[sdcr].*(allow_url_include|allow_url_fopen|safe_mode|disable_functions|auto_prepend_file)" />
       <add input="{QUERY_STRING}" pattern="(;|&lt;|>|'|&quot;|\)|%0A|%0D|%22|%27|%3C|%3E|%00).*(/\*|union|select|insert|drop|delete|update|cast|create|char|convert|alter|declare|order|script|set|md5|benchmark|encode)" />
       <add input="{QUERY_STRING}" pattern="(sp_executesql)" />
    </conditions>
   <action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
</rule>

Solution

  • Essentially you're trying to write your own black list based primitive mini-WAF (web application firewall). From my experience, it is pointless to try to guess all possible attacks based solely on black list primitive signatures, it's a game you cannot win. For example your attempt to block XSS by blocking script and Iframe tags can be easily be bypassed in so many ways, some of it are just as simple as <img src='a' onerror=alert('xss')/>, and I haven't even stared mentioning the lower-case upper-case regex evasion that would bypass pretty much all of your anti-SQL Injection attempts.

    Web application firewalls are products that worth millions of dollars, they keep a small army of investigators that try to update their signatures and also put great effort in behavior analysis and white-list based learning mode which increases the security level dramatically, and you can't really compare your nice try to their products. You'll probably stop stop script kiddies, make a pro hacker to sweat a bit more but you won't stop attackers based on this defense.

    My recommendation, instead of try to do it yourself, buy one of the major WAFs (F5 ,Imperva) or if you can't afford it just search for the right free IIS based WAF for you and install it. It would probably do a better job than you - no offend. Speaking of free WAFs, I'm familiar with the good old ModSecurity which apparently has an IIS version too.

    Good luck!