网络-ICMP协议、Ping命令实现与ICMP攻击

目录

简介

报文格式

差错报文

常见类型

不发送ICMP差错报文的情况

询问报文

常见类型

实战

简单Ping命令实现

抓包

手算校验和

全部代码

Traceroute

抓包

ICMP重定向攻击

原理

攻击

防御

参考


简介

ICMP(Internet Control Message Protocol,网际控制报文协议)是TCP/IP协议簇的一个子协议,用于在主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。

报文格式

ICMP报文分为两类:一类通知出错原因 ,一类用于诊断查询。

ICMP报文格式
  • 类型:占一字节,标识ICMP报文的类型,从类型值来看ICMP报文可以分为两大类。第一类是取值为1~127的差错报文,第2类是取值128以上的信息报文。
  • 代码:占一字节,标识对应ICMP报文的代码,它与类型字段一起共同标识了ICMP报文的详细类型。
  • 校验和:占两个字节,这是对包括ICMP报文数据部分在内的整个ICMP数据报的校验和,以检验报文在传输过程中是否出现了差错

差错报文

差错报文格式

常见类型

  • 3    目标/终点不可达:无法交付。当路由器或主机不能交付数据报时就向源点发送终点不可达报文。
  • 4    源点抑制
  • 5    重定向/改变路由:更好路由。路由器把改变路由报文发送给主机,让主机知道下次应将数据报发送给另外的路由器(可通过更好的路由)。
  • 11   超时:TTL=0。当路由器收到生存时间TTL=0的数据报时,除丢弃数据包外,还要向源点发送时间超过报文。当终点在预先规定的时间内不能收到一个数据报的全部数据报片时,就把已收到的数据报片都丢弃,并向源点发送时间超过报文。
  • 12  参数问题:首部字段有问题。当路由器或目的主机收到的数据报的首部中有的字段的值不正确时,就丢弃该数据报,并向源点发送参数问题报文。

4已弃用

不发送ICMP差错报文的情况

  • ICMP差错报告报文不再发送ICMP差错报告报文(否则可能死循环)
  • 对第一个分片的数据报片的所有后续数据报片都不发送ICMP差错报告报文。
  • 目的地址是多/广播地址的数据报都不发送ICMP差错报告报文。
  • 作为链路层广播的数据报都不发送ICMP差错报告报文。
  • 对具有特殊地址(如127.0.0.0或0.0.0.0)的数据报不发送ICMP差错报告报文。

询问报文

常见类型

  • 0    回送应答:收到回送请求报文的主机必须给源主机或路由器发送ICMP回送应答报文,表示本身可达以及自身状态。
  • 8    回送请求:主机或路由器向特定目的主机发出的询问,测试目的主机是否可达以及了解其相关状态。
  • 13  时间戳请求:请某个主机或路由器回答当前的日期和时间,用来进行时钟同步和测量时间。
  • 14  时间戳响应:收到时间戳请求报文的主机回答当前的日期和时间,用来进行时钟同步和测量时间。
  • 15  路由器应答
  • 16  路由器请求
  • 17  地址子网请求
  • 18  地址子网应答

后4个已经弃用了

实战

简单Ping命令实现

PING(Packet InterNet Groper)分组网间探测,用来测试两台主机之间的连通性。当然,也可能由于防火墙或具有白名单功能的路由器而无法Ping通。

抓包

打开WireShark,筛选icmp数据报,cmd命令,ping一下 www.baidu.com。

ping百度结果
回送请求报文
  • 类型:8(08),表示回送请求类型。
  • 代码:0(00)。
  • 校验和:4d 54,correct,校验正确。
  • Identifier、Sequence number:00 01 00 07。
  • 数据:如上图蓝色阴影部分。
回送应答报文

  • 类型:0(00),表示回送请求类型。
  • 代码:0(00)。
  • 校验和:45 54,correct,校验正确。
  • Identifier、Sequence number:00 01 00 07。
  • 数据:如上图蓝色阴影部分。

综上,Ping命令测试两个主机之间的连通性,使用了ICMP发送请求和回答报文

手算校验和

ICMP报文中的各个部分中,校验和是相对较难的。先看一下如何计算。

RFC 792校验和描述

首先,校验和字段置为0,然后,对首部中每个16bit进行二进制反码求和,结果存在校验和字段中。

回送请求报文

08 00 4d 54 00 01 00 07 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 61 62 63 64 65 66 67 68 69

校验码置0

08 00 00 00 00 01 00 07 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 61 62 63 64 65 66 67 68 69

16bit一组

0800 0000 0001 0007 6162 6364 6566 6768 696a 6b6c 6d6e 6f70 7172 7374 7576 7761 6263 6465 6667 6869

