My answer got longer than expected, so just to give you the answer: <body onpageshow="alert(1)">
probably works, and if it doesn't, <body onpaonpageonpagonpageonpageshowshoweshowshowgeshow="alert(1)">
will.
How to try to bypass a custom filter
You say you try to learn how to do is yourself, so here are some first steps I would take:
- check case: not all filters are case sensitive. Is
I
also substituted, or just i
?
- check alternative encodings:
:
isn't allowed, but :
may be (and it works the same in most contexts).
- check what the filter is actually doing. Are replacements always done, or only in certain contexts (eg is
i
always filtered, or only in script
, or only surrounded by characters, or ...)? Are filters only applied once (eg <scri<script>pt>
becomes <script>
)?
The last point is the most complex one. For example, you say that i
is always substituted for .
. But then in the comments, you say that <scri<script>pt>
becomes <scri<scr.pt>pt>
. So maybe i
is only substituted in script
?
You also said that on
is filtered. Are you sure? Or are only some of the specific event attributes filtered? Have you tried onFooBar
? If it is filtered, all on
attributes will be filtered. But if not, some might bypass the filter. If so, you should especially try the new HTM5 ones, some of which are often forgotten. Here is a list of event attributes to check.
What might work in your case
You weren't specific enough with your rules[*] to give a definite answer, but here are some ideas:
<meta http-equIv="refresh" content="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">
will work if the i
filter is not case sensitive (less likely), or if it only applies to script
(which seems to be the case in your example). Note that :
will work in place of :
.
- if the
i
filter is indeed not case sensitive, quite a lot of payloads might work: <scrIpt src=http://localhost/s.js></scrIpt>
(doesn't contain <script>
), <a href="javascrIpt:alert(1)">click</a>
, etc.
[*] And you even contradict yourself a bit; if <script>
becomes a blank space, and i
becomes .
, then why does <scri<script>pt>
lead to <scri<scr.pt>pt>
, instead of <scr. pt>
, which would match your rules?
What the actual filter is in your case
The XSS filters can be seen here.
From your filter description, you are at level two (or possibly level three). The filter is:
script
becomes sc.pt
onclick
becomes o.ick
onmouseover
becomes o.ver
onload
becomes o.oad
onerror
becomes o.err
ondblclick
becomes o.dbl
&
becomes !
:
becomes !
Or alternatively for level three, the same, except the on
filters are a bit less bad.
What payloads will bypass these filters
Now that we have a proper description of the filters, we can easily bypass them.
But first of, the bypasses that will actually not work:
<meta http-equiv="refresh" content="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">
: will not work. i
is not actually always replaced with .
(which makes sense, as such a filter would destroy all actual input), but we can't use &
, and thus can't use a data URL.
<a href="javascript:alert(1)">click</a>
: problem with script
, and also &
.
You can see that the first one only fails because of a rule you haven't mentioned: the &
replace.
But now to a solution that does work. For level two:
- any
on
attribute that isn't onclick
, onmouseover
, onload
, onerror
, or ondblclick
. There are a lot, for example <body onpageshow="alert(1)">
.
For level three, the trick is that there are a lot more on
attributes filters (I didn't check if it's all), but they are replaced by nothing (four times). That means that if you include the keyword in itself five times, you bypass the filter:
<body onpaonpageonpagonpageonpageshowshoweshowshowgeshow="alert(1)">
Conclusion
Your description of the filter is inconsistent and incomplete, so you cannot get the answer based on it.
When testing a filter such as this, you should try as many corner cases around the rules you defined as possible, so you get a description of the filter that is as close to reality as possible.
Then it is just a matter of finding the correct payload to bypass these rules.
i
for.
then the order of checks may come into play e.g.<scr.pt>
may end up being<script>
after filtering. Or if, it only does one pass<scri<script>pt>
would also become<script>
.i
is substituted for.
, but not the other way around, so why would<scr.pt>
become<script>
?