本文为 SEED Labs 2.0 - Virtual Private Network (V*N) Lab 的实验记录。

文章目录

  • 0. 实验目标
  • 1. 生成证书
  • 2. 设置 Docker
  • 3. 编写程序
  • 4. 测试
  • 5. 总结

0. 实验目标

本实验要求完成 V*N 的实现。其应当支持 TUN 建立、隧道加密、服务器认证、客户端登录、多用户等功能。

本实验的实验手册使用多虚拟机与 C 语言完成,而我们希望直接使用 docker 和 Python。我们一步到位完成了所有程序的编写,下面描述我们的具体步骤。

1. 生成证书

创建 CA

$ mkdir demoCA
$ cd demoCA
$ mkdir certs crl newcerts
$ touch index.txt serial
$ echo 1000 > serial
$ cd ..
$ cp /usr/lib/ssl/openssl.cnf myCA_openssl.cnf
$ openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -keyout ca.key -out ca.crt -subj "/CN=www.modelCA.com/O=Model CA LTD./C=US/ST=New York/L=Syracuse" -passout pass:dees

创建并签发服务器使用的证书。

$ openssl req -newkey rsa:2048 -sha256 -keyout vpn.key -out vpn.csr -subj "/CN=vpnlabserver.com/O=Model CA LTD./C=US/ST=New York/L=Syracuse" -passout pass:dees
$ openssl ca -config myCA_openssl.cnf -policy policy_anything -md sha256 -days 3650 -in vpn.csr -out vpn.crt -batch -cert ca.crt -keyfile ca.key

v*n.crtv*n.key,放入 server-certs 文件夹中。

ca.crt 放入 client-certs 文件夹中,并建立软链接:

$ openssl x509 -in ca.crt -noout -subject_hash
eaa14a05
$ ln -s ca.crt eaa14a05.0

2. 设置 Docker

编写 docker-compose.yml

version: "3"services:VPN_Client1:image: handsonsecurity/seed-ubuntu:largecontainer_name: client-10.0.2.5tty: truecap_add:- ALLextra_hosts:- "vpnlabserver.com:10.0.2.8"devices:- "/dev/net/tun:/dev/net/tun"volumes:- ./volumes:/volumesnetworks:net-10.0.2.0:ipv4_address: 10.0.2.5command: bash -c "tail -f /dev/null"VPN_Client2:image: handsonsecurity/seed-ubuntu:largecontainer_name: client-10.0.2.6tty: truecap_add:- ALLextra_hosts:- "vpnlabserver.com:10.0.2.8"devices:- "/dev/net/tun:/dev/net/tun"volumes:- ./volumes:/volumesnetworks:net-10.0.2.0:ipv4_address: 10.0.2.6command: bash -c "tail -f /dev/null"VPN_Client3:image: handsonsecurity/seed-ubuntu:largecontainer_name: client-10.0.2.7tty: truecap_add:- ALLextra_hosts:- "vpnlabserver.com:10.0.2.8"devices:- "/dev/net/tun:/dev/net/tun"volumes:- ./volumes:/volumesnetworks:net-10.0.2.0:ipv4_address: 10.0.2.7command: bash -c "tail -f /dev/null"Host_V:image: handsonsecurity/seed-ubuntu:largecontainer_name: host-192.168.60.101tty: truecap_add:- ALLvolumes:- ./volumes:/volumesnetworks:net-192.168.60.0:ipv4_address: 192.168.60.101command: bash -c "ip route del default  &&ip route add default via 192.168.60.1  &&/etc/init.d/openbsd-inetd start &&tail -f /dev/null"Router:image: handsonsecurity/seed-ubuntu:largecontainer_name: server-10.0.2.8-192.168.60.1tty: truecap_add:- ALLdevices:- "/dev/net/tun:/dev/net/tun"sysctls:- net.ipv4.ip_forward=1volumes:- ./volumes:/volumesnetworks:net-10.0.2.0:ipv4_address: 10.0.2.8net-192.168.60.0:ipv4_address: 192.168.60.1command: bash -c "ip route del default  &&ip route add default via 10.0.2.1 &&tail -f /dev/null"MITM:image: handsonsecurity/seed-ubuntu:largecontainer_name: mitm-10.0.2.9-192.168.60.2tty: truecap_add:- ALLdevices:- "/dev/net/tun:/dev/net/tun"sysctls:- net.ipv4.ip_forward=1volumes:- ./volumes:/volumesnetworks:net-10.0.2.0:ipv4_address: 10.0.2.9net-192.168.60.0:ipv4_address: 192.168.60.2command: bash -c "ip route del default  &&ip route add default via 10.0.2.1 &&tail -f /dev/null"networks:net-192.168.60.0:name: net-192.168.60.0ipam:config:- subnet: 192.168.60.0/24gateway: 192.168.60.100net-10.0.2.0:name: net-10.0.2.0ipam:config:- subnet: 10.0.2.0/24gateway: 10.0.2.1