校验和计算过程

按列计算,右侧第一列加和结果为7(111),剩1,进11,右侧第二列加和结果为9+1(进位)=10(1010),剩0进101。同理,最后将超出16位部分加和,结果回加到低位,得到求和结果,取反得到校验和。不习惯按列加的可以两两相加(待加个数超过16个时没有按列快,因为按列总是16列),以下代码使用的是两两加和

全部代码

"""
--coding:utf-8--
@File: Ping.py
@Author:frank yu
@DateTime: 2020.12.13 10:35
@Contact: frankyu112058@gmail.com
@Description:Implement of Ping
"""
import random
import socket
import struct
import time


def checksum(msg):
    """
    :param msg:icmp message(bytes)
    :return:checksum(bytes)
    """
    check_sum = 0
    n = len(msg)

    def carry_around_add(a, b):
        c = a + b
        return (c & 0xffff) + (c >> 16)

    for i in range(0, n, 2):
        w = msg[i] + (msg[i + 1] << 8)
        check_sum = carry_around_add(check_sum, w)
    res = ~check_sum & 0xffff
    res = res >> 8 | (res << 8 & 0xff00)
    return res


def icmp_packet(sequence_number):
    """
    :param sequence_number:
    :return: binary of icmp packet
    """
    icmp_type = 8  # ICMP Echo Request
    icmp_code = 0  # zero
    icmp_checksum = 0  # set to zero first
    icmp_Identifier = 1  # Identifier
    icmp_Sequence_number = sequence_number
    icmp_Data = b'abcdefghijklmnopqrstuvwabcdefghi'  # data
    icmp_message = struct.pack('>2B3H32s', icmp_type, icmp_code, icmp_checksum, icmp_Identifier, icmp_Sequence_number,
                               icmp_Data)
    icmp_checksum = checksum(icmp_message)
    icmp_message = struct.pack('>2B3H32s', icmp_type, icmp_code, icmp_checksum, icmp_Identifier, icmp_Sequence_number,
                               icmp_Data)
    return icmp_message


def icmp_request(dst_addr, pkt, timeout=2):
    """
    send icmp packet and return socket for listening
    :param timeout: timeout
    :param dst_addr: ip of destination address
    :param pkt: packet of icmp
    :return: socket of icmp,time
    """
    icmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
    icmp_socket.settimeout(timeout)
    icmp_socket.sendto(pkt, (dst_addr, 80))
    send_time = time.time()
    return icmp_socket, send_time


def icmp_reply(icmp_socket, send_time, sequence_num):
    """
    monitor the icmp socket and return how much time spent if receives reply msg
    :param icmp_socket:socket which sent icmp msg before
    :param send_time: time when send icmp request
    :param sequence_num:sequence num
    :return:time and TTL/-1,-1
    """
    try:
        recv_pkt, addr = icmp_socket.recvfrom(1024)
        # print(recv_pkt)
        recv_time = time.time()
        icmpHeader = recv_pkt[20:28]
        type, _, _, _, sequence = struct.unpack(">2B3H", icmpHeader)
        if type == 0 and sequence == sequence_num:
            return recv_time - send_time, recv_pkt[8]
    except socket.timeout:
        return -1, -1


def ping(host):
    """
    :param host:domain name or ip addr
    :return: None
    """
    Sequence_number = random.randint(0, 10 ** 4)
    # 若为ip,不变;若为域名,转为ip
    try:
        dst_addr = socket.gethostbyname(host)
    except socket.gaierror:
        print(f'something wrong, please check your input.')
        exit(0)
    miss, short, long, alltime = 0, 10 ** 9, 0, []
    print(f"正在 Ping {host} [{dst_addr}] 具有 32 字节的数据:")
    for i in range(0, 4):
        # 构造icmp数据包
        icmp_pkt = icmp_packet(Sequence_number + i)
        # print(icmp_pkt)
        # 发送并记录时间
        icmp_socket, send_time = icmp_request(dst_addr, icmp_pkt)
        # 接收并计算时间差
        times, TTL = icmp_reply(icmp_socket, send_time, Sequence_number + i)
        if times >= 0:
            print(f"来自 {dst_addr} 的回复: 字节=32 时间={int(times * 1000)}ms TTL={TTL}")
            if short > times:
                short = times
            if long < times:
                long = times
            alltime.append(times * 1000)
            time.sleep(1)
        else:
            print("请求超时。")
            miss += 1
    print()
    print(f'{dst_addr} 的 Ping 统计信息:\n'
          f'    数据包: 已发送 = 4,已接收 = {4 - miss},丢失 = {miss} ({int(miss / 4 * 100)}% 丢失),')
    if miss < 4:
        print('往返行程的估计时间(以毫秒为单位):\n'
              f'    最短 = {int(short * 1000)}ms,最长 = {int(long * 1000)}ms,平均 = {int(sum(alltime) / (4 - miss))}ms')
    return None


