1

Instead of using web.config for my rewrite rules, I have been using Helicon APE for emulating Apache's .htaccess feature.

I have a large universal .htaccess file that is applied across the server as part of a feature to restrict access to passing commands like union, delete etc..

However, I'm running into an issue where a line of the code is creating a false positive and blocking access to an otherwise proper URL.

The URL that is failing is: http://example.com/union-contracts

Using the below command in web.config, all missing URLs are pushed to index.php.

   <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/index.php" responseMode="ExecuteURL" />
    </httpErrors>

htaccess rule that is returning 403

RewriteCond %{QUERY_STRING} (;|<|>|'|"|\)|%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) [NC]
RewriteRule . - [F,L]

Ultimately, I would like to continue to block union for example, I just need to modify the line for false positives. Please note that I'm not looking for -contracts to be hardcoded in my .htaccess rule either.

I was able to duplicate the problem in htaccess in XAMPP after remembering how the URL is being generated in the first place.

On the visitor's side of the website, when a URL is accessed the server generates a 404 error. I filter out the URL to just the last part and I check the database against that string to see if it matches a saved URL, if so I don't pass on that 404 to the browser, it only gets passed if the URL can't be found.

When IIS generates a 404 error the URL becomes:

index.php?404;http://example.com:80/union-contracts

But it isn't passed onto the browser in that format.

imvain2
  • 123
  • 4
  • There must be "something else" going on here, another rewrite or something? Or another directive is blocking the request? If Helicon-APE trips on _that_ rule given the stated request only then it is seriously flawed (and I don't think it is). Your rewrite ("for a XAMPP test") also won't match the given regex on _that_ rule - so something else must also be happening here. That regex does not simply match the word "union" in the query string. There is an alternation block at the start of the regex, of which one _must_ match, before the word "union". – MrWhite Dec 28 '20 at 22:47
  • @MrWhite, that is exactly what I thought too. I limited it to that condition because the entire condition block uses the OR flag for each condition and when I comment out that condition, the URL works. I'm not worried about the XAMPP test, I was just seeing if I could duplicate the problem outside of IIS but I'm not worried about getting that to work. – imvain2 Dec 28 '20 at 23:08
  • So, your "XAMPP test" didn't actually trigger the 403? – MrWhite Dec 28 '20 at 23:48

1 Answers1

1

I've expressed my concern in comments that there must be "something else" going on, but anyway...

It's difficult to write an exception for a rule that shouldn't (read: "can't") be triggered in the first place - it's complete guess work. After all, why would the exception be honoured?

That rule should already avoid the given request for a couple of "big" reasons:

  1. The request does not include a query string.
  2. The regex in the condition (CondPattern) does not match any part of URL (even if it was in the query string).

Considering #2 above, the regex already contains a sufficient exception not to match the given request, so how can we implement "another" exception?

You could modify the CondPattern to avoid instances when a hyphen (-) follows the word "union", using a negative lookahead. For example:

(;|<|>|'|"|\)|%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)

So, the above matches Xunion, but not Xunion-, where X is one of the character sequences from the first alternation group (ie. (;|<|>|'|"|\)|%0A|%0D|%22|%27|%3C|%3E|%00)).

Or, when a hyphen follows any of these keywords:

(;|<|>|'|"|\)|%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)(?!-)


(UPDATE: The following two suggestions probably won't work, since it seems that the directives are only processed after IIS has already rewritten the URL. At which point the URL-path is simply /index.php, not /union-contracts.)

Or, modify the RewriteRule so that it does not match when the URL-path starts with /union- (although this potentially provides a backdoor for the XSS attempt the rule is trying to block). For example:

RewriteRule !^/?union- - [F]

(On Apache at least, the L flag is not required when the F flag is used - it is implied.)

Alternatively, you create another rule at the top, before your existing rules, that creates an exception for when requesting a URL that starts /union- and does not have a query string (although that would seem to be a contradiction, if the other rule is to be believed). For example:

RewriteCond %{QUERY_STRING} ^$
RewriteRule ^/?union- - [L]

However, this will potentially "break" the request if you have other rules later in the file that need to rewrite the URL in some way - since these will not occur.

I'm assuming Helicon-APE/mod_rewrite works in a directory-like context (eg. .htaccess style syntax as opposed to server or virtualhost context)? Although that should not affect the directives above, as it happens.


UPDATE: When IIS generates a 404 error the URL becomes:

index.php?404;http://example.com:80/union-contracts

Ah, yes! That URL will get caught by the original condition. So, it seems the mod_rewrite directives are processed after the IIS "rewrite" in web.config! This does make these "security" directives somewhat superfluous or even incorrect, since they are supposed to be run on the initial request from the client. And....

I filter out the URL to just the last part and I check the database against that string to see if it matches a saved URL

Since you are actively parsing the URL and filtering out just the part you require then these "security" checks would seem to be redundant anyway for these "404" requests at least. (In fact, depending on the real files (ie. non-404 requests) that could potentially be requested directly, could a malicious query string of this nature realistically pose a security threat for any URL?)

The two suggestions above to modify the regex in the condition to exclude instances when a hyphen follows the "union" keyword, or any keyword, would still apply here and would seem to solve the immediate problem.

You could also abort all checks when a URL of the form index.php?404; is encountered (ie. after it has been rewritten by IIS). (I'm assuming all your mod_rewrite directives are similar "security" checks?)

For example, add the following to the top of the .htaccess script, before the existing directives:

RewriteCond %{QUERY_STRING} ^404;
RewriteRule ^/?index\.php - [L]

This should prevent any further processing when a URL of the form /index.php?404;...... is encountered. (Since you are parsing the URL anyway in your script.)

MrWhite
  • 12,647
  • 4
  • 29
  • 41
  • Thank you for your assistance. I haven't implemented any of your recommendations but I did find out a very important detail that I had overlooked before that allowed me to duplicate the problem on XAMPP. I looked so hard at the rule that I forgot about how IIS generates URLS for 404 errors (which is how I'm handling URLs) but I'm not returning to the browser. The actual URL is `http://example.com/index.php?404;http://example.com:80/union-contracts` – imvain2 Dec 29 '20 at 17:06
  • Ah, thanks for the update. Yes, that will indeed match that _condition_! I've updated my answer. – MrWhite Dec 30 '20 at 01:23
  • From your update it seems the mod_rewrite directives are processed _after_ IIS has performed the internal rewrite. (Or, is it processed both before _and_ after?!) Just wondering... do you have any control over the order of processing? – MrWhite Dec 30 '20 at 02:07