Classic ASP is the old server-side web scripting technology based on VBScript, now superseded by ASP.NET, which lots of developers, including myself, learned to hate in the nineties when, for mysterious reasons, a certain customer decided he needed the whole "Enterprise" Microsoft 3-tiers stack (IIS/COM+/SQL Server). Luckily enough, nobody asks you to build anything new using ASP these days (even though there's always some insanely unmaintainable VBScript code out there which badly needs maintenance), but this technology, albeit agonizing, yet found a way to come back and make me sad again.

Some days ago this blog post, talking about a bypass method for NoScript's Anti-XSS filter, called for my attention (not thanks to its author).

Even though it's not very clear from that piece of writing, the issue at hand is quite simple but, in my opinion, outrageously stupid and annoying. I'm gonna call it "HomoXSSuality" (even though most LGBT people I know is neither simple, nor stupid nor annoying), because homoglyps and homophones conspire to make XSS (and SQL injection) attacks easier to pull.

Like any other server-side web programming framework, ASP gives developers some means to extract "parameters" (name/value pairs) from the HTTP requests, stored either in the query string or in the POST data. For instance, if an ASP script is invoked using the URL, parameters can be extracted by code like this:

Dim Name, Hero
Name = Request("name")
Hero = Request("hero")

At runtime, the Name variable will contain "Giorgio Maone", while Hero will be set to "Ὑπατία". This contrived example show also how "special" characters, such as space or Greek alphabet letters, are escaped by standard percent encoding, i.e. by taking the UTF-8 hexadecimal representation of the string and prefixing each byte with a "%" character: specifically, “ â€ translates to “%20”, and "Ὑπατία" to "%E1%BD%99%CF%80%CE%B1%CF%84%CE%AF%CE%B1". This is the translation you can obtain from the encodeURIComponent() ECMAScript function, and the recommended way of escaping URLs.
An older and never standardized method, implemented by the now deprecated JavaScript escape() function, produces more or less the same output for ASCII strings, but uses the UTF-16 representation prefixed with "%u" for higher (beyond ASCII) Unicode strings: for instance, “ â€ still stays “%20”, but "Ὑπατία" becomes "%u1F59%u03C0%u03B1%u03C4%u03AF%u03B1".

NoScript's Anti-XSS filter, while processing HTTP requests, does recognizes and properly handle both these encoding styles, and many more. Any web security filter should be able to do it, because web applications usually consume data that has been automatically decoded by their runtime environment.

But Classic ASP adds a perverse twist to its parameter decoding routines. The Request() API apparently assumes that developers and/or browsers and/or users are too stupid to handle non-ASCII Unicode characters (e.g. greek alphabet letters) by themselves, thus it tries to protect them from such execrable things by automatically translating any non-ASCII character into the ASCII counterpart which resembles it the most; when no suitable replacement can be picked, with either "?" or "�" (arbitrarily, it seems). So "%u1F59%u03C0%u03B1%u03C4%u03AF%u03B1", rather than "Ὑπατία", becomes a quite ugly "?pat?a". As you can see, while the replacement choice is mainly homoglyphic (α→a, τ→t), it may also follow homophonic criteria (π→p).

To figure out the whole range of Unicode-ASCII transliterations performed by ASP, I needed to write an ad hoc program mixing VBScript and JavaScript, and I also used it to automatically generate the ASPIdiocy.js mappings file that can be found in recent NoScript packages.

A short essay here, to give you just a taste of this madness:

(0x100) ~= A(0x41)
ā(0x101) ~= a(0x61)
Ä‚(0x102) ~= A(0x41)
ă(0x103) ~= a(0x61)
Ä„(0x104) ~= A(0x41)
Ä…(0x105) ~= a(0x61)
Ć(0x106) ~= C(0x43)
ć(0x107) ~= c(0x63)
Ĉ(0x108) ~= C(0x43)
ĉ(0x109) ~= c(0x63)
ÄŠ(0x10a) ~= C(0x43)
Ä‹(0x10b) ~= c(0x63)
Č(0x10c) ~= C(0x43)
č(0x10d) ~= c(0x63)
ÄŽ(0x10e) ~= D(0x44)
ď(0x10f) ~= d(0x64)
Đ(0x110) ~= �(0xfffd)
Ä‘(0x111) ~= d(0x64)
Ä’(0x112) ~= E(0x45)
Ä“(0x113) ~= e(0x65)
Ä”(0x114) ~= E(0x45)
Ä•(0x115) ~= e(0x65)
Ä–(0x116) ~= E(0x45)
Ä—(0x117) ~= e(0x65)
Ę(0x118) ~= E(0x45)
Ä™(0x119) ~= e(0x65)
Äš(0x11a) ~= E(0x45)
Ä›(0x11b) ~= e(0x65)
Ĝ(0x11c) ~= G(0x47)
ĝ(0x11d) ~= g(0x67)
Äž(0x11e) ~= G(0x47)
ÄŸ(0x11f) ~= g(0x67)
Ä (0x120) ~= G(0x47)
Ä¡(0x121) ~= g(0x67)
Ä¢(0x122) ~= G(0x47)
Ä£(0x123) ~= g(0x67)
Ĥ(0x124) ~= H(0x48)
Ä¥(0x125) ~= h(0x68)
Ħ(0x126) ~= H(0x48)
ħ(0x127) ~= h(0x68)
Ĩ(0x128) ~= I(0x49)
Ä©(0x129) ~= i(0x69)
Ī(0x12a) ~= I(0x49)
Ä«(0x12b) ~= i(0x69)
Ĭ(0x12c) ~= I(0x49)
Ä­(0x12d) ~= i(0x69)
Ä®(0x12e) ~= I(0x49)
į(0x12f) ~= i(0x69)
Ä°(0x130) ~= I(0x49)
ı(0x131) ~= i(0x69)
Ä´(0x134) ~= J(0x4a)
ĵ(0x135) ~= j(0x6a)
Ķ(0x136) ~= K(0x4b)
Ä·(0x137) ~= k(0x6b)
ĸ(0x138) ~= ?(0x3f)
Ĺ(0x139) ~= L(0x4c)
ĺ(0x13a) ~= l(0x6c)
Ä»(0x13b) ~= L(0x4c)
ļ(0x13c) ~= l(0x6c)
Ľ(0x13d) ~= L(0x4c)
ľ(0x13e) ~= l(0x6c)
Ł(0x141) ~= L(0x4c)
Å‚(0x142) ~= l(0x6c)
Ń(0x143) ~= N(0x4e)
Å„(0x144) ~= n(0x6e)
Å…(0x145) ~= N(0x4e)
ņ(0x146) ~= n(0x6e)
Ň(0x147) ~= N(0x4e)
ň(0x148) ~= n(0x6e)
Ō(0x14c) ~= O(0x4f)
ō(0x14d) ~= o(0x6f)
ÅŽ(0x14e) ~= O(0x4f)
ŏ(0x14f) ~= o(0x6f)
Ő(0x150) ~= O(0x4f)
Å‘(0x151) ~= o(0x6f)
Å”(0x154) ~= R(0x52)
Å•(0x155) ~= r(0x72)
Å–(0x156) ~= R(0x52)
Å—(0x157) ~= r(0x72)
Ř(0x158) ~= R(0x52)
Å™(0x159) ~= r(0x72)
Åš(0x15a) ~= S(0x53)
Å›(0x15b) ~= s(0x73)
Ŝ(0x15c) ~= S(0x53)
ŝ(0x15d) ~= s(0x73)
Åž(0x15e) ~= S(0x53)
ÅŸ(0x15f) ~= s(0x73)
Å¢(0x162) ~= T(0x54)
Å£(0x163) ~= t(0x74)
Ť(0x164) ~= T(0x54)
Å¥(0x165) ~= t(0x74)
Ŧ(0x166) ~= T(0x54)
ŧ(0x167) ~= t(0x74)
Ũ(0x168) ~= U(0x55)
Å©(0x169) ~= u(0x75)
Ū(0x16a) ~= U(0x55)
Å«(0x16b) ~= u(0x75)
Ŭ(0x16c) ~= U(0x55)
Å­(0x16d) ~= u(0x75)
Å®(0x16e) ~= U(0x55)
ů(0x16f) ~= u(0x75)
Å°(0x170) ~= U(0x55)
ű(0x171) ~= u(0x75)
Ų(0x172) ~= U(0x55)
ų(0x173) ~= u(0x75)
Å´(0x174) ~= W(0x57)
ŵ(0x175) ~= w(0x77)
Ŷ(0x176) ~= Y(0x59)
Å·(0x177) ~= y(0x79)
Ÿ(0x178) ~= �(0xfffd)
Ź(0x179) ~= Z(0x5a)
ź(0x17a) ~= z(0x7a)
Å»(0x17b) ~= Z(0x5a)
ż(0x17c) ~= z(0x7a)
〈(0x2329) ~= <(0x3c)
〈(0x3008) ~= <(0x3c)
<(0xff1c) ~= <(0x3c)
ʹ(0x2b9) ~= '(0x27)
ʼ(0x2bc) ~= '(0x27)
ˈ(0x2c8) ~= '(0x27)
′(0x2032) ~= '(0x27)
'(0xff07) ~= '(0x27)

As you can see in the end, I could list 3 different homoglyphs for < (less than, ASCII 0x27) and 5 for ' (apostrophe, ASCII 0x3c). Anybody with a bit of familiarity with XSS or SQL injection has already guessed where I'm going...

Classic ASP translates the query string parameter value %u3008scr%u0131pt%u3009%u212fval(%uFF07al%u212Frt(%22XSS%22)%u02C8)%u2329/scr%u0131pt%u232A to


which, if echoed back, is executed as a JavaScript block by web browsers.

Any "sane" web server runtime (either a recent IIS with ASP.NET or Apache with PHP/Python/Ruby, or a Java Servlet Container, or you pick yours) either leaves the %u... stuff alone (because this escaping style is deprecated), or translates the whole into


which obviously has no other meaning than "funny text", to any decent web browser.

This undocumented (AFAIK) Classic ASP "feature" (which was sooo good and smart that Microsoft itself dropped it in ASP.NET) can severely screw up with any anti-XSS filter. It does with Google Chrome's, it does not with Microsoft IE8's (unsurprisingly, since the original mess came from Redmond), it does not anymore with NoScript's, since version 2.0.2rc2.

Of course, it may also be used to bypass Web Application Firewalls (WAFs), which, ironically enough, are often deployed to "virtually patch" XSS and SQL injection bugs in hardly maintainable applications, just like the ones developed with Classic ASP: this blog had been just created when it witnessed a tragicomic case involving the United Nations.

So, how many WAFs out there can actually resist when HomoXSSuality calls?

32 Responses to “Lost in Translation (ASP's HomoXSSuality)”

  1. #1 concerned citizen says:

    Homophobia isn't cool.

  2. #2 Giorgio says:

    @concerned citizen:
    lack of irony is worse. I suppose you did not notice ;)

  3. #3 kuza55 says:

    Nice work Giorgio, That's pretty lulz.

  4. #4 Fabio Zendhi Nagao (nagaozen) says:

    Thanks for pointing this bug. There's an easy fix for this issue, but will need a pretty decent code management tool.

    Just replace Request.QueryString to AXE_GET. Here's the AXE_GET definition

  5. #5 Fabio Zendhi Nagao (nagaozen) says:

    Sorry for posting again, because reCaptcha doesn't allow code, I wrote a blog post about how to fix this. Check it at

  6. #6 Soroush Dalili says:

    I think we agree in one idea:
    Nice job.

  7. #7 meh says:

    You probably didn't mean it homophobic yet it comes across as such (yes I saw your reply and didn't get the significance.

    You should at least consider changing that name no matter how clever and funny you think it is.

  8. #8 Stephen says:

    As a butt-fucking queer I thoroughly support this blog post.

  9. #9 Giorgio says:

    @Soroush Dalili:
    Your article was already linked in my post.

    The significance was two clicks away.
    Could you elaborate why exactly does my post sound homophobic? Where's is it insulting or negatively connoting homosexual persons?

  10. #10 Soroush Dalili says:

    Sorry about that. I did not pay attention to it.

  11. #11 meh says:


    If you cannot see why someone could interpret "homoXSSuality" as a bit homophobic then I think you're naive.

    The reason is very simple:
    You write very negatively about something then naming it very similarly to "homosexuality" thus creating a link between all the negativity and homosexuality.

    The name being so similar creates this insinuation.

    Going further (and hopefully incorrectly), one can interpret the reason of the name to be that the writer assumed the readers thought "homosexuality == bad" and thus made a name close to homosexuality to transfer the reader's negative feelings towards homosexuality to homoXSSuality.

    Plus, your blog posts are shown on a planet and you can't expect everyone reading the planet to have read your previous posts or knowing your stances in matters like these or even read the post's comments.

  12. #12 Rodrigo Montoro (Sp0oKeR) says:

    Snort http_inspect unicode map has this table ready to normalize traffic. Take a look .

    Like for example ė(0x117) ~= e(0x65) at will be 117:65 .

    Brazilian guy wrote last year somethin pretty similar but in pt_BR about using something like séléct to bypass waf since application will normalize and send select to database in the backend.

    Nice work!

  13. #13 Giorgio says:

    Thanks for your kind explanation.
    OTHO, I would never give up a catchy pun like this to go after people totally lacking any sense of humor, irony and proportions. Fortunately all my LGBT acquaintances have these three qualities in great abundance.

    @Rodrigo Montoro (Sp0oKeR):
    Very interesting (and ironic), thanks.

  14. #14 Technogeek says:

    I can see (and somewhat agree with) the arguments against the name, and the "I have $MINORITY friends" defense has never exactly carried all that much weight. That said, given the nature of how the attack works, I can also see how it'd be hard to come up with a different nickname that's still as memorable.

    It'd probably be best to put the effort into it, though.

  15. #15 helmut says:

    About the name: it is based on homo (-glyphic, -phonic) and XSS.

    But to reduce the ofensive resemblance, what about


    This name adds the information that the "feature" cvonsitst in a insensitivity to (almost) homographic or homophonic characters. And it is distant enough to homosexuality, at least I feeel so (but English is not my mother tongue ...)


  16. #16 Chris says:

    Seems to be caught by ModSecurity's CRS FWIW

  17. #17 Giorgio says:

    Unfortunately this result is just a coincidence.
    That URL happens to trigger rules for different blacklisted issues.
    In fact, this simplified version of the same payload is not caught at all:

  18. #18 Joe Blow says:

    I don't know what self-annointed pseudo-psychologist first decided to chide those expressing or implying a distaste for homosexuality by calling them "homophobic", but every time I read or hear that asinine term, I want to slug 'em! Dislike or distaste is NOT fear -- and don't even start trying to anal-yze the roots of aversion, stating that it comes from fear. To wit: I like okra, including the boiled stuff. Some people hate it cooked that way, because it's slimy. Do they fear it? I daresay no, but they find it distasteful. The same applies to many, if not most people's reactions to homosexual practices. That is normal, in the strictest sense of the word, and nothing is ever going to change that. Period.

  19. #19 Giorgio says:

    @Joe Blow:
    While I find your anal-yze pun quite childish (do you feel the urge of inserting vaginal puns every time you talk about heterosexuality? you surely know that many "straight" people enjoy anal sex, right?), your okra analogy is plain wrong, as it doesn't account for the violent discrimination people around the world are victim of because of their sexual orientation or gender identity.

    Just because people hates okra (even if it was the majority), nobody legislates against cajun cuisine, or restricts okra eaters from getting married, or beats them savagely and cuts thembecause they dare to kiss in public, or tries to "heal" them, or proclaims that they're gonna burn in an imaginary hell because an imaginary god hates them, or directly sends them to their imaginary hell by real lapidation or hanging...

  20. #20 nassim says:

    @HEM and all the like

    weather homophobic or not the auther has the right to call it whatever he likes, the problem is in your head and not in the name.Because you are doing something that 90% of people on Earth see as disgusting (GLT) and dislike, you are paranoid and see everything as homophobic.
    Keep the name. Change minds.

  21. #21 Rodrigo Montoro (Sp0oKeR) says:

    Just tested agaisnt snort and a correct unicode map configured =)

    11:48:18.156514 IP > Flags [P.],
    seq 1904207205:1904207747, ack 712508180, win 33304, options
    [nop,nop,TS val 321481074 ecr 336780502], length 542
    .)ir....GET /login.php?pass=%25u3008scr%25u0131pt%25u3009%25u212fval%28%25uFF07al%25u212Frt%28%2522XSS%2522%29%25u02C8%29%25u2329%2Fscr%25u0131pt%25u232A+
    User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US;
    rv: Gecko/20100722 Firefox/3.6.8
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip,deflate
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    Keep-Alive: 115
    Connection: keep-alive
    Cache-Control: max-age=0
    [**] [1:102020202:0] <script> Encoded XSS post [**]
    [Priority: 0]
    TCP TTL:64 TOS:0x0 ID:18589 IpLen:20 DgmLen:594 DF
    *AP* Seq: 0x717FE565  Ack: 0x2A780314  Win: 0x8218  TcpLen: 32
    TCP Options (3) => NOP NOP TS: 321481074 336780502

    Rule that I created for the PoC

    alert tcp any any -> any any (msg:"<script> Encoded XSS

    When you use uricontent to match it receive the traffic normalized
    from http_inspect.

  22. #22 Get a Clue says:

    Dude, change the name. It's ridiculously inane; I don't care how many of your friends are gay (which, of course, is how every modern bigot starts every bigoted comment). You already knocked one knuckle-dragger out of the wood work, and you're sure to get more.

  23. #23 AnonymousCoward says:

    quoth Get a Clue:
    "You already knocked one knuckle-dragger out of the wood work, and you’re sure to get more."

    Suggest Get a Clue could get a clue about the state of Italian society, like today.
    Just an example:

    Maybe some people should just get used to seeing stuff that the Rapsinger calls bad a lot more. Their phobias might be manageable with constant exposure to fear-triggers. Other threads hosted here have shown that there is a very phobic, if small, subset of contributors that might benefit from even more desensitisation.

    And while on the topic of misapprehensions about who fucks whom with what and how, I get so tired of the dumb misapprehension, evident in this thread, that the "homo" part of homosexuality is the Latin "homo" meaning "man". It's the Greek "homos" meaning "same".

  24. #24 Joe Blow says:

    Well, Giorgio - Did you wet your finger and hold it up to gauge which way the wind was currently blowing before you decided to deride my "childish" pun ("anal-yze")? You were the one who sparked off this whole ass-inine (get it? ;P) exchange by coining the term "HomoXSSuality", which, I have little doubt, you knew implied the attachment of a critical or derisive connotation to "homosexuality".
    Furthermore -- don't even attempt to extrapolatively affix some kind of persecutionality to me or my attitudes -- which you know almost nothing about, except the fact that I think the term "homophobic" is puerile, and am quite disdainful of those who can't focus their minds, their passions, and their argumentative thrusts precisely enough to stay sufficiently coherent so as to refrain from tagging anyone who points out the fact that homosexual practices like buggery and fellatio, etc., are repellant to most non-homosexuals with such an epithet.
    You can NOT infer from my earlier post any of the negativity you seem to want to attribute to me pertaining to persecution of homosexuals, and the like. What I was saying was that the term "homophobic" is predominantly used pejoritavely (and incorrectly, as pointed out by AnonymousCoward: "...the "homo" part of homosexuality is... the Greek "homos" meaning "same") by those who feel over-weeningly righteous in assuming a politically-correct posture in defense of homosexuals.
    Last year, I signed an online petition demanding that any form of persecution or restriction of constitutional rights of homosexuals in the US be added to the federal list of hate crimes. I think consenting adults should be left entirely alone, when it comes to their private lives - in all ways. I couldn't care less, one way or another, who does whom, and in what way. That still doesn't change the fact that "normal" people -- meaning those who belong to the statistically predominant group in our society, based on their attitudes and/or behavior -- tend to feel rather disgusted when they think of men sucking each other off or screwing other dudes in the ass (with the corresponding acts pertaining to women making them queasy, as well). That is a fact, based both in nature and nurture. I didn't cook that up, I've simply made note of it, over the decades, and stated it here.
    If one feels that it is incumbent on them to try to alter their own "instinct", or their indoctrination, in order to more closely conform to what they perceive to be a more ethical or humane outlook - more power to them. That is strictly personal. Just keep your sophomoric "homophobe" appellation to yourself, and stop labelling others you poorly know or understand with such a childish term.
    So -- get off your high horse, and deal with what I've expressed as a personal opinion... and don't you dare try to make me out to be some fucking queer-hating villain. I have no doubt that God loves fags and dykes just as much as anyone else: we are all spiritual beings and were created out of divine love. So stick that in your pipe and fire it up!

  25. #25 Giorgio says:

    @Joe Blow:

    That is a fact, based both in nature and nurture

    "Nature" is often abused as an excuse:

  26. #26 lolfags says:

    Doesn't the bible state that any man who sleeps with another man should be put to death? Yet Joe Blows states that fags are religious? Hmmm. God hates fags.

  27. #27 EnoughOfTheF*ggotry says:

    This article is good. M$ bad.

  28. #28 Jamuse says:

    FWIW, ModSecurity can protect against this via the following rule which limits valid characters based on their byte range:

    deny,log,auditlog,msg:'Invalid characters detected'

  29. #29 Giorgio says:

    Jamuse: your rule will break any internationalized web app (plus those who should accept tabs and newline chars in their input, like... any blog?).

  30. #30 Jamuse says:

    Giorgio - that was just an example dude. You obviously need to apply it properly in each specific instance. For example, you shouldn't need a newline in a first name field, but you may in an address field. Thus the potential valid characters for each might be different (unless you have numbers in your first name). You can easily chain several rules together for each specific input field to ensure that all valid characters are accepted. This requires a deep understanding of each parameter accepted by the application. My point was just that ModSecurity (and I'm not talking about the Core Rule Set, just the ModSecurity rules language) has the flexibility to deal with this. Having said that, this was an awesome discovery.

  31. #31 AnonymousCoward says:

    Jo Blow, you can have those obsessive, twisted trigger images given the perspective of kindness and humanity that no church is able to provide.
    The fear and loathing is so bad that even your nick has to be a sad pun on the mechanics of sexual activity. The fear you display is so strong that it's not at all unexpected that you would be in such denial. Phobias are cureable. Seek help.

    Maone's pun has context, playfulness and several layers of meaning that have nix all to do with any kind of specific sexual activity. And all that from someone whose first language isn't English.

  32. #32 Jan says:

    What I want to know is, does it work on eunuchs? Actually, I'm just irritated with the comments that tell you you must grovel and slobber for the pressure group. It's gay.

Bad Behavior has blocked 722 access attempts in the last 7 days.