I am attempting to work on a project involving ICMP packets. I want to create a straightforward Golang program that would help ICMP Tunneling, a method of passing data over ICMP packets to get around security measures like firewalls. The issue is that I've never been able to make Dial, ListenIP, or ListenPacket's IP/Protocol parsing work. Even if it attempted to run the same code as other people and worked flawlessly for them.
Information:
- Go version: go version go1.17.5 windows/amd64
- OS: Windows 10, lastest version
- IDE: LiteIDE
- Destination IP: 192.168.1.5 (my PC's local IP)
- Destination Port: any for now, simply trying out RAW packets.
Here's what I've tried:
How to use Golang to compose raw TCP packet (using gopacket) and send it via raw socket - even if I followed the solution's instructions There was usually an issue processing IP4 and TCP in the network. ListenPacket and ListenIP delivered an error message such as "unexpected type" or "invalid host." I tired using the two following formats: ip:port and ip.
Here is my version of the GitHub code - because many dependencies were missing, I had to modify the code to make it function. Because of this tutorial, I know this code works.
Here is my version of the GitHub code
/*
Copyright 2013-2014 Graham King
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
For full license details see <http://www.gnu.org/licenses/>.
*/
package main
import (
"bytes"
"encoding/binary"
"log"
"math/rand"
"net"
)
const (
FIN = 1 // 00 0001
SYN = 2 // 00 0010
RST = 4 // 00 0100
PSH = 8 // 00 1000
ACK = 16 // 01 0000
URG = 32 // 10 0000
)
type TCPHeader struct {
Source uint16
Destination uint16
SeqNum uint32
AckNum uint32
DataOffset uint8 // 4 bits
Reserved uint8 // 3 bits
ECN uint8 // 3 bits
Ctrl uint8 // 6 bits
Window uint16
Checksum uint16 // Kernel will set this if it's 0
Urgent uint16
Options []TCPOption
}
type TCPOption struct {
Kind uint8
Length uint8
Data []byte
}
// Parse packet into TCPHeader structure
func NewTCPHeader(data []byte) *TCPHeader {
var tcp TCPHeader
r := bytes.NewReader(data)
binary.Read(r, binary.BigEndian, &tcp.Source)
binary.Read(r, binary.BigEndian, &tcp.Destination)
binary.Read(r, binary.BigEndian, &tcp.SeqNum)
binary.Read(r, binary.BigEndian, &tcp.AckNum)
var mix uint16
binary.Read(r, binary.BigEndian, &mix)
tcp.DataOffset = byte(mix >> 12) // top 4 bits
tcp.Reserved = byte(mix >> 9 & 7) // 3 bits
tcp.ECN = byte(mix >> 6 & 7) // 3 bits
tcp.Ctrl = byte(mix & 0x3f) // bottom 6 bits
binary.Read(r, binary.BigEndian, &tcp.Window)
binary.Read(r, binary.BigEndian, &tcp.Checksum)
binary.Read(r, binary.BigEndian, &tcp.Urgent)
return &tcp
}
func (tcp *TCPHeader) HasFlag(flagBit byte) bool {
return tcp.Ctrl&flagBit != 0
}
func (tcp *TCPHeader) Marshal() []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, tcp.Source)
binary.Write(buf, binary.BigEndian, tcp.Destination)
binary.Write(buf, binary.BigEndian, tcp.SeqNum)
binary.Write(buf, binary.BigEndian, tcp.AckNum)
var mix uint16
mix = uint16(tcp.DataOffset)<<12 | // top 4 bits
uint16(tcp.Reserved)<<9 | // 3 bits
uint16(tcp.ECN)<<6 | // 3 bits
uint16(tcp.Ctrl) // bottom 6 bits
binary.Write(buf, binary.BigEndian, mix)
binary.Write(buf, binary.BigEndian, tcp.Window)
binary.Write(buf, binary.BigEndian, tcp.Checksum)
binary.Write(buf, binary.BigEndian, tcp.Urgent)
for _, option := range tcp.Options {
binary.Write(buf, binary.BigEndian, option.Kind)
if option.Length > 1 {
binary.Write(buf, binary.BigEndian, option.Length)
binary.Write(buf, binary.BigEndian, option.Data)
}
}
out := buf.Bytes()
// Pad to min tcp header size, which is 20 bytes (5 32-bit words)
pad := 20 - len(out)
for i := 0; i < pad; i++ {
out = append(out, 0)
}
return out
}
// TCP Checksum
func Csum(data []byte, srcip, dstip [4]byte) uint16 {
pseudoHeader := []byte{
srcip[0], srcip[1], srcip[2], srcip[3],
dstip[0], dstip[1], dstip[2], dstip[3],
0, // zero
6, // protocol number (6 == TCP)
0, byte(len(data)), // TCP length (16 bits), not inc pseudo header
}
sumThis := make([]byte, 0, len(pseudoHeader)+len(data))
sumThis = append(sumThis, pseudoHeader...)
sumThis = append(sumThis, data...)
//fmt.Printf("% x\n", sumThis)
lenSumThis := len(sumThis)
var nextWord uint16
var sum uint32
for i := 0; i+1 < lenSumThis; i += 2 {
nextWord = uint16(sumThis[i])<<8 | uint16(sumThis[i+1])
sum += uint32(nextWord)
}
if lenSumThis%2 != 0 {
//fmt.Println("Odd byte")
sum += uint32(sumThis[len(sumThis)-1])
}
// Add back any carry, and any carry from adding the carry
sum = (sum >> 16) + (sum & 0xffff)
sum = sum + (sum >> 16)
// Bitwise complement
return uint16(^sum)
}
func main() {
packet := TCPHeader{
Source: 0xaa47, // Random ephemeral port
Destination: 80,
SeqNum: rand.Uint32(),
AckNum: 0,
DataOffset: 5, // 4 bits
Reserved: 0, // 3 bits
ECN: 0, // 3 bits
Ctrl: 2, // 6 bits (000010, SYN bit set)
Window: 0xaaaa, // size of your receive window
Checksum: 0, // Kernel will set this if it's 0
Urgent: 0,
Options: []TCPOption{},
}
data := packet.Marshal()
packet.Checksum = Csum(data, [4]byte{192, 168, 1, 5}, [4]byte{192, 168, 1, 5})
data = packet.Marshal()
conn, err := net.Dial("ip4:tcp", "192.168.1.5:555")
if err != nil {
log.Fatalf("Dial: %s\n", err)
}
conn.Write(data)
}
I'm using LiteIDE, and when I build and execute the Go produced exe, I get the following error:
2022/08/25 13:17:10 Dial: dial ip4:tcp 192.168.1.5: connect: An invalid argument was supplied.
This error is caused by line 178. I tried changing it to "192.168.1.5:555" (or any port), but now I get the following error:
2022/08/25 13:23:06 Dial: dial ip4:tcp: lookup 192.168.1.5:555: no such host
I'm also listening for incoming packets with ncat from nmap.org and Wireshark. Please keep in mind that I'm still studying Golang and in-depth networking. I understand networking at a basic level, but I'm attempting to become more comfortable with the more in-depth aspects of it (aka creating, manipulating and injecting RAW packets). I'm deliberately attempting something difficult since that's how I learn.