wrong checksum, low ttl

 avatar
unknown
golang
2 months ago
19 kB
6
Indexable
package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"strings"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"github.com/jackpal/gateway"
)

const (
	synTimeout            = 5 * time.Second
	arpTimeout            = 5 * time.Second
	defaultTTL            = 64
	httpPort              = 80
	httpUserAgent         = "Raw-PCAP-HTTP-Client/1.0"
	packetSnapLen         = 65535
	promiscuousMode       = false
	maxARPAttempts        = 3
	tcpSynRetries         = 3
	tcpFinRetries         = 3
	tcpAckRetries         = 3
	responseTimeout       = 10 * time.Second
	initialSequenceNumber = 1000
	initialWindowSize     = 65535
)

type tcpClient struct {
	mac                   *net.HardwareAddr
	dstMac                *net.HardwareAddr
	device                string
	dst, gw, src          net.IP
	handle                *pcap.Handle
	opts                  gopacket.SerializeOptions
	buf                   gopacket.SerializeBuffer
	open                  []string
	sequenceNumber        uint32
	acknowledgementNumber uint32
	sourcePort            layers.TCPPort
	destinationPort       layers.TCPPort
	ttl                   uint8  // TTL for IP packets, added to tcpClient struct
	hostname              string // Hostname for HTTP Host header
}

func newTcpClient(targetHost string, targetIP net.IP, ttl uint8) (*tcpClient, error) {
	c := &tcpClient{
		dst: targetIP,
		opts: gopacket.SerializeOptions{
			FixLengths:       true,
			ComputeChecksums: true,
		},
		buf:                   gopacket.NewSerializeBuffer(),
		sequenceNumber:        initialSequenceNumber,
		acknowledgementNumber: 0,
		sourcePort:            layers.TCPPort(54321),
		destinationPort:       layers.TCPPort(httpPort), // Still default to HTTP port, can be argument if needed
		ttl:                   ttl,                      // Set TTL from argument
		hostname:              targetHost,               // Store hostname for HTTP header
	}

	device, mac, gw, src, err := getInterface()
	if err != nil {
		return nil, err
	}
	log.Printf("Scanning host %v (%v) with interface %v, gateway %v, src %v", targetHost, targetIP, device, gw, src)
	c.gw, c.src, c.device, c.mac = *gw, *src, device, mac

	dstMac, err := GetARP(c.dst, c.gw, c.src, c.device, *c.mac)
	if err != nil {
		return nil, fmt.Errorf("ARP resolution failed: %w", err)
	}
	c.dstMac = dstMac

	handle, err := pcap.OpenLive(device, packetSnapLen, promiscuousMode, pcap.BlockForever)
	if err != nil {
		return nil, err
	}
	c.handle = handle
	return c, nil
}

func (c *tcpClient) close() {
	c.handle.Close()
}

func (c *tcpClient) send(layers ...gopacket.SerializableLayer) error {
	if err := gopacket.SerializeLayers(c.buf, c.opts, layers...); err != nil {
		return err
	}
	return c.handle.WritePacketData(c.buf.Bytes())
}

func (c *tcpClient) sendTCPSYN() error {
	eth := &layers.Ethernet{SrcMAC: *c.mac, DstMAC: *c.dstMac, EthernetType: layers.EthernetTypeIPv4}
	ip := &layers.IPv4{
		SrcIP:    c.src,
		DstIP:    c.dst,
		Version:  4,
		TTL:      c.ttl, // Use TTL from tcpClient struct
		Protocol: layers.IPProtocolTCP,
	}
	tcp := &layers.TCP{
		SrcPort: c.sourcePort,
		DstPort: c.destinationPort,
		SYN:     true,
		Seq:     c.sequenceNumber,
		Window:  initialWindowSize,
	}
	if err := tcp.SetNetworkLayerForChecksum(ip); err != nil {
		return err
	}
	if err := c.send(eth, ip, tcp); err != nil {
		return err
	}
	log.Printf("TCP SYN sent to %s:%d, seq=%d", c.dst, c.destinationPort, c.sequenceNumber)
	return nil
}