if __name__ == '__main__':
    host = input('please input domain name or ip addr:')
    ping(host)

结果如下:

结果截图

主机A ping 主机B ---> 构建 ICMP 包 ----> 构建 IP 分组 ----> 解析硬件地址封装成帧 ----> 物理层发送 ----> 网络层传输 ----> 到达主机B ----> 提取IP数据包交给IP层协议 ----> 提取信息交给ICMP协议,构建ICMP应答包 ----> 发送给主机A

Traceroute

这是UNIX系统的名字,在Windows中是tracert。

一个分组从源点到终点的路,使用了ICMP时间超过差错报告报文。

抓包

tracert 百度抓包
标题
  •  类型:11(0b),表示超时类型。
  • 代码:0(00)。
  • 校验和:f4 ff,correct,校验正确。
  • Unused:00 00 00 00。
  • 数据:剩下的部分。

标题
  •  类型:3(03),表示终点不可达类型。
  • 代码:3(03),表示终点不可达中的端口不可达类型。
  • 校验和:b9 dd,correct,校验正确。
  • Unused:00 00 00 00。
  • 数据:剩下的部分。

Traceroute从源主机向目的主机发送一连串的IP数据报,封装的是无法交付的UDP数据报(端口大于30000)。第一个数据报P1的生存时间TTL设置为1。当P1到达路径上的第一个路由器R1时,路由器R1先收下它,接着把TTL的值减1。由于TTL等于零了,R1就把P1丢弃了,并向源主机发送一个ICMP时间超过差错报告报文,源主机接着发送第二个数据报P2,并把TTL设置为2。P2先到达路由器R1,R1收下后把TTL减1再转发给路由器R2。R2收到P2时TTL为1,但减1后TTL变为零了。R2就丢弃P2,并向源主机发送一个ICMP时间超过差错报告报文。这样一直继续下去。当最后一个数据报刚刚到达目的主机时,数据报的TTL是1。主机不转发数据报,也不把TTL值减1。但因IP数据报中封装的是无法交付的UDP数据报,因此目的主机要向源主机发送ICMP终点不可达差错报告报文。这样,源主机达到了自己的目的,因为这些路由器和最后目的主机发来的ICMP报文正好给出了源主机想知道的路由信息一到达目的主机所经过的路由器的IP地址,以及到达其中的每一个路由器的往返时间。

ICMP重定向攻击

原理

ICMP重定向信息是路由器向主机提供实时的路由信息,当一个主机收到ICMP重定向信息(上面提到,type=5)时,它就会根据这个信息来更新自己的路由表。由于缺乏必要的合法性检查,如果一个黑客想要被攻击的主机修改它的路由表,黑客就会发送ICMP重定向信息给被攻击的主机,让该主机按照黑客的要求来修改路由表

攻击

攻击机

操作系统:kali

使用软件:netwox

IP:10.28..214.132

靶机

操作系统:Win10

IP:10.28..214.51

网关:10.28.128.1

靶机Ping网关与攻击机

攻击机攻击

打开ettercap,点击对勾

ettercap -G

点击搜索主机,打开主机列表

选择ICMP重定向攻击

输入网关MAC地址和IP地址

打开wireshark抓包,靶机浏览器打开几个网页。可以看到ICMP重定向报文。

点击可查看,网关已被修改为攻击机IP。

Ettercap显示靶机在访问的外网站点IP及端口。

 攻击机可以获得靶机发送的数据,但是无法接收从外网返回的数据,属于半个中间人攻击。

防御

(1)网关端:

关闭ICMP重定向(no ip redirects)。

变长子网掩码划分网段。

使用网络控制列表(ACL)和代理。

(2)主机端:

可以使用防火墙等过滤掉ICMP报文,或使用反间谍软件监控。

结合防ARP、IP欺骗等进行防御。 

参考

《TCP/IP详解卷1》第六、七、八章

《谢希仁 计算机网络第7版》4.4

stackoverflow-checksum-icmp-python-with-wireshark

RFC 792

ettercap的YouTube频道

ettercap的GitHub

更多内容查看:网络安全-自学笔记

喜欢本文的请动动小手点个赞,收藏一下,有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。如果您感觉有所收获,自愿打赏,可选择支付宝18833895206(小于),您的支持是我不断更新的动力。

  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lady_killer9

感谢您的打赏,我会加倍努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值