Skip to content

Is it possible to replace all privileged components? #229

@Lanius-collaris

Description

@Lanius-collaris

Which components require CAP_NET_RAW? mtr and traceroute? Is it possible to replace them?

Q & A about TCP traceroute

1. How to send a specific number of SYN?

Set TCP_SYNCNT socket option.

2. How to reuse a TCP socket?

TCP sockets can dissolve the association by connecting to an address with the sa_family member of sockaddr set to AF_UNSPEC.

3. How to know which packet triggered a ICMP packet?

Use a TCP option you can control, for example, maximum segment size (using TCP_MAXSEG socket option).

PoC

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "net/netip"
    "os"
    "syscall"
    "time"
)

const SO_EE_ORIGIN_ICMP6 = 3

func main() {
    //fastly.jsdelivr.net
    dst := syscall.SockaddrInet6{
        Port:   443,
        ZoneId: 0,
        Addr: [16]byte{
            0x2a, 0x04, 0x4e, 0x42,
            0, 0, 0, 0,
            0, 0, 0, 0,
            0, 0, 0x04, 0x85,
        },
    }
    sock, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
    if err != nil {
        panic(err)
    }
    defer syscall.Close(sock)
    err = syscall.SetNonblock(sock, true)
    if err != nil {
        panic(err)
    }

    err = syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, syscall.SO_TIMESTAMP, 1)
    if err != nil {
        panic(err)
    }
    err = syscall.SetsockoptInt(sock, syscall.IPPROTO_IPV6, syscall.IPV6_RECVHOPLIMIT, 1)
    if err != nil {
        panic(err)
    }
    err = syscall.SetsockoptInt(sock, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1)
    if err != nil {
        panic(err)
    }
    err = syscall.SetsockoptInt(sock, syscall.IPPROTO_IPV6, syscall.IPV6_RECVERR, 1)
    if err != nil {
        panic(err)
    }
    err = syscall.SetsockoptInt(sock, syscall.SOL_TCP, syscall.TCP_SYNCNT, 1)
    if err != nil {
        panic(err)
    }
    err = syscall.SetsockoptInt(sock, syscall.SOL_TCP, syscall.TCP_MAXSEG, 501)
    if err != nil {
        panic(err)
    }

    start := time.Now()
    err = syscall.Connect(sock, &dst)
    if err != nil {
        fmt.Printf("connecting... %v\n", err)
    }
    timer1 := time.NewTimer(5 * time.Millisecond)
    _ = <-timer1.C
    var buf [2048]byte
    var cmsgBuf [1024]byte
    n, cmsgN, flags, from, err := syscall.Recvmsg(sock, buf[:], cmsgBuf[:], syscall.MSG_ERRQUEUE)
    if err != nil {
        fmt.Printf("recvmsg error: %v\n", err)
    }
    fmt.Printf("n: %d\ncmsgN: %d\nflags: 0x%x\nfrom: %v\nmsg: %v\n\n", n, cmsgN, flags, from, buf[:n])
    fmt.Printf("start time\nreadable: %v\nUnixMicro: %d\n\n", start, start.UnixMicro())
    cmsgArr, err := syscall.ParseSocketControlMessage(cmsgBuf[:cmsgN])
    if err != nil {
        panic(err)
    }
    fmt.Printf("cmsgArr: %v\n\n", cmsgArr)
    for _, cmsg := range cmsgArr {
        if cmsg.Header.Level == syscall.SOL_SOCKET && cmsg.Header.Type == syscall.SO_TIMESTAMP {
            if len(cmsg.Data) >= 16 {
                reader := bytes.NewReader(cmsg.Data[:16])
                var sec int64
                var usec int64
                binary.Read(reader, binary.NativeEndian, &sec)
                binary.Read(reader, binary.NativeEndian, &usec)
                fmt.Printf("sec: %d\nusec: %d\n", sec, usec)
                fmt.Printf("cmsg timestamp: %v\n\n", time.Unix(sec, usec*1000))
            }
        }
        if cmsg.Header.Level == syscall.IPPROTO_IPV6 && cmsg.Header.Type == syscall.IPV6_HOPLIMIT {
            if len(cmsg.Data) >= 4 {
                reader := bytes.NewReader(cmsg.Data[:4])
                var hopLimit uint32
                binary.Read(reader, binary.NativeEndian, &hopLimit)
                fmt.Printf("cmsg hop limit: %d\n\n", hopLimit)
            }
        }
        if cmsg.Header.Level == syscall.IPPROTO_IPV6 && cmsg.Header.Type == syscall.IPV6_RECVERR {
            if len(cmsg.Data) >= 16 {
                eeOrigin := cmsg.Data[4]
                eeType := cmsg.Data[5]
                eeCode := cmsg.Data[6]
                fmt.Printf("eeOrigin: %d\neeType: %d\neeCode: %d\n\n", eeOrigin, eeType, eeCode)
                if eeOrigin == SO_EE_ORIGIN_ICMP6 && eeType == 3 && eeCode == 0 {
                    if len(cmsg.Data) >= 44 {
                        addr, _ := netip.AddrFromSlice(cmsg.Data[24:40])
                        fmt.Printf("ICMPv6 Time Exceeded Message from: %v\n", addr)
                    }
                }
            }
        }
    }
    os.WriteFile("cmsg.bin", cmsgBuf[:cmsgN], 0o600)
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions