Treck TCP/IP堆栈漏洞影响数百万IoT设备-附漏洞检测器

Treck TCP/IP堆栈漏洞影响数百万IoT设备-附漏洞检测器

美国网络安全基础设施和安全局(CISA)已警告Treck开发的低级TCP / IP软件库中的严重漏洞,如果将其武器化,则可能允许远程攻击者运行任意命令并安装拒绝服务(DoS )攻击。

这四个漏洞影响Treck TCP / IP堆栈版本6.0.1.67及更早版本,并且已由英特尔报告给该公司。其中两个在严重性上被评为严重。

Treck的嵌入式TCP / IP堆栈已在全球范围内部署在制造,信息技术,医疗保健和运输系统中。

其中最严重的漏洞是Treck HTTP Server组件中的基于堆的缓冲区溢出漏洞(CVE-2020-25066),该漏洞可能允许攻击者崩溃或重置目标设备,甚至执行远程代码。它的CVSS分数是9.8(满分10分)。

第二个缺陷是IPv6组件(CVE-2020-27337,CVSS分数9.1)写越界,未经身份验证的用户可以利用它来通过网络访问导致DoS条件。

其他两个漏洞涉及IPv6组件中的越界读取(CVE-2020-27338,CVSS得分5.9),未经身份验证的攻击者可能会利用该漏洞导致DoS和同一模块中的输入验证不正确(CVE-2020) -27336,CVSS得分3.7),这可能导致通过网络访问最多读取三个字节的越界读取。

Treck建议用户将堆栈更新到版本6.0.1.68,以解决这些缺陷。如果无法应用最新的修补程序,建议实施防火墙规则以过滤出HTTP标头中包含负内容长度的数据包。

以色列网络安全公司JSOF在软件库中发现了19个漏洞(称为Ripple20)之后六个月,才披露了Treck TCP / IP堆栈中的新缺陷,这可能使攻击者无需任何用户交互即可完全控制目标IoT设备。

更重要的是,本月早些时候,Forescout的研究人员发现了33个漏洞(统称为AMNESIA:33)影响了开放源代码TCP / IP协议栈,该行为可能被不良行为者滥用以接管脆弱的系统。

考虑到所涉及的复杂物联网供应链,该公司已发布了一种称为“项目记忆检测器”的新检测工具,以识别目标网络设备是否在实验室环境中运行易受攻击的TCP / IP堆栈。

Treck TCP/IP堆栈漏洞影响数百万IoT设备-附漏洞检测器

Treck TCP/IP堆栈漏洞详情

1.摘要

  • CVSS v3 9.8
  • 注意:远程利用
  • 供应商: Treck Inc.
  • 设备: TCP/IP
  • 漏洞基于堆的缓冲区溢出,越界读取,越界写入

Treck TCP/IP堆栈可能由其他名称所知,例如Kasago TCP/IP,ELMIC,Net + OS,Quadnet,GHNET v2,Kwiknet或AMX。

2.风险评估

成功利用此漏洞可能允许远程执行代码和拒绝服务条件。

3.技术细节

3.1受影响的产品

Treck TCP/IP堆栈6.0.1.67及更低版本的以下组件会受到影响:

  • HTTP服务器 
  • IPv6
  • DHCPv6

3.2漏洞概述

3.2.1    基于堆的缓冲区溢出CWE-122

Treck HTTP Server组件中的漏洞使攻击者可以导致拒绝服务情况。此漏洞还可能导致任意代码执行。

CVE-2020-25066已分配给此漏洞。计算得出的CVSS v3基本分数为9.8;CVSS向量字符串为(AV:N / AC:L / PR:N / UI:N / S:U / C:H / I:H / A:H)。

3.2.2    越界书写CWE-787

IPv6组件中的越界写操作可能会允许未经身份验证的用户通过网络访问潜在地导致可能的拒绝服务。

CVE-2020-27337已分配给此漏洞。计算得出的CVSS v3基本分数为9.1;CVSS向量字符串是(AV:N / AC:L / PR:N / UI:N / S:U / C:N / I:H / A:H)。

3.2.3    越界读取CWE-125

