wrong checksum, low ttl
unknown
golang
10 months ago
19 kB
9
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, ð, &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