无法接收超时消息。

huangapple go评论96阅读模式
英文:

cannot receive time exceeded message

问题

我正在基于pwnat的思路进行一些测试,它介绍了一种无需第三方的NAT穿透方法:服务器向固定地址(例如3.3.3.3)发送ICMP回显请求数据包,该地址不会返回回显回复。客户端伪装成互联网上的一跳,向服务器发送ICMP超时消息,期望服务器前面的NAT将ICMP超时消息转发给服务器。

在我对3.3.3.3进行ping测试后,我在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无法接收到消息。可能的原因是什么?
1: http://samy.pl/pwnat/

英文:

I'm doing some tests based on the idea of pwnat, it introduced a method for NAT traversal without 3rd party: the server sends ICMP echo request packets to the fixed address(for example, 3.3.3.3) where no echo replies won't be returned from, the client, pretending to be a hop on the Internet, sends an ICMP Time Exceeded packet to the server, expect the NAT in the front of the server to forward the ICMP time exceeded message to the server.
After I pinged to 3.3.3.3, then I run the code below in 192.168.1.100 to listen ICMP messages in Go:

package main
import (
&quot;fmt&quot;
&quot;golang.org/x/net/icmp&quot;
&quot;golang.org/x/net/ipv4&quot;
)
func main() {
c, err := icmp.ListenPacket(&quot;ip4:icmp&quot;, &quot;0.0.0.0&quot;)
if err != nil {
fmt.Println(&quot;listen error&quot;, err)
}
rb := make([]byte, 1500)
for {
n, _, err := c.ReadFrom(rb)
if err != nil {
fmt.Printf(&quot;read err: %s\n&quot;, err)
}
reply, err := icmp.ParseMessage(1, rb[:n])
if err != nil {
fmt.Println(&quot;parse icmp err:&quot;, 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&#39;s data
//fmt.Println(&quot;recv id &quot;, binary.BigEndian.Uint16(timeExceed.Data[22:24]))
fmt.Printf(&quot;ttl exceeded\n&quot;)
}
default:
}
}
}

and a program which runs in 192.168.2.100 to send forged time exceeded message to 192.168.1.100:

package main
import (
&quot;errors&quot;
&quot;fmt&quot;
&quot;golang.org/x/net/icmp&quot;
&quot;golang.org/x/net/ipv4&quot;
&quot;net&quot;
&quot;os&quot;
)
func sendTtle(host string) error {
conn, err := net.Dial(&quot;ip4:icmp&quot;, 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(&quot;3.3.3.3&quot;)
iph, err := h.Marshal()
if err != nil {
fmt.Println(&quot;ip header error&quot;, err)
return err
}
// 8 bytes of original datagram&#39;s data
echo := icmp.Message{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &amp;icmp.Echo{
ID: 3456, Seq: 1,
}}
oriReq, err := echo.Marshal(nil)
if err != nil {
return errors.New(&quot;Marshal error&quot;)
}
data := append(iph, oriReq...)
te := icmp.Message{
Type: ipv4.ICMPTypeTimeExceeded,
Code: 0,
Body: &amp;icmp.TimeExceeded{
Data: data,
}}
if buf, err := te.Marshal(nil); err == nil {
fmt.Println(&quot;sent&quot;)
if _, err := conn.Write(buf); err != nil {
return errors.New(&quot;write error&quot;)
}
} else {
return errors.New(&quot;Marshal error&quot;)
}
return nil
}
func main() {
argc := len(os.Args)
if argc &lt; 2 {
fmt.Println(&quot;usage: prpgram + host&quot;)
return
}
if err := sendTtle(os.Args[1]); err != nil {
fmt.Println(&quot;failed to send TTL exceeded message: &quot;, err)
}
}

the problem is 192.168.1.100 cannot receive the message. What're the possible reasons?
1: http://samy.pl/pwnat/

答案1

得分: 2

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

首先,你没有获取到由192.168.2.100发送到3.3.3.3的回显请求的标识符,该标识符将通过NAPT(如果有)唯一映射到外部查询ID,以便将来可以将具有相同查询ID的ICMP回显应答路由到发送者。根据rfc 3022 ICMP错误数据包修改部分

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

其次,根据rfc 5508:

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

因此,伪造的超时消息将无法通过。这里有更多详细信息。

英文:

Your code has no problem. If you run your code in the same network(I mean no NAT/router involvement), the program will receive time exceeded message as expected. The reason is the theory pwnat uses doesn't work nowadays.

  • First, you didn't get the identifier of the echo request sent by
    192.168.2.100 to 3.3.3.3, the identifier will be uniquely
    mapped to an external query ID by NAPT(if any) so that it can route
    future ICMP Echo Replies with the same query ID to the sender. According to rfc 3022 ICMP error packet modifications section,

    > In a NAPT setup, if the IP message embedded within ICMP happens to be
    > a TCP, UDP or ICMP Query packet, you will also need to modify the
    > appropriate TU port number within the TCP/UDP header or the Query
    > Identifier field in the ICMP Query header.

  • Second, according to rfc 5508:

    > If a NAT device receives an ICMP Error packet from the private realm,
    > and the NAT does not have an active mapping for the embedded payload,
    > the NAT SHOULD silently drop the ICMP Error packet.

So the forged time exceeded message wouldn't get through. Here is more details about this.

huangapple
  • 本文由 发表于 2017年2月8日 22:36:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/42115989.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定