在Treck IPv6中发现了一个问题。DHCPv6客户端组件中的越界读取可能允许未经身份验证的用户通过相邻的网络访问导致可能的拒绝服务。

CVE-2020-27338已分配给此漏洞。计算得出的CVSS v3基本分数为5.9;CVSS向量字符串为(AV:A / AC:H / PR:N / UI:N / S:U / C:H / I:N / A:H)。

3.2.4    越界读取CWE-125

IPv6组件中的输入验证不正确可能会导致未经身份验证的用户通过网络访问导致最多三个字节的越界读取。

CVE-2020-27336已分配给此漏洞。计算得出的CVSS v3基本得分为3.7;CVSS向量字符串是(AV:N / AC:H / PR:N / UI:N / S:U / C:L / I:N / A:N)。

3.3背景

  • 关键基础设施部门:关键制造业,信息技术,医疗保健和公共卫生,运输系统
  • 部署国家/地区:全球
  • 公司总部所在地:美国

3.4研究人员

英特尔向Treck报告了这些漏洞。

4.缓解措施

Treck建议用户使用受影响产品的最新版本(Treck TCP / IP 6.0.1.68或更高版本)。要获取补丁,请发送电子邮件至security@treck.com

Treck建议无法应用最新补丁程序以实施防火墙规则的用户,以过滤掉HTTP标头中包含负内容长度的数据包。

有关漏洞和缓解控制的更多详细信息,请参阅Treck通报。 

CISA建议用户采取防御措施以最大程度地利用此漏洞。具体来说,用户应:

  • 最小化所有控制系统设备和/或系统的网络暴露,并确保不能从Internet访问它们。
  • 在防火墙后面找到控制系统网络和远程设备,并将其与业务网络隔离。
  • 当需要进行远程访问时,请使用安全方法,例如虚拟专用网(VPN),并确认VPN可能存在漏洞,应将其更新为可用的最新版本。还应认识到VPN仅与连接的设备一样安全。

CISA提醒组织在部署防御措施之前进行适当的影响分析和风险评估。

CISA在us-cert.cisa.gov上的ICS页面上还提供了有关控制系统安全性推荐做法的 部分。有几种推荐的做法可供阅读和下载,包括使用“纵深防御”策略提高工业控制系统的网络安全性

其他缓解指南和建议措施可在技术信息文件ICS-TIP-12-146-01B–目标网络入侵检测和缓解策略中的us-cert.cisa.govICS网页上公开获得。 观察到任何可疑恶意活动的组织应遵循其既定的内部程序,并向CISA报告其发现结果,以对其他事件进行跟踪和关联。

需要高水平的技能才能进行开发。目前没有已知的公共利用专门针对这些漏洞。

Treck TCP/IP堆栈漏洞检测工具

project-memoria-detector工具旨在确定目标网络设备是否运行易受攻击的TCP / IP堆栈。

您可以在此处通过GitHub访问该工具。

project-memoria-detector是如何工作的?

该脚本通过三种活动的指纹识别方法识别目标设备上四个TCP / IP堆栈(uIP / Contiki,picoTCP,FNET和Nut / Net)的使用:

  • ICMP探测:脚本执行格式错误的ICMP回显请求,并检查回复的特征,包括生存时间(TTL)值和特定有效负载内容的变化,这些变化随堆栈而变化。
  • TCP选项签名:脚本发送TCP SYN数据包,并监视TCP SYN ACK响应以了解TCP选项字段的格式。每个堆栈使用不同的选项值答复,例如最大段大小(MSS)和窗口比例。
  • TCP紧急标志处理:脚本发送设置了紧急标志的TCP数据包并监视响应。每个堆栈以不同的TCP标志集和不同的TCP窗口大小值答复。

尽管该脚本已经在实验室环境中用受AMNESIA:33个影响的四个堆栈进行了测试,但我们不能保证该脚本的使用对所有可能的设备都是安全的。例如,格式错误的ICMP数据包可能会使运行不同堆栈的设备崩溃。因此,我们不建议将其直接用于具有关键任务设备的实际环境中(例如,具有患者连接设备的医院或具有关键安全性的工业控制系统)。理想的方法是在实验室设置或维护时段内测试设备。

project-memoria-detector.py

