좀 열심히 쓴 글

Bypass SNI Filtering

ch4rli3kop 2020. 6. 29. 00:04
반응형

Bypass SNI Filtering

Author

  • Student ID: ***

  • Name: Chanhee Park

  • GitHub ID: ch4rli3kop

Index

SNI (Server Name Indication) [1]

SNI는 SSL/TLS Extension의 한 종류로, TLS Handshake 과정 중 Client에서 Server로 접속하려는 사이트의 Host name/Domain name을 보내는 기술을 뜻한다.

이 기술의 필요성을 이해하기 위해서는 그 등장배경인 HTTPS에서의 name-based virtual hosting에 대해 살펴볼 필요가 있어, 간단히 짚고 넘어가려 한다.

What is name-based virtual hosting? [2]

name-based virtual hosting은 단일 IP 주소에 다음과 같이 복수 개의 domain/website를 운영할 수 있는 방법으로, IP 주소를 기존보다 적게 사용할 수 있어, 비용적인 측면과 관리적인 측면에서 이점이 있기때문에 일반적으로 많이 사용되는 방법이다.

                                                       Test0.com
Client      ->     Server(192.168.0.100)      ->     Test1.com
                                                      Test2.com

name-based virtual hosting에서는 Server는 우선적으로 IP를 기준으로 사이트를 매칭하며, 해당 결과가 여러 개 존재하는 경우에는 다음과 같은 Client의 Request header의 Host 값에 따라 사이트를 매칭시킨다.

The problem with name-based virtual hosting in HTTPS [3]

그러나 HTTPS를 사용하게 되면서 name-based virtual hosting을 이용하는 것에 문제가 발생한다. SSL/TLS 통신의 경우 Communication 이전에 다음과 같은 TLS Handshake 작업을 수행하는데, 이 작업 중에 Server가 Client Request의 도메인을 특정할 수 없었기 때문에 발생한 문제였다.

일반적으로 도메인마다 certificate를 가지고, Server는 Client Request에 따라 해당하는 도메인의 certificate를 Client에게 제공하여 정상적으로 Client/Server 간의 TLS Handshake가 이루어져야 한다.

그러나 이처럼 단일 IP가 복수 개의 도메인을 가지는 경우, 앞서 HTTP처럼 Client의 Request Host에 따라 도메인을 결정하려 해도, 이 데이터를 교환하기에 앞서 TLS Handshake가 이뤄지기 때문에 HTTPS에서는 Server가 Client Request가 어떤 도메인을 대상으로 하는지 특정할 수가 없었다.

물론 여러 도메인에 대해 하나의 인증서로 관리하는 SAN 방식도 존재하지만, 매번 새로 갱신해야하는 불편함과 domain을 추가할수록 크기가 커진다는 단점이 있었다.

What is SNI?

앞서 언급한 HTTPS에서의 name-based virtual hosting의 문제점을 보완하기 위해 나온 기술이 SNI이다.

다음과 같이 TLS Handshake 과정 중, 전송되는 Client Hello에서 사용되는 Extension으로, 이를 이용하여 Server는 Client Request의 도메인을 식별할 수 있다. 따라서, Server가 Client Request에 해당하는 도메인을 특정하여 Client에게 해당 도메인의 Certificate를 전달함으로써, Client/Server 간의 정상적인 HTTPS 통신이 이루어질 수 있다.

SNI Filtering [4]

구현은 조금씩 다를 수 있지만, 일반적인 SNI Filtering은 다음과 같은 방식으로 이루어진다. TLS Handshake의 Client Hello에서 평문으로 전송되는 SNI domain string을 추출하여 사전에 등록된 Black/White List Rule을 적용하여 Filtering 유무를 판별하는 방식이다.

Firewall/Gateway는 Client에서 오는 패킷이 Block 해야하는 domain으로 판별하면, 해당 Client Hello 패킷을 Drop한뒤, 다음 그림처럼 Client에게 RST 패킷을 전송함으로써 해당 TLS Handshake를 끊어버린다.


Bypass SNI Filtering

다음의 내용에서는 위에서 설명한 SNI Filtering을 우회하는 방법에 대하여 논의한다.

Packet Fragmentation [5]

대부분의 필터링 시스템은 Client Hello 패킷에서 SNI Field 데이터를 추출하기 위해, 3 Way handshake가 이뤄진 직후 보내는 첫 번째 패킷(Client Hello)을 검사한다. Client Hello Frame의 크기는 Cipher Suite와 Extension에 따라 다르지만, 이더넷의 MTU 값인 1500보다 훨씬 작기 때문에 첫 번째 패킷만을 처리하는 것은 대용량 트래픽 처리에 있어 합리적인 것으로 보인다.

MTU