其中:

  • V*N_Client1V*N_Client2V*N_Client3 为 3 个客户端
  • Host_V 为一台主机
  • Router 为 V*N 服务器
  • MITM 为中间人攻击使用的服务器

它们的 IP 和连接关系如下图所示

设置完成后,我们启动 docker

$ dcbuild
$ dcup

3. 编写程序

编写 V*N 服务器和中间人攻击服务器使用的 v*nserver.py

#!/usr/bin/env python3
import fcntl
import struct
import os
import ssl
import spwd
import crypt
from scapy.all import *TUNSETIFF = 0x400454ca  # ioctl request code
IFF_TUN = 0x0001  # create a tunnel
IFF_TAP = 0x0002  # create a tap device
IFF_NO_PI = 0x1000  # don't pass on packet info'''
Create the tun interface
'''
tun = os.open("/dev/net/tun", os.O_RDWR)  # open the tun device
# create the control block
ifr = struct.pack('16sH', b'tun%d', IFF_TUN | IFF_NO_PI)
ifname_bytes = fcntl.ioctl(tun, TUNSETIFF, ifr)  # create the interface'''
Get the interface name
'''
ifname = ifname_bytes.decode('UTF-8')[:16].strip("\x00")  # get the interface name
print("Interface Name: {}".format(ifname))  # print the interface name'''
Set route
'''
os.system("ip addr add 192.168.53.1/24 dev {}".format(ifname))  # set the route
os.system("ip link set dev {} up".format(ifname))  # set the interface up'''
Get certs
'''
SERVER_CERT = "/volumes/crt/server-certs/vpn.crt"  # server certificate
SERVER_PRIVATE = "/volumes/crt/server-certs/vpn.key"  # server private key'''
Set SSL
'''
context_srv = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)  # create the SSL context
context_srv.num_tickets = 0  # disable session tickets
# load the server certificate
context_srv.load_cert_chain(SERVER_CERT, SERVER_PRIVATE)'''
Set sock
'''
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,0)  # create the socket
sock.bind(("0.0.0.0", 443))  # bind the socket to the port
sock.listen(5)  # listen for connections
print(">>> Preparation done.")'''
Initialization
'''
inputs = [sock, tun]  # create the input list
con_dict = {}  # create the connection dictionary
ip_dict = {}  # create the IP dictionary'''
Main loop
'''
while True:ready, _, _ = select.select(inputs, [], [])  # select the ready inputsfor fd in ready:  # for each ready inputif fd is sock:  # if the input is the socket'''Acceppt a new connection and set up the connection'''con, addr = sock.accept()  # accept the connectionIPa, _ = addr  # get the IP address# wrap the connection with SSLcon = context_srv.wrap_socket(con, server_side=True)con.setblocking(0)  # set the socket to non-blockingprint(">>> {} new connection".format(IPa))'''Receive the username and password.If they are all correct, add the connection to the listening list.'''usrname = b''  # create the usernamepasswd = b''  # create the passwordre_client_auth = IP()  # create the packet to reply the client authenticationre_client_auth.src = '192.168.53.1'  # set the source IP addresswhile (usrname == b'') or (passwd == b''):  # while some data is not received# select the connection inputsready, _, _ = select.select([con], [], [])for fd in ready:  # for each ready inputdata = fd.recv(2048)  # receive the datapkt = IP(data)  # create the packetre_client_auth.dst = pkt.src  # set the destination IP addressif usrname == b'':  # if the username is not receivedusrname = pkt[Raw].load  # get the usernameelse:  # if the username is received but the password is not receivedpasswd = pkt[Raw].load  # get the passwordtry:# get the passwordpw1 = spwd.getspnam(usrname.decode()).sp_pwd# get the encrypted passwordpw2 = crypt.crypt(passwd.decode(), pw1)except KeyError:  # if the username is not found# message to the clientcon.sendall(bytes(re_client_auth/b'0'))con.close()  # close the connectionprint(">>> {} login failed - WRONG USERNAME".format(IPa))else:  # if the username is foundif pw1 != pw2:  # if the password is not correct# message to the clientcon.sendall(bytes(re_client_auth/b'0'))con.close()  # close the connectionprint(">>> {} login failed - WRONG PASSWORD".format(IPa))else:  # if the password is correct# message to the clientcon.sendall(bytes(re_client_auth/b'1'))inputs.append(con)  # add the connection to the input listprint(">>> {} login succeed".format(IPa))elif fd is tun:  # if the input is the tun interfacepacket = os.read(tun, 2048)  # read the packetpkt = IP(packet)  # create the packetprint("=== TUN:\t{}\t-->\t{}\t===".format(pkt.src, pkt.dst))# send the packet to the destinationcon_dict[pkt.dst].sendall(packet)else:  # if the input is the connectiondata = fd.recv(2048)  # receive the dataif data != b'':  # if the data is not emptypkt = IP(data)  # create the packetprint("=== SOCKET:\t{}\t-->\t{}\t===".format(pkt.src, pkt.dst))if pkt.src not in con_dict:  # if the source IP is not in the dictionary# add the connection to the dictionarycon_dict[pkt.src] = fd# add the IP address to the IP dictionaryip_dict[fd] = pkt.src# write the packet to the tun interfaceos.write(tun, bytes(pkt))else:  # if the data is emptyprint(">>> {} connection closed.".format(ip_dict[fd]))inputs.remove(fd)  # remove the connection from the input list# remove the IP from the connection dictionarydel con_dict[ip_dict[fd]]del ip_dict[fd]  # remove the connection from the IP dictionaryfd.close()  # close the connection

对于该程序,需要注意的是,我们使用了 TCP 而不是 UDP,所以最一开始建立的 sock 只会被用来和新客户端建立连接,而通信使用的是新建立的连接。

我们通过类似 ip route 的方式实现了文件描述符的选择,但由于在本案中连接数较少,我们没有使用多进程与管道——也就是说,在一条消息杯转发前,系统是阻塞的。经过测试,在 3 个客户端时,所有客户端都能正常通信,几乎不会有延迟。如果后期需要更多的客户端,我们再考虑增加多进程。

编写 V*N_Client1 使用的 v*nclient1.py

#!/usr/bin/env python3import fcntl
import struct
import os
import socket
import ssl
import getpass
from scapy.all import *TUNSETIFF = 0x400454ca # ioctl request code
IFF_TUN = 0x0001 # create a tunnel
IFF_TAP = 0x0002 # create a tap device
IFF_NO_PI = 0x1000 # don't pass on packet infohostname = 'vpnlabserver.com' # hostname of the server
port = 443 # port of the server
cadir = '/volumes/crt/client-certs' # directory of the client certificates'''
Set up the TLS context
'''
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) # create the SSL contextcontext.load_verify_locations(capath=cadir) # load the client certificates
context.verify_mode = ssl.CERT_REQUIRED # verify the client certificates
context.check_hostname = True # check the hostname of the server'''
Create TCP connection
'''
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # create the socket
sock.connect((hostname, port)) # connect to the server'''
Add the TLS
'''
try:ssock = context.wrap_socket( # wrap the socket with TLSsock, server_hostname=hostname, do_handshake_on_connect=False)ssock.do_handshake() # do the TLS handshake
except: # if the TLS handshake failsprint(">>> Certificate failed") # print error messagessock.shutdown(socket.SHUT_RDWR) # shutdown the socketssock.close() # close the socketexit() # exit the program
print("Server hostname: {}".format(ssock.server_hostname)) # print the server hostname'''
Create the tun interface
'''
tun = os.open("/dev/net/tun", os.O_RDWR) # open the tun device
ifr = struct.pack('16sH', b'tun%d', IFF_TUN | IFF_NO_PI) # create the control block
ifname_bytes = fcntl.ioctl(tun, TUNSETIFF, ifr) # create the interface'''
Get the interface name
'''
ifname = ifname_bytes.decode('UTF-8')[:16].strip("\x00") # get the interface name
print("Interface Name: {}".format(ifname)) # print the interface nameos.system("ip addr add 192.168.53.3/24 dev {}".format(ifname)) # set the route
os.system("ip link set dev {} up".format(ifname)) # set the interface up
os.system("ip route add 192.168.60.0/24 dev {} via 192.168.53.3".format(ifname)) # set the routeprint(">>> Preparation done.")'''
Login
'''
usrname = input("Input username: ") # input the username
passwd = getpass.getpass("Input password: ") # input the password
client_auth = IP()
client_auth.src = '192.168.53.3' # set the source IP address
client_auth.dst = '192.168.53.1' # set the destination IP address
ssock.send(bytes(client_auth/bytes(usrname.encode()))) # send the username
ssock.send(bytes(client_auth/bytes(passwd.encode()))) # send the passwordready, _, _ = select.select([ssock, tun], [], []) # wait for the server to send
for fd in ready:data = ssock.recv(2048) # receive the datapkt = IP(data) # create the packetclient_auth_result = pkt[Raw].load # get the resultif client_auth_result == b'0': # if the result is 0print(">>> Login failed") # print error messageprint(">>> Server closed") ssock.shutdown(socket.SHUT_RDWR) # shutdown the socketssock.close() # close the socketexit() # exit the program
print(">>> Login succeed")'''
Main loop
'''
while True:ready, _, _ = select.select([ssock, tun], [], []) # wait for the server to sendfor fd in ready: # for each file descriptorif fd is tun: # if the file descriptor is the tun devicepacket = os.read(tun, 2048) # read the packetpkt = IP(packet) # create the packetprint("=== TUN:\t{}\t-->\t{}\t===".format(pkt.src, pkt.dst)) ssock.send(packet) # send the packetif fd is ssock: # if the file descriptor is the socketdata = ssock.recv(2048) # receive the dataif data != b'': # if the data is not empty# print (">>> Receive {} from {}".format(data, fd.getpeername()))pkt = IP(data) # create the packetprint("=== SOCKET:\t{}\t-->\t{}\t===".format(pkt.src, pkt.dst))os.write(tun, bytes(pkt)) # send the packet to the tun deviceelse: # if the data is emptyprint(">>> Server closed") ssock.shutdown(socket.SHUT_RDWR) # shutdown the socketssock.close() # close the socketexit() # exit the program

v*nserver2.pyv*nserver3.py 同理,只需要修改对应的 IP 地址即可。

以上程序实现了 **TUN 建立、隧道加密、服务器认证、客户端登录、多用户(无多进程)**的功能。程序的每一行都有详细的注释,在此不再赘述各个功能是如何实现的。

到目前为止,所有准备工作均已经完成,文件夹内结构如下所示:

.
├── docker-compose.yml
└── volumes├── crt│   ├── ca.key│   ├── client-certs│   │   ├── eaa14a05.0│   │   └── ca.crt│   ├── demoCA│   ├── myCA_openssl.cnf│   ├── server-certs│   │   ├── vpn.crt│   │   └── vpn.key│   └── vpn.csr├── vpnclient1.py├── vpnclient2.py├── vpnclient3.py└── vpnserver.py

4. 测试

相关命令几乎全是简单的 pingtelnet,此处不再赘述。

5. 总结

本实验较为简单。

【SEED Labs 2.0】Virtual Private Network (V*N) Lab相关推荐

  1. 【SEED Labs 2.0】V*N Tunneling Lab

    本文为 SEED Labs 2.0 - V*N Tunneling Lab 的实验记录. 文章目录 实验原理 Task 1: Network Setup Task 2: Create and Conf ...

  2. 【SEED Labs 2.0】Packet Sniffing and Spoofing Lab

    本文为 SEED Labs 2.0 - Packet Sniffing and Spoofing Lab 的实验记录. 文章目录 实验原理 Lab Task Set 1: Using Scapy to ...

  3. 【SEED Labs 2.0】ARP Cache Poisoning Attack Lab

    本文为 SEED Labs 2.0 - ARP Cache Poisoning Attack Lab 的实验记录. 文章目录 实验原理 Task 1: ARP Cache Poisoning Task ...

  4. 【SEED Labs 2.0】Buffer-Overflow Attack

    本文为 SEED Labs 2.0 - Buffer-Overflow Attack Lab (Server Version) 的实验记录. 实验原理 Task1: Get Familiar with ...

  5. 【SEED Labs 2.0】Cross-Site Request Forgery Attack

    本文为 SEED Labs 2.0 - Cross-Site Request Forgery Attack Lab 的实验记录. 实验原理 在客户机和服务器之间进行请求-响应时,两种最常被用到的方法是 ...

  6. 【计算机网络学习笔记17】网络安全、加密技术、“Virtual Private Network”技术

    [计算机网络学习笔记17]网络安全.加密技术."Virtual Private Network"技术 一.网络安全概述 1.1 网络系统的安全目标: 1.可用性(Availabil ...

  7. 【VMware vSAN 7.0】5.4.2 创建 vSAN 集群

    [VMware vSAN 7.0]5.4.2 创建 vSAN 集群-我们有软硬件解决方案 IT干货 2021-03-31 10:19:32 123 收藏 1 分类专栏: 1.服务器虚拟化集群方案 文章 ...

  8. 【接箱子2.0】新手划过,dalao勿喷

    [接箱子2.0]新手划过,dalao勿喷 哈喽,本喵这个萌新又回来啦!(^_^) 这次前前后后优化了好多次代码,终于- 我的代码突破80行啦! 开心 ------------------------- ...

  9. 扩散模型Diffusion Model 【质量提升2.0】【扩散模型】

    扩散模型Diffusion Model [质量提升2.0][扩散模型] 文章目录 扩散模型Diffusion Model [质量提升2.0][扩散模型] 一.扩散模型简介 二.前向扩散简介 三.逆向扩 ...

最新文章

  1. 推荐一款 Nginx 可视化配置神器
  2. 改进张益唐证明,陶哲轩「他的证明比我还强」,这个天才青年还解决了困扰数学界近80年的「简单问题」...
  3. python编程100行_自己动手写100行Python代码抢火车票!
  4. DHH观点:为什么我偏爱Ruby?
  5. 2019 年百度之星·程序设计大赛 - 初赛一
  6. python中hist函数参数_用hist参数解释Python,python,解读
  7. android p 第三方预装,android P 隐藏API对系统APP的影响
  8. 控件readonly之后的取值
  9. install intel c/c++ compiler
  10. [转载]for循环的执行顺序
  11. 002.FTP配置项详解
  12. VB2010(17)_消息对话框MessageBox
  13. 微信8.0自动发送炸弹python脚本
  14. IADS Revision Note 1: Asymptotic Notations
  15. eda技术试卷_EDA技术试题库
  16. 微信小程序的支付流程 —— 总结
  17. Winsock API编程之UDP小结
  18. git bug分支管理
  19. 中啦 Computers in Biology and Medicine(CIBM)-- 投稿经验分享
  20. postman断言—Chai.js语法总结

热门文章

  1. 1086 就不告诉你 ——C++实现
  2. emqtt-bench安装及使用,包含部分问题的分析解决
  3. 再谈淘宝客PID被劫持之事! [复制链接]
  4. Linux开机引导流程
  5. oracle exp ora 01406,8i9i exp 10g遭遇 ora-1406及ora-1406相关问题
  6. 【Anaconda3】更新conda的方法
  7. kaldi在linux上编译,Ubuntu 12.04下编译安装Kaldi
  8. 基于Kera框架的手写数字识别
  9. SQL语句中‘相关子查询’与‘非相关子查询’有什么区别?
  10. #今日论文推荐#快到离谱,图像识别仅需1纳秒!光子深度神经网络PDNN登上Nature