func (c *tcpClient) receiveTCPSYNACK() (*layers.TCP, error) {
	portFilter := fmt.Sprintf("tcp and src host %s and src port %d and dst port %d and tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)",
		c.dst.String(), c.destinationPort, c.sourcePort)

	if err := c.handle.SetBPFFilter(portFilter); err != nil {
		return nil, fmt.Errorf("BPF filter error for SYN-ACK: %w, filter: %s", err, portFilter)
	}
	defer c.handle.SetBPFFilter("")

	packetSource := gopacket.NewPacketSource(c.handle, c.handle.LinkType())
	start := time.Now()
	for time.Since(start) < synTimeout {
		packet, err := packetSource.NextPacket()
		if err != nil {
			continue
		}

		tcpLayer := packet.Layer(layers.LayerTypeTCP)
		if tcp, ok := tcpLayer.(*layers.TCP); ok && tcp.SYN && tcp.ACK && tcp.SrcPort == c.destinationPort && tcp.DstPort == c.sourcePort {
			log.Printf("TCP SYN-ACK received from %s:%d, ack=%d, seq=%d", c.dst, tcp.SrcPort, tcp.Ack, tcp.Seq)
			return tcp, nil
		}
	}
	return nil, fmt.Errorf("no TCP SYN-ACK response received after %v", synTimeout)
}

func (c *tcpClient) sendTCPACK(ackNumber, seqNumber uint32) error {
	eth := &layers.Ethernet{SrcMAC: *c.mac, DstMAC: *c.dstMac, EthernetType: layers.EthernetTypeIPv4}
	ip := &layers.IPv4{
		SrcIP:    c.src,
		DstIP:    c.dst,
		Version:  4,
		TTL:      c.ttl, // Use TTL from tcpClient struct
		Protocol: layers.IPProtocolTCP,
	}
	tcp := &layers.TCP{
		SrcPort: c.sourcePort,
		DstPort: c.destinationPort,
		ACK:     true,
		Seq:     seqNumber,
		Ack:     ackNumber,
		Window:  initialWindowSize,
	}
	if err := tcp.SetNetworkLayerForChecksum(ip); err != nil {
		return err
	}

	log.Printf("Sending TCP ACK, seq=%d, ack=%d to %s:%d", seqNumber, ackNumber, c.dst, c.destinationPort)
	return c.send(eth, ip, tcp)
}

func (c *tcpClient) performTCPHandshake() error {
	for i := 0; i < tcpSynRetries; i++ {
		c.sequenceNumber = initialSequenceNumber + uint32(i)
		if err := c.sendTCPSYN(); err != nil {
			log.Printf("SYN send attempt %d failed: %v", i+1, err)
			continue
		}

		synAckResponse, err := c.receiveTCPSYNACK()
		if err == nil {
			c.acknowledgementNumber = synAckResponse.Seq + 1
			c.sequenceNumber++
			log.Printf("SYN-ACK received, server seq=%d, client ack=%d", synAckResponse.Seq, c.acknowledgementNumber)
			break
		}
		log.Printf("Retrying SYN... attempt %d/%d: %v", i+1, tcpSynRetries, err)
		time.Sleep(time.Duration(i+1) * time.Second)
	}

	if c.sequenceNumber == initialSequenceNumber {
		return fmt.Errorf("TCP handshake failed after %d SYN retries", tcpSynRetries)
	}

	for i := 0; i < tcpAckRetries; i++ {
		err := c.sendTCPACK(c.acknowledgementNumber, c.sequenceNumber)
		if err == nil {
			log.Println("TCP ACK sent successfully.")
			break
		} else {
			log.Printf("ACK send attempt %d failed: %v", i+1, err)
			if i < tcpAckRetries-1 {
				time.Sleep(time.Duration(i+1) * time.Second)
			} else {
				return fmt.Errorf("ACK send failed after %d retries: %w", tcpAckRetries, err)
			}
		}
	}

	if c.acknowledgementNumber == 0 {
		return fmt.Errorf("TCP handshake failed after %d ACK retries", tcpAckRetries)
	}

	log.Println("TCP handshake completed.")
	return nil
}

