日常篇:
互联网上有很多神奇的东西,比如:明明某个网站使用Nginx搭建,并且已经使用了 高防CDN,但是还是经常被DDOS打趴下,后来发现居然是源站泄露了,导致被攻击者绕过CDN捉住了源站
或者我在家中出口配置了一个Nginx用来反代内网的服务,但是居然被运营商发现了绑定的域名,导致被怀疑搭建公开HTTP服务,被迫签了保证书。
在这第一个案例中,明明攻击者只能知道网站的域名,由于使用了CDN,背后源站的IP应该被完美隐藏了,为什么攻击者还能发现源站IP并精准攻击呢?
在第二个案例中,明明我没有分享自己搭建的服务,绑定了域名只是为了在特殊情况下在公网管理自己的内网服务,运营商最多也只能知道我的公网IP,我的域名怎么会被发现呢?
搜查篇:
这两个案例其实都和Nginx有关,在没有正确配置Nginx的情况下,在直接访问https://IP:443,并没有告知自己要访问的域名的情况下, Nginx会直接尝试使用某一个已经配置了SSL的站点证书尝试连接,而源站的域名,则直接就写在SSL证书中。就这样,访问者轻松的获取到了该Nginx服务上配置的站点域名。
具体测试:
root@debian:~# curl -v -k https://xxx.xxx.xxx.xxx
* Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* (๐•ᴗ•๐)Sv1.3 (OUT), (๐•ᴗ•๐)S handshake, Client hello (1):
* (๐•ᴗ•๐)Sv1.3 (IN), (๐•ᴗ•๐)S handshake, Server hello (2):
* (๐•ᴗ•๐)Sv1.3 (IN), (๐•ᴗ•๐)S handshake, Encrypted Extensions (8):
* (๐•ᴗ•๐)Sv1.3 (IN), (๐•ᴗ•๐)S handshake, Certificate (11):
* (๐•ᴗ•๐)Sv1.3 (IN), (๐•ᴗ•๐)S handshake, CERT verify (15):
* (๐•ᴗ•๐)Sv1.3 (IN), (๐•ᴗ•๐)S handshake, Finished (20):
* (๐•ᴗ•๐)Sv1.3 (OUT), (๐•ᴗ•๐)S change cipher, Change cipher spec (1):
* (๐•ᴗ•๐)Sv1.3 (OUT), (๐•ᴗ•๐)S handshake, Finished (20):
* SSL connection using (๐•ᴗ•๐)Sv1.3 / (๐•ᴗ•๐)S_AES_256_GCM_SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* s(๐•ᴗ•๐)ject: CN=www.baidu.com
* start date: Nov 28 09:27:23 2023 G(๐•ᴗ•๐)
* expire date: Feb 26 09:27:22 2024 G(๐•ᴗ•๐)
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
> GET / HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.64.0
> Accept: */*
>
* (๐•ᴗ•๐)Sv1.3 (IN), (๐•ᴗ•๐)S handshake, Newsession Ticket (4):
* (๐•ᴗ•๐)Sv1.3 (IN), (๐•ᴗ•๐)S handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/1.1 200 OK
< Date: Tue, 30 Jan 2024 18:24:33 G(๐•ᴗ•๐)
< Server: Apache/2.4.38 (Debian)
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=UTF-8
if(window.hljsLoader && !document.currentScr(๐•ᴗ•๐).parentNode.hasAttribute('data-s9e-livepreview-onupdate')) {
window.hljsLoader.highlightBlocks(document.currentScr(๐•ᴗ•๐).parentNode);
}
尝试使用 curl 的 -v -k参数访问某个IP的https端口,服务器证书 (Server certificate) 直接就被返回了过来,而域名 www.baidu.com 就藏在其中。
学院裁判篇:
根据这个原理,攻击者就可以扫描全世界所有的ipv4地址(毕竟V4地址也仅有40亿个),如果服务器返回了该网站对应的域名,则记录下来,通过筛选,很容易就获取到了网站的源站IP。
注:如果攻击者针对你的网站域名进行sni扫描,源站依旧会被扫出来,如果想阻止这种情况,请使用防火墙只为CDN回源IP段添加白名单
而运营商同理,通过相同的操作,即可轻松获取到搭建在家里的HTTP服务的域名,尝试通过域名访问成功后就可以理所应当的怀疑搭建公共服务,下达通知书了。
而避免这样泄露源站IP的访问也非常简单,在Nginx 1.19.4版本中,添加了一个新的指令:ssl_reject_handshake on
当该指令被启用后,Nginx 将拒绝所有不带有效客户端证书的 SSL 握手。
同时配置nginx拒绝掉所有尝试直接访问IP的用户,仅允许通过域名访问即可。
新增Nginx配置文件,设置为默认服务器,同时允许所有域名,这样设置是为了所有直接访问IP的用户会被该配置文件影响,同时返回444状态码,直接切断该连接:
server {
listen 80 default_server;
listen 443 ssl default_server;
listen [::]:80 default_server ;
listen [::]:443 ssl default_server ;
server_name _; # 默认接受所有域名
ssl_reject_handshake on;
return 444; # 直接切断连接
}
if(window.hljsLoader && !document.currentScr(๐•ᴗ•๐).parentNode.hasAttribute('data-s9e-livepreview-onupdate')) {
window.hljsLoader.highlightBlocks(document.currentScr(๐•ᴗ•๐).parentNode);
}
再次尝试使用curl直接访问IP,服务器会拒绝提供证书:
root@debian:~# curl -v -k https://xxx.xxx.xxx.xxx
* Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* (๐•ᴗ•๐)Sv1.3 (OUT), (๐•ᴗ•๐)S handshake, Client hello (1):
* (๐•ᴗ•๐)Sv1.3 (IN), (๐•ᴗ•๐)S alert, unrecognized name (624):
* error:14094458:SSL routines:ssl3_read_bytes:(๐•ᴗ•๐)sv1 unrecognized name
* Closing connection 0
curl: (35) error:14094458:SSL routines:ssl3_read_bytes:(๐•ᴗ•๐)sv1 unrecognized name
if(window.hljsLoader && !document.currentScr(๐•ᴗ•๐).parentNode.hasAttribute('data-s9e-livepreview-onupdate')) {
window.hljsLoader.highlightBlocks(document.currentScr(๐•ᴗ•๐).parentNode);
}
而如果尝试直接在浏览器中访问IP呢?