What’s wrong with WebSocket APIs? Unveiling vulnerabilities in WebSocket APIs.

What’s wrong with WebSocket APIs? Unveiling vulnerabilities in WebSocket APIs.

Slides from Hacktivity 2019 conference - https://hacktivity.com/index.php/presentations/.

0e97d20ff87bf33851da8cadb86affa9?s=128

Mikhail Egorov

October 25, 2019
Tweet

Transcript

  1. What’s wrong with w/ WebSocket API? Unveiling vulnerabilities in WebSocket

    APIs Mikhail Egorov / @0ang3el #HACKTIVITY2019
  2. # whoami 2 ▪ Security researcher / full-time bug hunter

    ▪ https://bugcrowd.com/0ang3el ▪ https://hackerone.com/0ang3el ▪ Conference speaker ▪ https://www.slideshare.net/0ang3el ▪ https://speakerdeck.com/0ang3el
  3. Previous work 3 ▪ https://media.blackhat.com/bh-us- 12/Briefings/Shekyan/BH_US_12_Shekyan_Toukharian_Hacking_Websocket_Slides .pdf ▪ https://www.nccgroup.trust/us/about-us/newsroom-and- events/blog/2017/may/wssip-a-websocket-manipulation-proxy/

    ▪ https://chybeta.github.io/2018/04/07/spring-messaging-Remote-Code-Execution- %E5%88%86%E6%9E%90-%E3%80%90CVE-2018-1270%E3%80%91/ ▪ https://www.twistlock.com/labs-blog/demystifying-kubernetes-cve-2018-1002105- dead-simple-exploit/ ▪ https://github.com/andresriancho/websocket-fuzzer ▪ https://www.irongeek.com/i.php?page=videos/derbycon9/stable-35-old-tools-new- tricks-hacking-websockets-michael-fowl-nick-defoe
  4. 4 WebSocket protocol essentials

  5. WebSocket protocol – RFC 6455 5 ▪ Efficient two-way communication

    protocol ▪ WebSocket is stateful (HTTP is stateless) ▪ Two main parts: handshake and data transfer
  6. WebSocket protocol – RFC 6455 6 ▪ Extensibility: subprotocols and

    extensions ▪ Subprotocols ▪ https://www.iana.org/assignments/websocket/websocket.xml#subpro tocol-name ▪ Wamp ▪ Stomp ▪ Soap ▪ …
  7. WebSocket protocol – RFC 6455 7 ▪ Extensibility: subprotocols and

    extensions ▪ Extensions ▪ https://www.iana.org/assignments/websocket/websocket.xml#extens ion-name ▪ permessage-deflate ▪ bbf-usp-protocol
  8. WebSocket protocol – RFC 6455 8 ▪ Origin-based security model

    (Browser clients) ▪ No authentication ▪ Client must do client-to-server masking
  9. WebSocket protocol support 9 ▪ Major web browsers ▪ Web

    servers / Proxies ▪ Apache httpd, Nginx, IIS, … ▪ HAProxy, Traefik, Varnish, Envoy, … ▪ Cloud providers ▪WebSocket API (api gateways) ▪WebSocket proxying (load balancers)
  10. WebSocket handshake 10 Upgrade request Base64(Random nonce) Protocol version Required

    HTTP version
  11. WebSocket handshake 11 Required status code BASE64(SHA1(Sec-WebSocket-Key || CONST ))

  12. WebSocket data transfer 12 \x00 – continuation frame \x01 –

    text frame \x02 – binary frame \x08 – close frame \x09 – ping \x0A – pong other values are reserved
  13. WebSocket data transfer - masking 13 ▪ Masking key is

    32-bit long passed inside frame ▪ Client must send masked data ▪ MASKED = MASK ^ DATA (^ - XOR) ▪ Mechanism protects against cache poisoning and smuggling attacks
  14. 14 Cross-Site WebSocket Hijacking

  15. WebSocket security for Web Browser 15 ▪ SOP doesn’t work

    for WebSocket in web browser ▪ Read from WebSocket cross-origin ▪ Write to WebSocket cross-origin ▪ Header Origin should be checked on handshake step (origin-based security model)
  16. CSWSH 16 ▪ Cookies are used to authenticate upgrade request

    ▪ Header Origin isn’t checked or checked poorly
  17. CSWSH 17 ▪ CORS tricks from @albinowax are applicable to

    WebSocket ▪ https://portswigger.net/research/exploiting-cors-misconfigurations- for-bitcoins-and-bounties ▪ Null origin ▪ Pre-domain wildcard ▪ Post-domain wildcard ▪ …
  18. CSWSH – Null origin 18 ▪ nullorigin.html <iframe src="data:text/html, <script>const

    socket = new WebSocket('wss://example.com'); </script>"></iframe>
  19. CSWSH 19 ▪ Playground ▪ https://portswigger.net/web-security/websockets/cross-site- websocket-hijacking

  20. CSWSH – template for attack 5

  21. Demo 5

  22. 22 Authentication / IDOR issues

  23. Authentication 23 ▪ WebSocket protocol doesn’t offer authentication ▪ Developers

    have to roll out their own AuthN ▪ It’s secure to check AuthN only during handshake ▪ Common secure implementations ▪ Session cookies ▪ Tokens
  24. Broken authentication – Case 1 24 ▪ Some ID /

    GUID is required in Upgrade request ▪ Guess ID ▪ Leak GUID (minor IDOR, …)
  25. Broken authentication – Case 2 25 ▪ No authentication during

    handshake step ▪ Some ID / GUID required in API messages ▪ Guess ID ▪ Leak GUID (minor IDOR, …)
  26. Broken authentication – Case 2 26 ▪ Exposing GraphQL subscriptions

    w/o AuthN ▪ https://github.com/righettod/poc-graphql#subscriptions- websocket-endpoint-default-enabling ▪ Path /subscriptions
  27. Insecure Direct Object Reference issues 27 ▪ Strong authentication during

    handshake step ▪ Some ID / GUID required in API messages ▪ Guess ID ▪ Leak GUID (minor IDOR, …)
  28. 28 Smuggling through WebSocket

  29. Reverse proxying WebSocket connection 29 Client Frontend Reverse proxy Backend

    /socket.io/ Public WebSocket API
  30. Reverse proxying WebSocket connection 30 Client Frontend Reverse proxy Upgrade

    request Upgrade request Backend /socket.io/
  31. Reverse proxying WebSocket connection 31 Client Frontend Reverse proxy Upgrade

    request Upgrade request HTTP/1.1 101 HTTP/1.1 101 Backend /socket.io/
  32. Reverse proxying WebSocket connection 32 Client Frontend Reverse proxy Upgrade

    request Upgrade request HTTP/1.1 101 HTTP/1.1 101 WebSocket connection direct WebSocket connection Client - Backend Backend /socket.io/
  33. Smuggling through WebSocket connection 33 Client Frontend Reverse proxy (vulnerable)

    Private REST API Public WebSocket API Backend /internal /socket.io/
  34. 34 Backend Client Frontend Reverse proxy (vulnerable) /internal Upgrade request

    /socket.io/ Sec-WebSocket-Version: 1337 Upgrade request Sec-WebSocket-Version: 1337 Version correctness isn’t checked! Smuggling through WebSocket connection
  35. 35 Backend Client Frontend Reverse proxy (vulnerable) /internal Upgrade request

    /socket.io/ Sec-WebSocket-Version: 1337 Upgrade request Sec-WebSocket-Version: 1337 HTTP/1.1 426 HTTP/1.1 426 Response correctness isn’t checked! Smuggling through WebSocket connection
  36. 36 Backend Client Frontend Reverse proxy (vulnerable) /internal Upgrade request

    /socket.io/ Sec-WebSocket-Version: 1337 Upgrade request Sec-WebSocket-Version: 1337 HTTP/1.1 426 HTTP/1.1 426 TLS connection direct TLS connection Client – Backend not WebSocket!!! Client can access /internal Smuggling through WebSocket connection
  37. Challenge – challenge.0ang3el.tk 37 ▪ URL ▪ https://challenge.0ang3el.tk/websocket.html ▪ You

    need to access flag on localhost:5000 ▪ Seems no one solved
  38. Challenge – challenge.0ang3el.tk 38 ▪ Frontend ▪ Not disclosed WebSocket

    reverse proxy ▪ socket.io.js ▪ Proxies only WebSocket API - /socket.io/ path ▪ Backend ▪ Flask, Flask-SoketIO, Flask-Restful ▪ Listens on localhost:5000 only
  39. challenge1.py

  40. challenge1.py - DEMO

  41. Vulnerable reverse proxies 41 ▪ Vulnerable ▪ Varnish, Envoy proxy

    <= 1.8.0, other non-disclosed ▪ Not vulnerable ▪ Nginx, HAProxy, Traefik, others
  42. Varnish response 42 ▪ WebSocket proxying configuration ▪ https://varnish-cache.org/docs/6.3/users-guide/vcl-example- websockets.html

  43. Smuggling through WebSocket connection 43 Client Frontend Reverse proxy (Nginx

    or another) Private REST API Public WebSocket API & REST API Backend /internal /api/socket.io/ /api/health
  44. Smuggling through WebSocket connection 44 Client Frontend Reverse proxy (Nginx

    or another) Backend /internal /api/socket.io/ /api/health example.com GET HTTP/1.1 200
  45. Smuggling through WebSocket connection 45 Client Frontend Reverse proxy (Nginx

    or another) Backend /internal /api/socket.io/ /api/health Only Upgrade: websocket header is checked! POST /api/health?u= POST /api/health?u=
  46. Smuggling through WebSocket connection 46 Client Frontend Reverse proxy (Nginx

    or another) Backend /internal /api/socket.io/ /api/health attacker.com GET HTTP/1.1 101 HTTP/1.1 101 HTTP/1.1 101 Only status code is checked for response! POST /api/health?u= POST /api/health?u=
  47. Smuggling through WebSocket connection 47 Client Frontend Reverse proxy (Nginx

    or another) Backend /internal /api/socket.io/ /api/health HTTP/1.1 101 HTTP/1.1 101 TLS connection direct TLS connection Client – Backend not WebSocket!!! Client can access /internal POST /api/health?u= POST /api/health?u= Client-to-Server masking isn’t checked by proxy!!!
  48. Challenge2 – challenge2.0ang3el.tk 48 ▪ URL ▪ https://challenge2.0ang3el.tk/websocket.html ▪ You

    need to access flag on localhost:5000 ▪ Seems no one solved
  49. Challenge2 – challenge2.0ang3el.tk 49 ▪ Frontend ▪ Nginx as WebSocket

    reverse proxy ▪ socket.io.js ▪ Proxies only /api/public path (socket.io and healthcheck) ▪ Backend ▪ Flask, Flask-SoketIO, Flask-Restful ▪ Listens on localhost:5000 only
  50. Challenge2 – challenge2.0ang3el.tk 50 ▪ Nginx config

  51. Challenge2 – challenge2.0ang3el.tk 51 ▪ REST API - healthcheck

  52. Challenge2.py 5

  53. Challenge2.py - Demo 5

  54. Vulnerable reverse proxies 54 ▪ Almost all proxies are affected

    ▪ But exploitation is limited ▪ External SSRF is required that returns status code ▪ …
  55. 55 Discovering WebSocket APIs

  56. Discovering WebSocket API 56 ▪ Monitor Upgrade requests ▪ Analyze

    JavaScript files ▪ Try to establish WebSocket connection to each URL ▪ …
  57. 57 Conclusion

  58. Ideas for further research 58 ▪ Security of WebSocket subprotocols

    ▪ More smuggling techniques ▪ HTTP/2 and WebSocket ▪ …
  59. Thank you! @0ang3el