Maximum Transmission Unit의 약자로, 특정 레이어에서 전송할 수 있는 최대 데이터 단위를 나타낸다.

그러나 이는 역으로 말하면, 첫 번째 패킷이 Client Hello가 아니도록 혼선을 준다면, 필터링 시스템을 우회할 수 있을 것이다. 또한 첫 번째 패킷만을 검사한다는 것은 패킷이 분할되었을 경우, Reassemble할 수 없다는 것을 뜻한다. 따라서, MTU 값을 Client Hello보다 작게 조정하여 Packet Fragmentation을 야기한다면, SNI Filtering을 우회할 수 있다.

Example

본 예시에서는 Packet fragmentation을 이용한 SNI Filtering 우회 방법에 대해 다룬다. 다음은 MTU 값이 200으로 조정하여 SNI 차단 대상인 www.pornhub.com으로 HTTPS Request를 보낸 예시이다. 앞서 SNI Filtering에 차단된 사례와 다르게, SNI Filtering을 우회하여 성공적으로 TLS Handshake가 성립됨으로써 정상적인 Response를 받는 것을 확인할 수 있다.

$ sudo ip link set ens33 mtu 200     

$ ifconfig                          
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 200
      inet 192.168.41.148 netmask 255.255.255.0 broadcast 192.168.41.255
      ...

$ curl https://www.pornhub.com  
<!DOCTYPE html>
<!--[if lt IE 7 ]> <html class="ie6 language-en" lang="en"> <![endif]-->
<!--[if IE 7 ]> <html class="ie7 language-en" lang="en"> <![endif]-->
<!--[if IE 8 ]> <html class="ie8 language-en" lang="en"> <![endif]-->
<!--[if IE 9 ]> <html class="ie9 language-en" lang="en"> <![endif]-->
<!--[if !(IE)]><!--> <html class="language-en" lang="en"> <!--<![endif]-->


<head>
...

Manipulating SNI Field [4]

SNI는 단일 IP 주소에 여러 domain이 존재하는 HTTPS 상의 name-based virtual hosting 환경에서 사용되며, HTTPS는 다음과 같은 프로세스로 처리된다.

  1. IP matching을 통한 domain을 검색한다.

  2. Client Hello의 SNI Field를 통해 domain을 식별하고, 해당 domain의 Certificate를 Client에게 전송함으로써 TLS Handshake가 이루어진다.

  3. SSL/TLS 키 교환이 종료되면, Request를 암호화하여 통신이 진행되는데, 이때는 Request 내부의 Host Field를 통해 domain을 특정한다.

위와 같은 프로세스 내에서, 다음 두 가지 상황의 공격이 존재할 수 있다.

IP 주소에 단일 domain을 사용하는 경우

앞서 설명했듯, SNI는 단일 IP 주소에 여러 domain이 존재하는 경우, Server가 Client에게 특정 domain의 Certificate를 제공하기 위해 사용되는 Field이다. 따라서 단일 domain의 경우 SNI Field는 사용되지 않아, 임의로 SNI Field를 수정하여 SNI Filtering을 우회할 수 있다.

PoC (Proof of Concept)

다음의 예시는 단일 domain을 갖는 유해 사이트인 "gelbooru.com"을 대상으로 SNI Field를 임의의 값으로 변조하는 코드이다. 실행 결과 SNI Filtering을 우회하여 정상적으로 TLS Handshake를 맺은 뒤, Response를 받는 것을 확인할 수 있다.

import socket, ssl

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_default_certs()

SNI = 'www.ArbitraryDomain.com'
HOST = 'gelbooru.com'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssl_sock = context.wrap_socket(s, server_hostname=SNI)
ssl_sock.connect((HOST, 443))

ssl_sock.send(b"GET / HTTP/1.1\r\nHost: gelbooru.com\r\n\r\n")
res = ssl_sock.recv()
print(res)
Result
C:\Users\pch21\Documents\Network>python test.py
b'HTTP/1.1 200 OK\r\nServer: nginx/1.10.3\r\nDate: Fri, 15 May 2020 08:52:48 GMT\r\nContent-Type: text/html; charset=UTF-8\r\nTransfer-Encoding:
...

SAN Certificate만 사용하는 경우

Subject Alternate Name Certificate는 하나의 Certificate로 여러 domain을 관리하는 방법이다. 유해 사이트가 이러한 SAN 방식만을 사용한다면, 역시 하나의 Certificate만 존재하므로 SNI Field를 이용할 필요가 없이 정상적인 TLS Handshake를 맺는 것이 가능하다. 키 교환이 이루어진 다음에는 Request Header에 존재하는 Host Field를 이용하여 domain을 구분하므로, 앞선 방식과 동일한 코드로 SNI Filtering을 우회할 수 있다.

Manipulating Request Host Field [4]