#!/usr/bin/python
# project-memoria-detector -- detection tool for vulnerable embedded TCP/IP stacks 
# see https://www.forescout.com/research-labs/amnesia33/

# Copyright (C) 2020 Forescout Technologies, Inc.

# Program License

# "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you."

# All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program for personal, governmental, business or non-profit use. You are prohibited from using the Program in derivative works for commercial purposes. You are prohibited from modifying the Program to be used in a commercial product or service, either alone or in conjunction with other code, either downloadable or accessed as a service. "Derivative works" shall mean any work, whether in source or object form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship.

# You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License applies to the code; keep intact all notices of the absence of any warranty; give all recipients a copy of this License along with the Program; and do not financially benefit from the sale or other conveyance of the Program either alone or in conjunction with other code, downloaded or accessed as a service.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Program and reproducing the content of the copyright notice.


import sys
import subprocess
import argparse
from binascii import hexlify
from scapy.all import conf, send, sr1, IP, ICMP, TCP, Raw, RandShort
import time

DEFAULT_TCP_DPORT=80
DEFAULT_TIMEOUT=4

'''
TCP option signatures
'''
picotcp_tcp_opts_1 = [
        ('MSS', 1460),
        ('SAckOK', b''),
        ('WScale', 0),
        ('Timestamp', None),
        ('NOP', None),
        ('NOP', None),
        ('NOP', None),
        ('NOP', None),
        ('EOL', None),
]

picotcp_tcp_opts_2 = [
        ('WScale', 0),
        ('EOL', None),
]

fnet_tcp_opts = [
        ('MSS', 1460),
        ('WScale', 0),
        ('EOL', None),
]

uip_tcp_opts_1 = [
        ('MSS', 1460),
]

uip_tcp_opts_2 = [
        ('MSS', 1240),
]

nutnet_tcp_opts = [
        ('MSS', 536),
]

'''
This is a helper function that checks the TCP option sequences
'''
def check_tcp_options(tcp_opts, signature):
    # The signatures do not match if they have different lengths 
    if len(signature) != len(tcp_opts):
        return False

    for i in range(0, len(signature)):
        # The signatures do not match if the order of the options is not exact, 
        # or the option values do not match (except when it is set to 'None' in the signature)
        if (tcp_opts[i][0] != signature[i][0]):
            return False
        else:
            if (signature[i][1] != None and signature[i][1] != tcp_opts[i][1]):
                return False
    return True

# MATCHES
MATCH_VUL=3
MATCH_POT=2
MATCH_POT_WEAK=1
MATCH_OTHER=0
MATCH_NO_REPLY=-1

def match_level_str(match_level):
    if match_level >= MATCH_VUL:
        return 'High'
    elif match_level == MATCH_POT:
        return 'Medium'
    elif match_level == MATCH_POT_WEAK:
        return 'Low'
    elif match_level == MATCH_OTHER:
        return 'No match'
    return 'No reply'