func (c *tcpClient) sendHTTPGetRequest(wrongChecksum bool, ttl uint8, hostname string) error {
	eth := layers.Ethernet{
		SrcMAC:       *c.mac,
		DstMAC:       *c.dstMac,
		EthernetType: layers.EthernetTypeIPv4,
	}
	ip4 := layers.IPv4{
		SrcIP:    c.src,
		DstIP:    c.dst,
		Version:  4,
		TTL:      ttl,
		Protocol: layers.IPProtocolTCP,
	}
	tcp := layers.TCP{
		SrcPort: c.sourcePort,
		DstPort: c.destinationPort,
		ACK:     true,
		Seq:     c.sequenceNumber,
		Ack:     c.acknowledgementNumber,
		Window:  initialWindowSize,
		PSH:     true,
	}
	if err := tcp.SetNetworkLayerForChecksum(&ip4); err != nil {
		return err
	}

	httpPayload := []byte("GET / HTTP/1.1\r\nHost: " + hostname + "\r\nUser-Agent: " + httpUserAgent + "\r\nConnection: close\r\n\r\n")

	opts := gopacket.SerializeOptions{
		FixLengths:       true,
		ComputeChecksums: !wrongChecksum,
	}
	buffer := gopacket.NewSerializeBuffer()
	if err := gopacket.SerializeLayers(buffer, opts, &eth, &ip4, &tcp, gopacket.Payload(httpPayload)); err != nil {
		return err
	}

	packetData := buffer.Bytes()

	if wrongChecksum {
		// Assuming IPv4 header is 20 bytes, TCP starts after it
		tcpLayer := packetData[20:]

		// TCP checksum is at bytes 16-17 (offset 16, 2 bytes) in TCP header
		checksumOffset := 16
		originalChecksum := uint16(tcpLayer[checksumOffset])<<8 | uint16(tcpLayer[checksumOffset+1])

		// XOR and Flip a bit in the checksum to corrupt it
		corruptedChecksum := originalChecksum ^ uint16(0x00FF)

		// Update the checksum bytes in the packet data with the corrupted value (little-endian)
		tcpLayer[checksumOffset] = byte(corruptedChecksum >> 8)
		tcpLayer[checksumOffset+1] = byte(corruptedChecksum & 0xFF)

		log.Printf("Original TCP Checksum: 0x%04X", originalChecksum)
		log.Printf("Corrupted TCP Checksum: 0x%04X", corruptedChecksum)
	}

	err := c.handle.WritePacketData(packetData)
	if err != nil {
		log.Printf("Error sending HTTP GET request to port %v: %v", c.destinationPort, err)
		return err
	}
	log.Printf("HTTP GET request sent to %s(%s):%d, wrongChecksum=%v", c.hostname, c.dst, c.destinationPort, wrongChecksum)
	return nil
}

func (c *tcpClient) receiveTCPResponse() (gopacket.Packet, error) {
	filter := fmt.Sprintf("tcp and src host %s and src port %d and dst port %d", c.dst.String(), c.destinationPort, c.sourcePort)
	if err := c.handle.SetBPFFilter(filter); err != nil {
		return nil, fmt.Errorf("BPF filter error for TCP response: %w, filter: %s", err, filter)
	}
	defer c.handle.SetBPFFilter("")

	packetSource := gopacket.NewPacketSource(c.handle, c.handle.LinkType())
	start := time.Now()
	for time.Since(start) < responseTimeout {
		packet, err := packetSource.NextPacket()
		if err != nil {
			continue
		}

		tcpLayer := packet.Layer(layers.LayerTypeTCP)
		if tcp, ok := tcpLayer.(*layers.TCP); ok && tcp.DstPort == c.sourcePort && tcp.SrcPort == c.destinationPort {
			if tcp.RST {
				return nil, fmt.Errorf("TCP RST received, connection reset")
			}
			if tcp.FIN {
				log.Println("TCP FIN received, server closed connection")
				return packet, nil
			}

			appLayer := packet.ApplicationLayer()
			if appLayer != nil {
				log.Printf("TCP Response packet received, payload length: %d", len(appLayer.Payload()))
				return packet, nil
			} else {
				log.Println("TCP ACK or control packet received without payload, waiting for data...")
			}
		}
	}
	return nil, fmt.Errorf("no TCP response received after %v", responseTimeout)
}

func (c *tcpClient) sendTCPFIN() error {
	for i := 0; i < tcpFinRetries; i++ {
		eth := &layers.Ethernet{SrcMAC: *c.mac, DstMAC: *c.dstMac, EthernetType: layers.EthernetTypeIPv4}
		ip := &layers.IPv4{
			SrcIP:    c.src,
			DstIP:    c.dst,
			Version:  4,
			TTL:      c.ttl,
			Protocol: layers.IPProtocolTCP,
		}
		tcp := &layers.TCP{
			SrcPort: c.sourcePort,
			DstPort: c.destinationPort,
			FIN:     true,
			ACK:     true,
			Seq:     c.sequenceNumber,
			Ack:     c.acknowledgementNumber,
			Window:  initialWindowSize,
		}
		if err := tcp.SetNetworkLayerForChecksum(ip); err != nil {
			return err
		}

		if err := c.send(eth, ip, tcp); err != nil {
			log.Printf("Failed to send FIN packet (attempt %d): %v", i+1, err)
			time.Sleep(time.Duration(i+1) * time.Second)
			continue
		}
		log.Printf("TCP FIN sent successfully (attempt %d)", i+1)
		return nil
	}
	return fmt.Errorf("failed to send FIN packet after %d retries", tcpFinRetries)
}