이 방법은 앞 경우와 유사하지만, 단일 IP에 대하여 SAN Certificate와 다른 Certificate를 사용하고, SAN Certificate 내에 유효한 domain과 악성 domain이 모두 존재할 때, 유용하게 사용할 수 있다. 이 우회 방법은 다음과 같은 프로세스로 진행할 수 있다.

  1. IP 주소를 통하여 해당 Server에 접근한다.

  2. 악성 domain과 동일한 SAN Certificate를 사용하는 유효한 domain을 SNI Field로 사용하여 SNI Filtering을 우회한다.

  3. TLS Handshake를 맺은 뒤, Request Header의 Host Field로 악성 domain에 접근한다.

PoC (Proof of Concept)

본 증명에서는 SAN Certificate 내의 악성 domain에 접근하는 과정을 보일 예정이다. 테스트는 다음과 같은 Google의 SAN Certificate를 사용하는 domain을 대상으로 진행한다.

이 중 www.android.com은 유효한 domain, www.youtube.com은 악성 domain이라고 가정한다.

import socket, ssl

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_default_certs()

SNI = 'www.ArbitraryDomain.com'
HOST = 'www.android.com'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssl_sock = context.wrap_socket(s, server_hostname=SNI)
ssl_sock.connect((HOST, 443))
ssl_sock.send(b"GET / HTTP/1.1\r\nHost: www.youtube.com\r\n\r\n")
res = ssl_sock.recv()
print(res)

Result

실행 결과, P3P 정책이 없어 제대로된 웹페이지가 열리지는 않지만, 정상적으로 해당 악성 domain에 접근하여 Response를 가져오는 것을 확인할 수 있다.

C:\Users\pch21\Documents\Network>python test.py
b'HTTP/1.1 200 OK\r\nStrict-Transport-Security: max-age=31536000\r\nX-Content-Type-Options: nosniff\r\nP3P: CP="This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl=ko for more info."\r\nX-Frame-Options: SAMEORIGIN\r\nExpires: Tue, 27 Apr 1971 19:44:06 GMT\r\nCache-Control: no-cache\r\nContent-Type: text/html; charset=utf-8\r\nDate: Fri, 15 May 2020 10:56:45 GMT\r\nServer: YouTube Frontend Proxy\r\nX-XSS-Protection: 0\r\nSet-Cookie: YSC=n8ndAV2OIh4; path=/; domain=.youtube.com; secure; httponly; samesite=None\r\nSet-
...

Drop RST Packet [6]

SNI Filtering 장비가 탐지한 Client Hello 패킷을 Drop 시키지 않거나 Server에게 연결을 종료한다는 신호를 주지 않고, 단순히 Client에게 RST 패킷을 보냄으로써 연결을 끊어버릴 때 유용하게 사용할 수 있다.

다음과 같이 Client에게 전송되는 RST 패킷을 사전에 Drop 시키는 것으로 쉽게 우회할 수 있는 방법이나, 현재 대부분의 SNI Filtering 장비에서는 사용할 수 없는 방법이다.

$ sudo iptables -A INPUT -p tcp --tcp-flags RST RST --sport 443 -j DROP

VPN [7]

Virtual Private Network의 약자로서, 네트워크의 특정 지점간의 가상 연결이다. 두 지점 사이에는 암호화를 포함하는 보안적 요소가 가미된 VPN Turnnel이 생성되어 두 지점 간의 데이터를 보호할 수 있다.

SNI Filtering 장비가 존재하지 않는 네트워크 지점과의 VPN 연결을 통해 SNI Filtering을 우회할 수 있다.

ESNI [8, 9]

Encrypted Server Name Indication 기술로, Client와 Server 간의 사전 키 교환을 통해 SNI Field로의 domain 유출을 방지할 수 있다. 가시성과 보안의 측면 사이에서 여러 논의가 진행 중에 따라, Client Hello를 모두 암호화하거나, SNI 및 암호화와 관련된 추가적인 Field만 암호화하는 등의 다양한 형태가 존재한다.

DoT (DNS over TLS) / DoH (DNS over HTTPS) [10, 11, 12]

ESNI 기술의 핵심은 SNI를 암호화하기 위한 Client와 Server 간의 사전 키 교환이다. 이는 DoT, DoH 두 가지 기술을 활용한 것인데, 먼저 DoT의 경우 일반적인 TLS Handshake와 유사하다. 853 Port를 사용하여 TCP 연결을 수행하고, TLS Handshake를 맺어 Session을 생성한다. DoH는 이 DoT로 만든 TLS Session으로 HTTPS 통신을 사용하는 방법으로, HTTPS Request 내부에 다음과 같이 DNS Query를 삽입함으로써, DNS Query를 암호화할 수 있다.