'''
This function attempts to actively fingerprint the usage of embedded TCP/IP stacks via ICMPv4 echo requests.
The function performs malformed ICMPv4 echo requests and checks for specific ICMPv4 echo replies.
Based on the response seen, it returns the 'stack_name' string that suggests which embedded TCP/IP stack is used in the DUT.
(currently, only PicoTCP and uIP/Contiki signatures are available)
If none of the expected responses was seen, 'None' is returned.
A match status is also returned (see MATCHES)
'''
def icmpv4_probe(dst_host, timeout):
    icmptype_i=0x8
    icmptype_name_i='ICMP ECHO'
    icmptype_o=0x0
    icmptype_name_o='ICMP ECHO_REPLY'

    response = None
    response2 = None 
    stack_name = ''
    match = ''

    # Send a normal ICMP packet with a 'seq' number other than zero, just to ensure the seq counter at picoTCP is
    # changed and the next packet will be accepted.
    r = sr1(IP(dst=dst_host, ttl=20)/ICMP(id=0xff, seq=1, type=icmptype_i),filter='icmp[icmptype] = {}'.format(icmptype_o), timeout=timeout)
    if not r:
        return (stack_name, match_level_str(MATCH_NO_REPLY))

    # Prepare a malformed ICMP packet
    icmp_raw = b'\x08\x01\x02'
    ipv4_probe = IP(dst=dst_host, ttl=20, proto=0x01)/Raw(load=icmp_raw)

    # Send the malformed ICMP packet
    # If we get the expected response it is either PicoTCP or uIP/Contiki:
    #   - we first check that the TTL value of the echo packet is changed into 64 for the reply packet
    #   - we then check the payload sequence of the echo reply packet
    response = sr1(ipv4_probe, filter='icmp[icmptype] = {}'.format(icmptype_o), timeout=timeout)
    if response:
        if (response.ttl == 64):
            if (hexlify(response.load) == b'0001ff'):
                match = MATCH_VUL 
                stack_name = 'PicoTCP'
            elif (hexlify(response.load) == b'00010a'):
                match = MATCH_VUL
                stack_name = 'uIP/Contiki'
        if not match:
            match = MATCH_OTHER

    else: # we did not get a reply for the first malformed packet
        _id = 0xab
        _seq = 0xba
        # Nut/Net should reply to ICMP packets with incorrect IP and ICMP checksums
        ipv4_probe = IP(dst=dst_host, ttl=20, chksum=0xdead)/ICMP(id=_id, seq=_seq, type=icmptype_i, chksum=0xbeaf)
        response = sr1(ipv4_probe, filter='icmp[icmptype] = {}'.format(icmptype_o), timeout=timeout)
        if response:
            if (response.ttl == 64):
                if (response[ICMP].id == _id and response[ICMP].seq == _seq and response[ICMP].type == 0x00):
                    match = MATCH_POT_WEAK
                    stack_name = 'Nut/Net'
        if not match:
            match = MATCH_OTHER # no reply for the second malformed packet

    return (stack_name, match_level_str(match))