func (c *tcpClient) Run() error {
	if err := c.performTCPHandshake(); err != nil {
		return fmt.Errorf("TCP handshake failed: %w", err)
	}

	if err := c.sendHTTPGetRequest(false, 4, "google.com"); err != nil {
		return fmt.Errorf("HTTP GET request failed: %w", err)
	}

	if err := c.sendHTTPGetRequest(false, c.ttl, c.hostname); err != nil {
		return fmt.Errorf("HTTP GET request failed: %w", err)
	}

	responsePacket, err := c.receiveTCPResponse()
	if err != nil {
		return fmt.Errorf("receiving TCP response failed: %w", err)
	}

	if appLayer := responsePacket.ApplicationLayer(); appLayer != nil {
		fmt.Printf("\n--- HTTP Response Payload ---\n%s\n--- End Payload ---\n", string(appLayer.Payload()))
	} else {
		log.Println("No application layer payload in TCP response.")
	}

	if err := c.sendTCPFIN(); err != nil {
		return fmt.Errorf("sending FIN packet failed: %w", err)
	}

	log.Println("Connection closed gracefully.")
	return nil
}

func GetARP(tip, gip, mip net.IP, device string, hardwareAddr net.HardwareAddr) (*net.HardwareAddr, error) {
	log.Println("Getting target MAC for IP:", tip)

	var target net.IP
	if inLocalNet(tip, gip) {
		target = tip
	} else {
		target = gip
	}

	handle, err := pcap.OpenLive(device, packetSnapLen, promiscuousMode, arpTimeout)
	if err != nil {
		return nil, fmt.Errorf("pcap.OpenLive failed: %w", err)
	}
	defer handle.Close()

	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	arpChannel := make(chan net.HardwareAddr)
	errorChannel := make(chan error)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		defer close(arpChannel)
		defer close(errorChannel)
		for {
			select {
			case <-ctx.Done():
				errorChannel <- fmt.Errorf("ARP search cancelled for IP %s", target)
				return
			default:
				packet, err := packetSource.NextPacket()
				if err != nil {
					if errors.Is(err, pcap.NextErrorTimeoutExpired) {
						errorChannel <- fmt.Errorf("ARP timeout for IP %s", target)
						continue
					}
					errorChannel <- fmt.Errorf("packetSource.NextPacket error: %w", err)
					continue
				}

				arpLayer := packet.Layer(layers.LayerTypeARP)
				if arpPacket, ok := arpLayer.(*layers.ARP); ok && arpPacket.Operation == layers.ARPReply {
					if net.IP(arpPacket.SourceProtAddress).Equal(target) {
						arpChannel <- arpPacket.SourceHwAddress
						return
					}
				}
			}
		}
	}()

	arpTries := 0
	for arpTries < maxARPAttempts {
		err = sendARPRequest(handle, hardwareAddr, mip, target)
		if err != nil {
			log.Printf("Error sending ARP request: %v", err)
		}

		select {
		case macAddr := <-arpChannel:
			log.Println("ARP Response received, MAC address:", macAddr)
			return &macAddr, nil
		case err := <-errorChannel:
			log.Printf("ARP attempt %d error: %v", arpTries+1, err)
		default:
			time.Sleep(5 * time.Second)
		}
		arpTries++
	}

	return nil, fmt.Errorf("failed to get MAC address for IP %s after %d attempts", target, maxARPAttempts)
}

func sendARPRequest(handle *pcap.Handle, srcMAC net.HardwareAddr, srcIP, dstIP net.IP) error {
	eth := &layers.Ethernet{
		SrcMAC:       srcMAC,
		DstMAC:       net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
		EthernetType: layers.EthernetTypeARP,
	}

	arp := &layers.ARP{
		AddrType:          layers.LinkTypeEthernet,
		Protocol:          layers.EthernetTypeIPv4,
		HwAddressSize:     uint8(6),
		ProtAddressSize:   uint8(4),
		Operation:         layers.ARPRequest,
		SourceHwAddress:   srcMAC,
		SourceProtAddress: srcIP.To4(),
		DstHwAddress:      net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
		DstProtAddress:    dstIP.To4(),
	}

	buf := gopacket.NewSerializeBuffer()
	opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}

	if err := gopacket.SerializeLayers(buf, opts, eth, arp); err != nil {
		return fmt.Errorf("gopacket.SerializeLayers failed: %w", err)
	}

	packetData := buf.Bytes()
	if err := handle.WritePacketData(packetData); err != nil {
		return fmt.Errorf("handle.WritePacketData failed: %w", err)
	}
	log.Println("ARP request sent for IP:", dstIP)
	return nil
}