#GET 
  :method = GET
  :scheme = https
  :authority = dnsserver.example.net
  :path = /dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB
  accept = application/dns-message
#POST
  :method = POST
  :scheme = https
  :authority = dnsserver.example.net
  :path = /dns-query
  accept = application/dns-message
  content-type = application/dns-message
  content-length = 33

Encrypted Server Name Indication [13, 14]

ESNI을 위해서는 Client/Server 간의 SNI를 암호화하기 위한 키 교환이 필요한데, 이와 관련된 Record(Server의 공개키, Cipher suites, ESNI 버전 등)를 DNS Server에 저장하여, Client는 DoH를 통해 암호화된 DNS Query/Response로 해당 정보를 가져온다.

Client가 Server의 ESNI Record 정보를 성공적으로 수신하면, Client는 Diffie-Hellman 알고리즘에 기반하여, Server의 공개키와 일회성으로 만든 개인키를 이용해서 SNI를 암호화하기 위한 비밀키를 생성한다. 이 후, SNI를 암호화한 데이터와 ESNI Record 관련 정보들을 저장한 encrypted_server_name Extension을 갖는 새로운 Client Hello를 생성하여 TLS Handshake를 수행한다.

이와 같은 방식으로 SNI 정보의 평문 유출을 방지함으로써, ESNI는 SNI Filtering을 불가능하게 만들 수 있다. 또한, ENSI는 Server의 Certificate를 TLS Handshake 과정 이후에 전송하는 TLS 1.3에서만 지원함으로써, Certificate 내에 저장되어 있는 domain 정보의 유출마저 막는다.

Conclusion

이와 같이 SNI Filtering 기술의 몇 가지 우회 방법에 대하여 알아보았다. 대부분의 우회 방법들이 현재까지도 통용되고, 통신사 장비에 따른 보안 조치를 보았을 때, 정부의 유해 사이트 차단 정책을 위해서는 SNI Filtering 기술뿐만 아니라 암호화 트래픽을 식별할 수 있는 기술의 추가적인 발전이 필요할 것이다.

Reference

[1] D. Eastlake, "RFC 6066 - Transport Layer Security (TLS) Extensions: Extension Definitions", 2011. [Online]. Available: https://tools.ietf.org/html/rfc6066

[2] Apache, "Name-Based Virtual Host Support - Apache HTTP Server Version 2.4", [Online]. Available at: https://httpd.apache.org/docs/2.4/en/vhosts/name-based.html

[3] Cloudflare, "What Happens in a TLS Handshake?", 2020. [Online]. Available: https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/

[4] W. M. Shbair, T. Cholez, A. Goichot and I. Chrisment, "Efficiently bypassing SNI-based HTTPS filtering," 2015 IFIP/IEEE International Symposium on Integrated Network Management (IM), Ottawa, ON, 2015, pp. 990-995, doi: 10.1109/INM.2015.7140423.

[5] LoG, "IT From Log : 네이버 블로그", 2019. [Online]. Available at: http://blog.naver.com/malloc813/221463849554

[6] G. Lee, "SNI 차단 우회", gilgil.gitlab, 2019. Available: https://gilgil.gitlab.io/2019/02/14/1.html

[7] Clouddwelling, "VPN’S: How They Work And Why You Really Need One | Clouddwelling", [Online]. Available at: https://clouddwelling.com/how-vpns-work/

[8] Ghedini, A., "Encrypt It Or Lose It: How Encrypted SNI Works", 2018. [Online]. Available at: https://blog.cloudflare.com/encrypted-sni/

[9] Pol4bear, "알아두면 쓸데없는 신비한 ESNI",2020. [Online]. Available at: https://pol4.dev/what-is-esni/#esni%EC%9D%98-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D

[10] Z. Hu, L. Zhu, J. Heidemann, A. Mankin, D. Wessels and P. Hoffman, "RFC 7858 - Specification for DNS over Transport Layer Security (TLS)", 2016. [Online]. Available: https://tools.ietf.org/html/rfc7858

[11] P. Hoffman and P. McManus, "RFC 8484 - DNS Queries over HTTPS (DoH)", 2018. [Online]. Available: https://tools.ietf.org/html/rfc8484

[12] S. Dickinson, D. Gillmor and T. Reddy, "RFC 8310 - Usage Profiles for DNS over TLS and DNS over DTLS", 2018. [Online]. Available: https://tools.ietf.org/html/rfc8310

[13] E. Rescorla, K. Oku, N. Sullivan and C. Wood, "draft-ietf-tls-esni-06 - Encrypted Server Name Indication for TLS 1.3", 2020. [Online]. Available: https://tools.ietf.org/html/draft-ietf-tls-esni-06

[14] E. Rescorla, "RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3", 2018. [Online]. Available: https://tools.ietf.org/html/rfc8446



반응형