无法接收超时消息

Posted

技术标签:

【中文标题】无法接收超时消息【英文标题】:cannot receive time exceeded message 【发布时间】:2017-06-26 05:45:52 【问题描述】:

我正在根据pwnat的思想做一些测试,它介绍了一种无第三方NAT穿越的方法:服务器将ICMP echo请求包发送到固定地址(例如3.3.3.3),其中没有echo回复不会返回,客户端伪装成互联网上的一跳,向服务端发送ICMP Time Exceeded报文,期望服务端的NAT转发ICMP超时报文给服务端. 在我 ping 到 3.3.3.3 之后,我在 192.168.1.100 中运行以下代码以在 Go 中侦听 ICMP 消息:

package main

import (
    "fmt"
    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
)

func main() 
    c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
    if err != nil 
        fmt.Println("listen error", err)
    
    rb := make([]byte, 1500)

    for 
        n, _, err := c.ReadFrom(rb)
        if err != nil 
            fmt.Printf("read err: %s\n", err)
        
        reply, err := icmp.ParseMessage(1, rb[:n])
        if err != nil 
            fmt.Println("parse icmp err:", err)
            return
        

        switch reply.Type 
        case ipv4.ICMPTypeTimeExceeded:
            if _, ok := reply.Body.(*icmp.TimeExceeded); ok 
                // internet header(20 bytes) plus the first 64 bits of the original datagram's data
                //fmt.Println("recv id ", binary.BigEndian.Uint16(timeExceed.Data[22:24]))
                fmt.Printf("ttl exceeded\n")
            
        default:
        
    

还有一个在192.168.2.100 中运行的程序,用于向192.168.1.100 发送伪造的超时消息:

package main

import (
    "errors"
    "fmt"
    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
    "net"
    "os"
)

func sendTtle(host string) error 
    conn, err := net.Dial("ip4:icmp", host)

    if err != nil 
        return err
    

    // original IP header
    h := ipv4.Header
        Version:  4,
        Len:      20,
        TotalLen: 20 + 8,
        TTL:      64,
        Protocol: 1,
    
    h.Src = net.ParseIP(host)
    h.Dst = net.ParseIP("3.3.3.3")
    iph, err := h.Marshal()
    if err != nil 
        fmt.Println("ip header error", err)
        return err
    

    // 8 bytes of original datagram's data
    echo := icmp.Message
        Type: ipv4.ICMPTypeEcho, Code: 0,
        Body: &icmp.Echo
            ID: 3456, Seq: 1,
        

    oriReq, err := echo.Marshal(nil)
    if err != nil 
        return errors.New("Marshal error")
    
    data := append(iph, oriReq...)

    te := icmp.Message
        Type: ipv4.ICMPTypeTimeExceeded,
        Code: 0,
        Body: &icmp.TimeExceeded
            Data: data,
        

    if buf, err := te.Marshal(nil); err == nil 
        fmt.Println("sent")
        if _, err := conn.Write(buf); err != nil 
            return errors.New("write error")
        
     else 
        return errors.New("Marshal error")
    

    return nil


func main() 
    argc := len(os.Args)
    if argc < 2 
        fmt.Println("usage: prpgram + host")
        return
    
    if err := sendTtle(os.Args[1]); err != nil 
        fmt.Println("failed to send TTL exceeded message: ", err)
    

问题是192.168.1.100 收不到消息。可能的原因是什么?

【问题讨论】:

不,我将Dst设置为3.3.3.3是为了假装超时消息是由192.168.2.100发送到3.3.3.3的回显请求引起的。 好的。你的代码对我有用。服务器打印“ttl 超出”。你确定你的机器在 192.168.1.100 和 192.168.2.100 之间有连接吗?这些机器听起来好像它们可能位于不同的子网中,因此请确保您可以在它们之间进行路由,并且没有防火墙将事情搞砸。尝试在客户端和服务器上使用 tcpdump 进行调试,您将看到数据包是否出现,并且看起来应该出现。 感谢您的回复。两个子网之间存在连接。路由器似乎丢弃了来自wireshark的数据包。 【参考方案1】:

您的代码没有问题。如果您在同一个网络中运行代码(我的意思是不涉及 NAT/路由器),程序将按预期收到超时消息。原因是 pwnat 使用的理论现在已经行不通了。

首先,您没有得到由 192.168.2.1003.3.3.3,标识符将是唯一的 通过 NAPT(如果有)映射到外部查询 ID,以便它可以路由 未来的 ICMP Echo 以相同的查询 ID 回复发送者。根据rfc 3022 ICMP error packet modifications section,

在 NAPT 设置中,如果嵌入在 ICMP 中的 IP 消息恰好是 TCP、UDP 或 ICMP 查询数据包,您还需要修改 TCP/UDP 标头或查询中的适当 TU 端口号 ICMP 查询标头中的标识符字段。

其次,根据 rfc 5508:

如果 NAT 设备从私有领域收到 ICMP 错误数据包, 并且 NAT 没有嵌入有效负载的活动映射, NAT 应该静默丢弃 ICMP 错误数据包。

所以伪造的超时消息不会通过。 Here 对此有更多详细信息。

【讨论】:

以上是关于无法接收超时消息的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有超时/死锁的情况下在PROMELA进程中发送和接收?

阻塞非阻塞超时(同步与异步)

Selenium 超时接收来自渲染器的消息

从渲染器接收消息超时:0.100 条日志消息使用 ChromeDriver 和 Chrome v80 通过 Selenium Java

错误 [严重]:从渲染器接收消息超时:20.000 在 Jenkins 上通过 Selenium 执行测试套件时

java socket 客户端设置未接收到数据的超时问题