'''
This function attempts to actively fingerprint the usage of embedded TCP/IP stacks via specific TCP signatures.
'''
def tcpv4_probe(dst_host, dst_port, interface, use_fw, timeout):
    # Use the default interface if none is provided
    if interface == None:
        interface = conf.iface

    src_ip_addr = None
    stack_name_tcp = None
    match_tcp = MATCH_OTHER
    stack_name_tcp_opts = None
    stack_name_tcp_urg  = None
    match_tcp_opts = MATCH_OTHER
    match_tcp_urg  = MATCH_OTHER

    try:
        seqn = 0
        ip_lyr = IP(version=0x4, id=0x00fb, dst=dst_host)
        src_port = int(RandShort()._fix()/2+2**15)
        syn = ip_lyr/TCP(dport=dst_port, sport=src_port, flags='S', seq=seqn)

        # We need to set up this rule in order to disable RST packets sent by the Linux kernel
        src_ip_addr = syn.src
        if use_fw:
            subprocess.check_call(['iptables', '-A', 'OUTPUT', '-p', 'tcp', '--tcp-flags', 'RST', 'RST', '-s', '%s' % src_ip_addr, '-j', 'DROP'])

        syn_ack = sr1(syn, timeout=timeout, iface=interface)
        if not syn_ack:
            return (None, match_level_str(MATCH_NO_REPLY))


        # check the TCP options sequence
        uip_tcp_opts_1_match = check_tcp_options(syn_ack[TCP].options, uip_tcp_opts_1)
        uip_tcp_opts_2_match = check_tcp_options(syn_ack[TCP].options, uip_tcp_opts_2)
        fnet_tcp_opts_match = check_tcp_options(syn_ack[TCP].options, fnet_tcp_opts)
        picotcp_tcp_opts_1_match = check_tcp_options(syn_ack[TCP].options, picotcp_tcp_opts_1)
        picotcp_tcp_opts_2_match = check_tcp_options(syn_ack[TCP].options, picotcp_tcp_opts_2)
        nutnet_tcp_opts_match = check_tcp_options(syn_ack[TCP].options, nutnet_tcp_opts)
        timeout2=timeout

        if uip_tcp_opts_1_match or uip_tcp_opts_2_match: 
            match_tcp_opts = MATCH_POT_WEAK 
            stack_name_tcp_opts = 'uIP/Contiki'
        elif fnet_tcp_opts_match:
            match_tcp_opts = MATCH_POT
            stack_name_tcp_opts = 'FNET'
            timeout2=20 # FNET may need a bit more time to send the [FIN, ACK] packet
        elif picotcp_tcp_opts_1_match or picotcp_tcp_opts_2_match:
            match_tcp_opts = MATCH_POT
            stack_name_tcp_opts = 'PicoTCP'
        elif nutnet_tcp_opts_match:
            match_tcp_opts = MATCH_POT_WEAK
            stack_name_tcp_opts = 'Nut/Net' 

        seqn += 1
        ackn = syn_ack[TCP].seq + 1

        ack = ip_lyr/TCP(dport=dst_port, sport=src_port, flags='A', seq=seqn, ack=ackn)
        send(ack, iface=interface)

        tcp_data = b'\x41\x41'
        urgent_offset = 0x00

        urg_pkt  = ip_lyr/TCP(dport=dst_port, sport=src_port, flags='UA', seq=seqn, ack=ackn, urgptr=urgent_offset)/Raw(load=tcp_data)
        urg_resp = sr1(urg_pkt, timeout=timeout2, iface=interface)

        # Check the response to the packet with the Urgent flag set
        if urg_resp:
            if urg_resp[TCP].flags == 'A':
                if urg_resp[TCP].window == 1240 or urg_resp[TCP].window == 1460:
                    stack_name_tcp_urg = 'uIP/Contiki'
                    match_tcp_urg = MATCH_POT_WEAK
                elif urg_resp[TCP].window == 3214:
                    stack_name_tcp_urg = 'Nut/Net'
                    match_tcp_urg = MATCH_POT_WEAK
                else:
                    stack_name_tcp_urg = None
                    match_tcp_urg = MATCH_OTHER

            elif urg_resp[TCP].flags == 'FA' and urg_resp[TCP].window == 2048:
                stack_name_tcp_urg = 'FNET'
                match_tcp_urg = MATCH_POT_WEAK
            elif urg_resp[TCP].flags == 'R' and urg_resp[TCP].window == 0:
                stack_name_tcp_urg = 'PicoTCP'
                match_tcp_urg = MATCH_POT_WEAK
            else:
                stack_name_tcp_urg = None
                match_tcp_urg = MATCH_OTHER

        # If we have a discrepancy between TCP options and TCP Urgent flag fingerprint...
        if stack_name_tcp_opts != stack_name_tcp_urg:
            if match_tcp_opts >= match_tcp_urg:
                stack_name_tcp = stack_name_tcp_opts
                match_tcp = match_tcp_opts
            else:
                stack_name_tcp = stack_name_tcp_urg
                match_tcp = match_tcp_urg

        # If both fingerprints match the same stack...
        else:
            stack_name_tcp = stack_name_tcp_opts
            match_tcp = match_tcp_opts + match_tcp_urg

        # Terminate the connection
        rst = ip_lyr/TCP(dport=dst_port, sport=src_port, flags='R', seq=seqn, ack=ackn)
        send(rst, iface=interface)

    except Exception as ex:
        print('ERROR: {}'.format(ex))

    finally:
        # Cleanup the iptables rule
        if use_fw:
            if src_ip_addr != None:
                subprocess.check_call(['iptables', '-D', 'OUTPUT', '-p', 'tcp', '--tcp-flags', 'RST', 'RST', '-s', '%s' % src_ip_addr, '-j', 'DROP'])

    return (stack_name_tcp, match_level_str(match_tcp))

'''
The is the main code block
'''
if __name__ == '__main__':
    conf.verb = 0 # make Scapy silent

    parser = argparse.ArgumentParser()
    parser.add_argument('ip_dst', help='destination IP address')
    parser.add_argument('-p', '--port', dest='tcp_dport', default=DEFAULT_TCP_DPORT, type=int, nargs='?', help='known open TCP port (default: {})'.format(DEFAULT_TCP_DPORT))
    parser.add_argument('-t', '--timeout', dest='timeout', default=DEFAULT_TIMEOUT, type=int, nargs='?', help='timeout (default: {})'.format(DEFAULT_TIMEOUT))
    parser.add_argument('-i', '--iface', dest='interface', default=None, nargs='?', help='interface name as shown in scapy\'s show_interfaces() function')
    parser.add_argument('-og', '--override-gateway', dest='gw', default=None, const='use_ip_dst', type=str, nargs='?', help='override gateway for ip_dst in scapy routing table')
    parser.add_argument('-fw', '--override-firewall', dest='fw', default=True, const=True, type=bool, nargs='?', help='override firewall')
    args = parser.parse_args()

    gw = None
    if args.gw:
        if args.gw == 'use_ip_dst':
            gw = args.ip_dst
        else:
            gw = args.gw

    if gw:
        conf.route.add(host=(args.ip_dst), gw=gw)

    interface = args.interface
    dst_host = args.ip_dst
    dst_port = args.tcp_dport
    timeout = args.timeout
    fw = args.fw

    if dst_host != None:
        print('{}'.format(dst_host))
        (stack_name_icmp, match_icmp) = icmpv4_probe(dst_host, timeout)
        if stack_name_icmp:
            print('\tICMP fingerprint => the host {} may be running the {} TCP/IP stack ({} level of confidence)'.format(dst_host, stack_name_icmp, match_icmp))
        else:
            print('\tICMP fingerprint => failed to determine the TCP/IP stack (reason: {})'.format(match_icmp))

        if dst_port != None:
            (stack_name_tcp, match_tcp) = tcpv4_probe(dst_host, dst_port, interface, fw, timeout)
            if stack_name_tcp:
                print('\tTCP fingerprint => the host {} may be running the {} TCP/IP stack ({} level of confidence)\n'.format(dst_host, stack_name_tcp, match_tcp))
            else:
                print('\tTCP fingerprint => failed to determine the TCP/IP stack (reason: {})\n'.format(match_tcp))

依存关系

请注意,该工具仅在Linux测试环境中进行了测试(任何现代Linux发行版都可以使用)。假定测试环境使用iptableshttps://linux.die.net/man/8/iptables)作为基本防火墙。

该工具需要使用最新版本的Python 2.x或3.x(请参见https://www.python.org/,最好使用> = 2.7.17或> = 3.7.7版)以及Scapy(请参见https://scapy.net/,版本> = 2.4.3为佳)。

要将Scapy与Python一起安装并进行常规故障排除,请遵循以下文档:scapy.readthedocs.io/en/latest/installation.html 。

用法

由于Scapy需要root特权才能运行,因此该工具必须使用来运行sudo,例如:

$ sudo -E python project-memoria-detector.py [options]

要查看运行脚本的可用选项,请使用以下选项运行它-h

$ python project-memoria-detector.py -h

通常,该脚本至少需要以下三个选项:(1)目标设备的IP地址(-ip_dst),开放的TCP端口(-p)和目标网络接口(-i)。例如:

$ sudo -E python project-memoria-detector.py -ip_dst 192.168.212.42 -p 80 -i eth1

解释结果

在目标设备上运行该工具后,它将首先输出目标的IP地址,然后将输出ICMP指纹识别的结果,然后输出TCP指纹识别的结果。每个匹配的指纹也用圆括号中的匹配置信度水平进行补充。例如:

192.168.43.22
        ICMP fingerprint => the host 192.168.43.22 may be running the uIP/Contiki TCP/IP stack (高可信度)
        TCP fingerprint => the host 192.168.43.22 may be running the uIP/Contiki TCP/IP stack (中等可信度)

在某些指纹不匹配的情况下(例如,签名未知),该工具将产生类似于以下内容的输出(在此示例中,目标设备与任何ICMP指纹都不匹配,但与TCP指纹):

192.168.43.23
        ICMP fingerprint => failed to determine the TCP/IP stack (原因:不匹配)
        TCP fingerprint => the host 192.168.43.23 may be running the FNET TCP/IP stack (高可信度)

如果没有对ICMP和/或TCP消息的答复(例如,设备处于脱机状态,和/或它不响应ICMP回显请求,和/或没有打开的TCP端口),则输出如下所示:

192.168.43.24
        ICMP fingerprint => failed to determine the TCP/IP stack (原因:不匹配)
        TCP fingerprint => failed to determine the TCP/IP stack (原因:不匹配)

from from from