From 037d5cea223077e433d646ab56b86316eb5265f8 Mon Sep 17 00:00:00 2001 From: evilsocket Date: Wed, 20 Feb 2019 17:54:44 +0100 Subject: [PATCH] finished the duckyscript parser --- .../hid_logitech.go => hid/build_logitech.go} | 2 +- .../hid_builders.go => hid/builders.go} | 2 +- .../hid_command.go => hid/command.go} | 2 +- modules/hid/duckyparser.go | 187 ++++++++++++++++++ modules/{hid_recon => hid}/hid_inject.go | 27 ++- modules/{hid_recon => hid}/hid_recon.go | 59 ++++-- modules/{hid_recon => hid}/hid_show.go | 2 +- modules/{hid_recon => hid}/hid_show_sort.go | 2 +- modules/{hid_recon => hid}/hid_sniff.go | 2 +- .../hid_keymaps.go => hid/keymaps.go} | 2 +- modules/modules.go | 4 +- 11 files changed, 249 insertions(+), 42 deletions(-) rename modules/{hid_recon/hid_logitech.go => hid/build_logitech.go} (98%) rename modules/{hid_recon/hid_builders.go => hid/builders.go} (92%) rename modules/{hid_recon/hid_command.go => hid/command.go} (97%) create mode 100644 modules/hid/duckyparser.go rename modules/{hid_recon => hid}/hid_inject.go (84%) rename modules/{hid_recon => hid}/hid_recon.go (70%) rename modules/{hid_recon => hid}/hid_show.go (99%) rename modules/{hid_recon => hid}/hid_show_sort.go (96%) rename modules/{hid_recon => hid}/hid_sniff.go (99%) rename modules/{hid_recon/hid_keymaps.go => hid/keymaps.go} (99%) diff --git a/modules/hid_recon/hid_logitech.go b/modules/hid/build_logitech.go similarity index 98% rename from modules/hid_recon/hid_logitech.go rename to modules/hid/build_logitech.go index f7ac5b49..c66d7adf 100644 --- a/modules/hid_recon/hid_logitech.go +++ b/modules/hid/build_logitech.go @@ -1,4 +1,4 @@ -package hid_recon +package hid const ( frameDelay = 12 diff --git a/modules/hid_recon/hid_builders.go b/modules/hid/builders.go similarity index 92% rename from modules/hid_recon/hid_builders.go rename to modules/hid/builders.go index 527ed6eb..750588d0 100644 --- a/modules/hid_recon/hid_builders.go +++ b/modules/hid/builders.go @@ -1,4 +1,4 @@ -package hid_recon +package hid import ( "github.com/bettercap/bettercap/network" diff --git a/modules/hid_recon/hid_command.go b/modules/hid/command.go similarity index 97% rename from modules/hid_recon/hid_command.go rename to modules/hid/command.go index d0c99c59..17799ea6 100644 --- a/modules/hid_recon/hid_command.go +++ b/modules/hid/command.go @@ -1,4 +1,4 @@ -package hid_recon +package hid import ( "time" diff --git a/modules/hid/duckyparser.go b/modules/hid/duckyparser.go new file mode 100644 index 00000000..c5536965 --- /dev/null +++ b/modules/hid/duckyparser.go @@ -0,0 +1,187 @@ +package hid + +import ( + "fmt" + "strconv" + "strings" + + "github.com/evilsocket/islazy/fs" +) + +type DuckyParser struct { + mod *HIDRecon +} + +func (p DuckyParser) parseLiteral(what string, kmap KeyMap) (*Command, error) { + // get reference command from the layout + ref, found := kmap[what] + if found == false { + return nil, fmt.Errorf("can't find '%s' in current keymap", what) + } + return &Command{ + HID: ref.HID, + Mode: ref.Mode, + }, nil +} + +func (p DuckyParser) parseModifier(line string, kmap KeyMap, modMask byte) (*Command, error) { + // get optional key after the modifier + ch := "" + if idx := strings.IndexRune(line, ' '); idx != -1 { + ch = line[idx+1:] + } + cmd, err := p.parseLiteral(ch, kmap) + if err != nil { + return nil, err + } + // apply modifier mask + cmd.Mode |= modMask + return cmd, nil +} + +func (p DuckyParser) parseNumber(from string) (int, error) { + idx := strings.IndexRune(from, ' ') + if idx == -1 { + return 0, fmt.Errorf("can't parse number from '%s'", from) + } + + num, err := strconv.Atoi(from[idx+1:]) + if err != nil { + return 0, fmt.Errorf("can't parse number from '%s': %v", from, err) + } + + return num, nil +} + +func (p DuckyParser) parseString(from string) (string, error) { + idx := strings.IndexRune(from, ' ') + if idx == -1 { + return "", fmt.Errorf("can't parse string from '%s'", from) + } + return from[idx+1:], nil +} + +func (p DuckyParser) lineIs(line string, tokens ...string) bool { + for _, tok := range tokens { + if strings.HasPrefix(line, tok) { + return true + } + } + return false +} + +func (p DuckyParser) Parse(kmap KeyMap, path string) (cmds []*Command, err error) { + lines := []string{} + source := []string{} + reader := (chan string)(nil) + + if reader, err = fs.LineReader(path); err != nil { + return + } else { + for line := range reader { + lines = append(lines, line) + } + } + + // preprocessing + for lineno, line := range lines { + if p.lineIs(line, "REPEAT") { + if lineno == 0 { + err = fmt.Errorf("error on line %d: REPEAT instruction at the beginning of the script", lineno+1) + return + } + times := 1 + times, err = p.parseNumber(line) + if err != nil { + return + } + + for i := 0; i < times; i++ { + source = append(source, lines[lineno-1]) + } + } else { + source = append(source, line) + } + } + + cmds = make([]*Command, 0) + for lineno, line := range source { + cmd := &Command{} + if p.lineIs(line, "CTRL", "CONTROL") { + if cmd, err = p.parseModifier(line, kmap, 1); err != nil { + return + } + } else if p.lineIs(line, "SHIFT") { + if cmd, err = p.parseModifier(line, kmap, 2); err != nil { + return + } + } else if p.lineIs(line, "ALT") { + if cmd, err = p.parseModifier(line, kmap, 4); err != nil { + return + } + } else if p.lineIs(line, "GUI", "WINDOWS", "COMMAND") { + if cmd, err = p.parseModifier(line, kmap, 8); err != nil { + return + } + } else if p.lineIs(line, "CTRL-ALT", "CONTROL-ALT") { + if cmd, err = p.parseModifier(line, kmap, 4|1); err != nil { + return + } + } else if p.lineIs(line, "CTRL-SHIFT", "CONTROL-SHIFT") { + if cmd, err = p.parseModifier(line, kmap, 1|2); err != nil { + return + } + } else if p.lineIs(line, "ESC", "ESCAPE", "APP") { + if cmd, err = p.parseLiteral("ESCAPE", kmap); err != nil { + return + } + } else if p.lineIs(line, "ENTER") { + if cmd, err = p.parseLiteral("ENTER", kmap); err != nil { + return + } + } else if p.lineIs(line, "UP", "UPARROW") { + if cmd, err = p.parseLiteral("UP", kmap); err != nil { + return + } + } else if p.lineIs(line, "DOWN", "DOWNARROW") { + if cmd, err = p.parseLiteral("DOWN", kmap); err != nil { + return + } + } else if p.lineIs(line, "LEFT", "LEFTARROW") { + if cmd, err = p.parseLiteral("LEFT", kmap); err != nil { + return + } + } else if p.lineIs(line, "RIGHT", "RIGHTARROW") { + if cmd, err = p.parseLiteral("RIGHT", kmap); err != nil { + return + } + } else if p.lineIs(line, "DELAY", "SLEEP") { + secs := 0 + if secs, err = p.parseNumber(line); err != nil { + return + } + cmd = &Command{Sleep: secs} + } else if p.lineIs(line, "STRING", "STR") { + str := "" + if str, err = p.parseString(line); err != nil { + return + } + + for _, c := range str { + if cmd, err = p.parseLiteral(string(c), kmap); err != nil { + return + } + cmds = append(cmds, cmd) + } + + continue + } else { + err = fmt.Errorf("can't parse line %d ('%s') of %s", lineno+1, line, path) + return + } + + cmds = append(cmds, cmd) + } + + return +} diff --git a/modules/hid_recon/hid_inject.go b/modules/hid/hid_inject.go similarity index 84% rename from modules/hid_recon/hid_inject.go rename to modules/hid/hid_inject.go index 58641885..62ec5c63 100644 --- a/modules/hid_recon/hid_inject.go +++ b/modules/hid/hid_inject.go @@ -1,4 +1,4 @@ -package hid_recon +package hid import ( "fmt" @@ -43,11 +43,13 @@ func errNoKeyMap(layout string) error { } func (mod *HIDRecon) prepInjection() (error, *network.HIDDevice, []*Command) { + // we can only inject onto visible connections dev, found := mod.Session.HID.Get(mod.sniffAddr) if found == false { return errNoDevice(mod.sniffAddr), nil, nil } + // get the device specific protocol handler builder, found := FrameBuilders[dev.Type] if found == false { if dev.Type == network.HIDTypeUnknown { @@ -56,24 +58,21 @@ func (mod *HIDRecon) prepInjection() (error, *network.HIDDevice, []*Command) { return errNotSupported(dev), nil, nil } - keyLayout := KeyMapFor(mod.keyLayout) - if keyLayout == nil { + // get the keymap from the selected layout + keyMap := KeyMapFor(mod.keyLayout) + if keyMap == nil { return errNoKeyMap(mod.keyLayout), nil, nil } - str := "hello world from bettercap ^_^" - cmds := make([]*Command, 0) - for _, c := range str { - if m, found := keyLayout[string(c)]; found { - cmds = append(cmds, &Command{ - HID: m.HID, - Mode: m.Mode, - }) - } else { - return fmt.Errorf("could not find HID command for '%c'", c), nil, nil - } + // parse the script into a list of Command objects + cmds, err := mod.parser.Parse(keyMap, mod.scriptPath) + if err != nil { + return err, nil, nil } + mod.Info("%s loaded ...", mod.scriptPath) + + // build the protocol specific frames to send if err := builder.BuildFrames(cmds); err != nil { return err, nil, nil } diff --git a/modules/hid_recon/hid_recon.go b/modules/hid/hid_recon.go similarity index 70% rename from modules/hid_recon/hid_recon.go rename to modules/hid/hid_recon.go index c7723a86..8cc04f92 100644 --- a/modules/hid_recon/hid_recon.go +++ b/modules/hid/hid_recon.go @@ -1,6 +1,7 @@ -package hid_recon +package hid import ( + "fmt" "sync" "time" @@ -29,12 +30,14 @@ type HIDRecon struct { inPromMode bool inInjectMode bool keyLayout string + scriptPath string + parser DuckyParser selector *utils.ViewSelector } func NewHIDRecon(s *session.Session) *HIDRecon { mod := &HIDRecon{ - SessionModule: session.NewSessionModule("hid.recon", s), + SessionModule: session.NewSessionModule("hid", s), waitGroup: &sync.WaitGroup{}, sniffLock: &sync.Mutex{}, hopPeriod: 100 * time.Millisecond, @@ -51,70 +54,88 @@ func NewHIDRecon(s *session.Session) *HIDRecon { inInjectMode: false, pingPayload: []byte{0x0f, 0x0f, 0x0f, 0x0f}, keyLayout: "US", + scriptPath: "", } mod.AddHandler(session.NewModuleHandler("hid.recon on", "", - "Start HID recon.", + "Start scanning for HID devices on the 2.4Ghz spectrum.", func(args []string) error { return mod.Start() })) mod.AddHandler(session.NewModuleHandler("hid.recon off", "", - "Stop HID recon.", + "Stop scanning for HID devices on the 2.4Ghz spectrum.", func(args []string) error { return mod.Stop() })) - sniff := session.NewModuleHandler("hid.sniff ADDRESS", `(?i)^hid\.sniff ([a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}|clear)$`, - "TODO TODO", - func(args []string) error { - return mod.setSniffMode(args[0]) - }) + mod.AddParam(session.NewBoolParameter("hid.lna", + "true", + "If true, enable the LNA power amplifier for CrazyRadio devices.")) - sniff.Complete("hid.sniff", s.HIDCompleter) + /* + pretty useless until i don't implement the microsoft specific keylogger + sniff := session.NewModuleHandler("hid.sniff ADDRESS", `(?i)^hid\.sniff ([a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}|clear)$`, + "TODO TODO", + func(args []string) error { + return mod.setSniffMode(args[0]) + }) - mod.AddHandler(sniff) + sniff.Complete("hid.sniff", s.HIDCompleter) + + mod.AddHandler(sniff) + */ mod.AddHandler(session.NewModuleHandler("hid.show", "", - "TODO TODO", + "Show a list of detected HID devices on the 2.4Ghz spectrum.", func(args []string) error { 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", + inject := session.NewModuleHandler("hid.inject ADDRESS LAYOUT FILENAME", `(?i)^hid\.inject ([a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2})\s+(.+)\s+(.+)$`, + "Parse the duckyscript FILENAME and inject it as HID frames spoofing the device ADDRESS, using the LAYOUT keyboard mapping.", func(args []string) error { - return mod.setInjectionMode(args[0]) + if err := mod.setInjectionMode(args[0]); err != nil { + return err + } + mod.keyLayout = args[1] + mod.scriptPath = args[2] + return nil }) inject.Complete("hid.inject", s.HIDCompleter) mod.AddHandler(inject) + mod.parser = DuckyParser{mod} mod.selector = utils.ViewSelectorFor(&mod.SessionModule, "hid.show", []string{"mac", "seen"}, "mac desc") return mod } func (mod HIDRecon) Name() string { - return "hid.recon" + return "hid" } func (mod HIDRecon) Description() string { - return "TODO TODO" + return "A scanner and frames injection module for HID devices on the 2.4Ghz spectrum, using Nordic Semiconductor nRF24LU1+ based USB dongles and Bastille Research RFStorm firmware." } func (mod HIDRecon) Author() string { - return "Simone Margaritelli " + return "Simone Margaritelli (this module and the nrf24 client library), Bastille Research (the rfstorm firmware and original research), phikshun and infamy for JackIt." } func (mod *HIDRecon) Configure() error { var err error - if mod.dongle, err = nrf24.Open(); err != nil { + if err, mod.useLNA = mod.BoolParam("hid.lna"); err != nil { return err } + if mod.dongle, err = nrf24.Open(); err != nil { + return fmt.Errorf("make sure that a nRF24LU1+ based USB dongle is connected and running the rfstorm firmware: %s", err) + } + mod.Debug("using device %s", mod.dongle.String()) if mod.useLNA { diff --git a/modules/hid_recon/hid_show.go b/modules/hid/hid_show.go similarity index 99% rename from modules/hid_recon/hid_show.go rename to modules/hid/hid_show.go index cb4cbe6e..3418cbb4 100644 --- a/modules/hid_recon/hid_show.go +++ b/modules/hid/hid_show.go @@ -1,4 +1,4 @@ -package hid_recon +package hid import ( "fmt" diff --git a/modules/hid_recon/hid_show_sort.go b/modules/hid/hid_show_sort.go similarity index 96% rename from modules/hid_recon/hid_show_sort.go rename to modules/hid/hid_show_sort.go index 57bb2cc3..a70690e6 100644 --- a/modules/hid_recon/hid_show_sort.go +++ b/modules/hid/hid_show_sort.go @@ -1,4 +1,4 @@ -package hid_recon +package hid import ( "github.com/bettercap/bettercap/network" diff --git a/modules/hid_recon/hid_sniff.go b/modules/hid/hid_sniff.go similarity index 99% rename from modules/hid_recon/hid_sniff.go rename to modules/hid/hid_sniff.go index 9a86a37d..532568fe 100644 --- a/modules/hid_recon/hid_sniff.go +++ b/modules/hid/hid_sniff.go @@ -1,4 +1,4 @@ -package hid_recon +package hid import ( "time" diff --git a/modules/hid_recon/hid_keymaps.go b/modules/hid/keymaps.go similarity index 99% rename from modules/hid_recon/hid_keymaps.go rename to modules/hid/keymaps.go index 0c296d0b..df2cde12 100644 --- a/modules/hid_recon/hid_keymaps.go +++ b/modules/hid/keymaps.go @@ -1,4 +1,4 @@ -package hid_recon +package hid import ( "sort" diff --git a/modules/modules.go b/modules/modules.go index f1ba1365..15e15c4b 100644 --- a/modules/modules.go +++ b/modules/modules.go @@ -10,7 +10,7 @@ import ( "github.com/bettercap/bettercap/modules/dns_spoof" "github.com/bettercap/bettercap/modules/events_stream" "github.com/bettercap/bettercap/modules/gps" - "github.com/bettercap/bettercap/modules/hid_recon" + "github.com/bettercap/bettercap/modules/hid" "github.com/bettercap/bettercap/modules/http_proxy" "github.com/bettercap/bettercap/modules/http_server" "github.com/bettercap/bettercap/modules/https_proxy" @@ -57,5 +57,5 @@ func LoadModules(sess *session.Session) { sess.Register(update.NewUpdateModule(sess)) sess.Register(wifi.NewWiFiModule(sess)) sess.Register(wol.NewWOL(sess)) - sess.Register(hid_recon.NewHIDRecon(sess)) + sess.Register(hid.NewHIDRecon(sess)) }