func inLocalNet(ip, g net.IP) bool {
	for i, n := range ip.To4()[0:3] {
		if n != g.To4()[i] {
			return false
		}
	}
	return true
}

func getOutboundIP() net.IP {
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		return nil
	}
	defer conn.Close()
	localAddr := conn.LocalAddr().(*net.UDPAddr)
	return localAddr.IP
}

func getInterface() (device string, mac *net.HardwareAddr, gWip *net.IP, src *net.IP, err error) {
	gWipVal, err := gateway.DiscoverGateway()
	if err != nil {
		return "", nil, nil, nil, err
	}
	device, _ = selectDevice()
	myIP := getOutboundIP()
	macVal := getMAC(myIP)
	return device, &macVal, &gWipVal, &myIP, nil
}

func getMAC(ip net.IP) net.HardwareAddr {
	interfaces, err := net.Interfaces()
	if err != nil {
		log.Fatal(err)
	}
	for _, inf := range interfaces {
		if addr, err := inf.Addrs(); err == nil {
			for _, addr := range addr {
				if strings.Split(addr.String(), "/")[0] == ip.String() {
					return inf.HardwareAddr
				}
			}
		}
	}
	return net.HardwareAddr{0, 0, 0, 0, 0, 0}
}

func selectDevice() (device string, ip net.IP) {
	localIP := getOutboundIP()
	devices, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatal(err)
	}
	for _, dev := range devices {
		for _, address := range dev.Addresses {
			if localIP != nil {
				if address.IP.String() == localIP.String() {
					log.Println("Selected device: ", dev.Description)
					return dev.Name, localIP
				}
			} else if address.IP.String() != "127.0.0.1" && !strings.Contains(dev.Description, "Loopback") {
				return dev.Name, address.IP
			}
		}
	}
	return "", nil
}

func main() {
	var host string
	var destIPString string
	var ttlValue int

	flag.StringVar(&host, "host", "", "Destination host (hostname or IP address, required)")
	flag.StringVar(&destIPString, "ip", "", "Destination IP address (optional, will resolve host if not provided)")
	flag.IntVar(&ttlValue, "ttl", defaultTTL, "TTL value for IP packets (optional)")
	flag.Parse()

	if host == "" {
		fmt.Println("Usage: go run main.go -host <hostname_or_ip> [-ip <ip_address>] [-ttl <ttl_value>]")
		os.Exit(1)
	}

	var destIP net.IP
	var err error

	if destIPString != "" {
		destIP = net.ParseIP(destIPString).To4()
		if destIP == nil {
			log.Fatalf("Invalid destination IP address: %q", destIPString)
			return
		}
	} else {
		parsedHostIP := net.ParseIP(host).To4()
		if parsedHostIP != nil {
			destIP = parsedHostIP
		} else {
			ips, err := net.LookupIP(host)
			if err != nil {
				log.Fatalf("Could not resolve hostname %q: %v", host, err)
				return
			}
			for _, ip := range ips {
				if ipv4 := ip.To4(); ipv4 != nil {
					destIP = ipv4
					break
				}
			}
			if destIP == nil {
				log.Fatalf("No IPv4 address found for hostname %q", host)
				return
			}
			log.Printf("Resolved hostname %q to IP %v", host, destIP)
		}
	}

	if destIP == nil {
		log.Fatalf("Destination IP address is not determined for host %q", host)
		return
	}

	if ttlValue < 1 || ttlValue > 255 {
		log.Fatalf("Invalid TTL value: %d. TTL must be between 1 and 255", ttlValue)
		return
	}
	ttl := uint8(ttlValue)

	client, err := newTcpClient(host, destIP, ttl)
	if err != nil {
		log.Printf("Failed to create TCP client for %v (%v): %v", host, destIP, err)
		return
	}
	defer client.close()
	log.Printf("Starting TCP client for %v (%v), TTL=%d", host, destIP, ttl)

	if err := client.Run(); err != nil {
		log.Printf("TCP client error communicating with %v (%v): %v", host, destIP, err)
	}
}
Editor is loading...
Leave a Comment