Showing nodes accounting for 41542, 12.21% of 340337 total
Dropped 146 nodes (cum <= 1701)
Dropped 4 edges (freq <= 340)
Showing top 80 nodes out of 86
+See https://git.io/JfYMW for how to read the graph
+
+
+
+
+
+
+
+
Save options as
+
+
+
+
+
+
+
Delete config
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/profiling/protomolecule.profile b/profiling/protomolecule.profile
new file mode 100644
index 0000000..839527b
Binary files /dev/null and b/profiling/protomolecule.profile differ
diff --git a/src/arboghast/arboghast.go b/src/arboghast/arboghast.go
new file mode 100644
index 0000000..32e4b92
--- /dev/null
+++ b/src/arboghast/arboghast.go
@@ -0,0 +1,54 @@
+// arboghast is a package focused on fuzzing BLE devices
+package arboghast
+
+import (
+ "sync"
+
+ bluetooth "git.tcp.direct/kayos/prototooth"
+
+ projVars "protomolecule/src/config"
+)
+
+type Circus struct {
+ Trick map[int]*Fuzzy
+ uuid_pool []bluetooth.UUID
+ mu *sync.Mutex
+}
+
+type Fuzzy struct {
+ config *bluetooth.AdvertisementOptions
+ ad *bluetooth.Advertisement
+ mu *sync.Mutex
+}
+
+var circus *Circus
+
+func Awaken() {
+ circus = &Circus{
+ Trick: make(map[int]*Fuzzy),
+ uuid_pool: []bluetooth.UUID{},
+ mu: &sync.Mutex{},
+ }
+}
+
+func NewEntropicFuzzer() *Fuzzy {
+ options := &bluetooth.AdvertisementOptions{
+ LocalName: RandomAlpha(16),
+
+ // Interval:
+ }
+ mutex := &sync.Mutex{}
+
+ ad := projVars.GlobalConfig.GetScanAdapter().DefaultAdvertisement()
+ ad.Configure(*options)
+
+ return &Fuzzy{
+ config: options,
+ ad: ad,
+ mu: mutex,
+ }
+}
+
+func (f *Fuzzy) StartAd() {
+ f.ad.Start()
+}
diff --git a/src/arboghast/entropy.go b/src/arboghast/entropy.go
new file mode 100644
index 0000000..f61b6c9
--- /dev/null
+++ b/src/arboghast/entropy.go
@@ -0,0 +1,34 @@
+package arboghast
+
+import (
+ "math/rand"
+ "time"
+ "unsafe"
+)
+
+var src = rand.NewSource(time.Now().UnixNano())
+
+const (
+ letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ letterIdxBits = 6 // 6 bits to represent a letter index
+ letterIdxMask = 1<= 0; {
+ if remain == 0 {
+ cache, remain = src.Int63(), letterIdxMax
+ }
+ if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
+ b[i] = letterBytes[idx]
+ i--
+ }
+ cache >>= letterIdxBits
+ remain--
+ }
+
+ return *(*string)(unsafe.Pointer(&b))
+}
diff --git a/src/blueStuff/blueStuff.go b/src/blueStuff/blueStuff.go
index a3914ec..3f52e90 100644
--- a/src/blueStuff/blueStuff.go
+++ b/src/blueStuff/blueStuff.go
@@ -2,19 +2,15 @@ package blueStuff
import "git.tcp.direct/kayos/prototooth"
-//controls connections to found devices to get service characteristics
-
-//TODO: disable the scan adapter, enable the attack adapter (this step pmight not be needed)
+// controls connections to found devices to get service characteristics
+// TODO: disable the scan adapter, enable the attack adapter (this step pmight not be needed)
// use the result.LocalName passed from scanStuff to start connection
// once connected, for list of service characteristics and check for read / write for each
// loog all of this to eros
func pathways(target bluetooth.ScanResult) {
-
println("Discovery")
- //device, err := projVars.ScanAdapter.Connect(bluetooth.ScanResult.Address, bluetooth.ConnectionParams{})
-
- //srvcs, err := projVars.ScanAdapter.
-
+ // device, err := projVars.GlobalConfig.GetScanAdapter().Connect(bluetooth.ScanResult.Address, bluetooth.ConnectionParams{})
+ // srvcs, err := projVars.GlobalConfig.GetScanAdapter().
}
diff --git a/src/config/config.go b/src/config/config.go
new file mode 100644
index 0000000..c10f1ad
--- /dev/null
+++ b/src/config/config.go
@@ -0,0 +1,99 @@
+package common
+
+import (
+ "flag"
+
+ bluetooth "git.tcp.direct/kayos/prototooth"
+)
+
+type Mode uint32
+
+const (
+ ModeNone Mode = iota
+ ModeScan
+ ModeTrack
+ ModeAttack
+)
+
+func (m Mode) String() string {
+ return [...]string{"None", "Track", "Attack"}[m]
+}
+
+// Tooth is a bluetooth adapter.
+type Tooth struct {
+ ID int
+ Usecase Mode
+ *bluetooth.Adapter
+}
+
+type Config struct {
+ // ManuFile is a place holder for a file with list of all known Manufacturers
+ // this is actually stored by eros and likely deprecated.
+ ManuFile string
+ Teeth []*Tooth
+
+ CooleanFlags struct {
+ Debug *bool
+ Attack *bool
+ Tracking *bool
+ }
+ StringFlags struct {
+ MACVendorFile *string
+ MACTarget *string
+ }
+}
+
+func (c *Config) GetScanAdapter() *bluetooth.Adapter {
+ if len(c.Teeth) == 1 {
+ return c.Teeth[0].Adapter
+ }
+ var choice *bluetooth.Adapter
+ for _, v := range c.Teeth {
+ if v.Usecase == ModeScan || v.Usecase == ModeTrack {
+ choice = v.Adapter
+ break
+ }
+ }
+ return choice
+}
+
+var DefaultConfig = Config{
+ ManuFile: DefManuFile,
+ Teeth: []*Tooth{
+ &Tooth{
+ ID: 0,
+ Usecase: ModeNone,
+ Adapter: bluetooth.DefaultAdapter,
+ },
+ },
+ CooleanFlags: struct {
+ Debug *bool
+ Attack *bool
+ Tracking *bool
+ }{
+ Debug: flag.Bool("d", false, "global debug logging and pretty printing"),
+ Attack: flag.Bool("a", false, "attack mode"),
+ Tracking: flag.Bool("t", false, "device tracking mode"),
+ },
+
+ StringFlags: struct {
+ MACVendorFile *string
+ MACTarget *string
+ }{
+ MACVendorFile: flag.String("m", DefManuFile, "json file with manufacturer identifying data to preload into eros"),
+ MACTarget: flag.String("w", "00:00:00:00:00:00", "MAC address of target"),
+ },
+}
+
+const DefManuFile = "./ManufUUID.json"
+
+// Service UUIDs for testing
+var (
+ SonosScanTest = bluetooth.New16BitUUID(0xfe07)
+ AppleScanTest = bluetooth.New16BitUUID(0x004c)
+ GlobalConfig *Config
+)
+
+func init() {
+ GlobalConfig = &DefaultConfig
+}
diff --git a/src/cortazar/cortazar.go b/src/cortazar/cortazar.go
new file mode 100644
index 0000000..07a3aaf
--- /dev/null
+++ b/src/cortazar/cortazar.go
@@ -0,0 +1,34 @@
+package cortazar
+
+//Intended to be used as the target analysis package
+
+//for example
+
+/*
+
+1. attempt bonding (LTK exchange) with target, while in aggressive attack mode
+2. store current LTK associated with target to device in eros
+3. disconnect and forget target
+4. recconect to target, pair, bond, store LTK, disconnect, forget -- repeatTK
+5. compare LTKs for entropy
+
+** could also be used for link key which is the pin established during pairing
+*/
+
+//---------
+
+/*
+ device metrics
+ - location and device type/manufacturer/etc...
+ - when and where a device is seen over time
+
+
+
+
+*/
+
+//-----------
+
+/*
+
+ */
diff --git a/src/deimos/main.go b/src/deimos/main.go
new file mode 100644
index 0000000..a070c01
--- /dev/null
+++ b/src/deimos/main.go
@@ -0,0 +1,77 @@
+package deimos
+
+import (
+ "errors"
+ "fmt"
+ "github.com/google/gopacket/dumpcommand"
+ "github.com/google/gopacket/pcap"
+ "github.com/rs/zerolog/log"
+ "strconv"
+ "strings"
+)
+
+type DevMap map[int]pcap.Interface
+
+var Devices DevMap
+var Interface *pcap.Handle
+
+func init() {
+ Devices = make(map[int]pcap.Interface)
+}
+
+func getInterfaces() []pcap.Interface {
+ devices, err := pcap.FindAllDevs()
+ if err != nil {
+ log.Error().Err(err).Msg("failed to get network interfaces")
+ return nil
+ }
+ return devices
+}
+
+func getBlueDevices() []pcap.Interface {
+ var blues []pcap.Interface
+ devices := getInterfaces()
+ for n, device := range devices {
+ if strings.Contains(strings.ToLower(device.Name), "bluetooth") {
+ Devices[n] = device
+ blues = append(blues, device)
+ }
+ if strings.Contains(strings.ToLower(device.Name), "hci") {
+ Devices[n] = device
+ blues = append(blues, device)
+ }
+ }
+ return blues
+}
+
+func PrintBlueDevices() {
+ getBlueDevices()
+ for n := range Devices {
+ devid := strconv.Itoa(n)
+ fmt.Println("[" + devid + "] " + Devices[n].Name)
+ }
+}
+
+func Sniff(dev int) error {
+ var (
+ err error
+ d pcap.Interface
+ preInterface *pcap.InactiveHandle
+ ok bool
+ )
+ if d, ok = Devices[dev]; !ok {
+ return errors.New("adapter ID does not exist")
+ }
+ if preInterface, err = pcap.NewInactiveHandle(d.Name); err != nil {
+ return err
+ }
+ defer preInterface.CleanUp()
+ if err = preInterface.SetPromisc(true); err != nil {
+ return err
+ }
+ if Interface, err = preInterface.Activate(); err != nil {
+ return err
+ }
+ go dumpcommand.Run(Interface)
+ return nil
+}
diff --git a/src/dust/dust.go b/src/dust/dust.go
index 1d3b8e9..4ae7ef9 100644
--- a/src/dust/dust.go
+++ b/src/dust/dust.go
@@ -9,7 +9,12 @@ import (
func Must(action string, err error) {
if err != nil {
- log.Fatal().Err(err).Str("action", action).Msg("FATAL_ERROR")
+ switch err.Error() {
+ case "AdapterExists: Launch helper exited with unknown return code 1":
+ log.Error().Msg("Is your bluetooth adapter enabled? Linux: is bluetoothd started?")
+ default:
+ log.Fatal().Err(err).Str("action", action).Msg("FATAL_ERROR")
+ }
}
}
diff --git a/src/eros/eros.go b/src/eros/eros.go
index a184cf7..60c9359 100644
--- a/src/eros/eros.go
+++ b/src/eros/eros.go
@@ -1,5 +1,3 @@
-package eros
-
// Eros controls the Bitcask db that holds the devices captured during scanning
//
// Addr (Key)
@@ -7,19 +5,22 @@ package eros
// Raw Advertisement Packet (Advertisement)
// Services UUIDs (Services)
-// TODO:
-// Output to report
+// TODO:
//
+// Output to report
+package eros
import (
"bufio"
- "encoding/json"
"os"
"strconv"
"strings"
+ "sync"
"time"
- projVars "protomolecule/src/vars"
+ jsoniter "github.com/json-iterator/go"
+
+ projVars "protomolecule/src/config"
bluetooth "git.tcp.direct/kayos/prototooth"
"github.com/prologic/bitcask"
@@ -27,23 +28,27 @@ import (
)
var (
- // deviceDb will hold details about devices discovered
- deviceDb *bitcask.Bitcask
- // attackDb will hold details about exploits to be used against BLE devices
- attackDb *bitcask.Bitcask
- // serviceDb will hold definitions of various bluetook services and will ultimately be updated via an HTTP repository
- serviceDb *bitcask.Bitcask
+ json = jsoniter.ConfigCompatibleWithStandardLibrary
+
+ dbs = []string{
+ "devices", // details about devices discovered
+ "exploits", // details about exploits to be used against BLE devices
+ "services", // definitions of various bluetooth services and will ultimately be updated via an HTTP repository
+ "manufacturers", // manufacturer to UUID correlations and info
+ }
err error
Manufacturers ManufData
+ // Exploits Exploit
// DataDir - should be defined by config or cmd flag
DataDir string = "./.eros-data/"
)
+var DB map[string]*bitcask.Bitcask
+
// Ingest UUID will add the UUID to the manufacturer if it doesn't already exist
func (manuf *Manufacturer) IngestUUID(uuid bluetooth.UUID) bool {
-
contains := func(s []bluetooth.UUID, v bluetooth.UUID) bool {
for _, a := range s {
if a == v {
@@ -60,10 +65,24 @@ func (manuf *Manufacturer) IngestUUID(uuid bluetooth.UUID) bool {
manuf.UUIDs = append(manuf.UUIDs, uuid)
return true
}
+func FinalizeDevice(bigidea Device) *Device {
+ bigidea.mu = &sync.RWMutex{}
+ return &bigidea
+}
+
+func (d *Device) ConnectHandler(target bluetooth.Addresser, c bool) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ if c {
+ d.Connected = true
+ } else {
+ d.Connected = false
+ }
+}
// ManufLoad loads data from a json file containing UUID manufacturer associations
func ManufLoad() {
- path := projVars.ManuFile
+ path := *projVars.GlobalConfig.StringFlags.MACVendorFile
var uuid bluetooth.UUID
log.Info().
@@ -72,8 +91,8 @@ func ManufLoad() {
f, err := os.Open(path)
if err != nil {
- log.Debug().Msg(err.Error())
- os.Exit(1)
+ log.Error().Err(err).
+ Msg("Failed to open JSON file")
}
defer f.Close()
@@ -88,7 +107,7 @@ func ManufLoad() {
utmp, err := strconv.ParseUint(mf.UString, 0, 16)
if err != nil {
- log.Fatal().Str("file", projVars.ManuFile).
+ log.Fatal().Str("file", path).
Str("string", mf.UString).
Err(err).Msg("MANUFACTURER_PARSE_ERROR")
}
@@ -99,8 +118,8 @@ func ManufLoad() {
log.Debug().
Str("Manufacturer", mf.Name).
Str("UUID_String", mf.UString).
- //Interface("UUID_Type", uuid).
- //Str("Website", mf.Website).
+ // Interface("UUID_Type", uuid).
+ // Str("Website", mf.Website).
Msg("LOADED_MANUFACTURER_DATA")
mf.UString = ""
@@ -123,7 +142,7 @@ func ManufLoad() {
log.Debug().Str("Manufacturer", mf.Name).
Int("UUID_Count", len(mf.UUIDs)).
- //Interface("UUIDs", mf.UUIDs).
+ // Interface("UUIDs", mf.UUIDs).
Msg("EXISTING_MANUFACTURER_FOUND")
}
@@ -132,52 +151,64 @@ func ManufLoad() {
Manufacturers.Entries = append(Manufacturers.Entries, *mf)
}
}
+
+ for _, manuf := range Manufacturers.Entries {
+ var jsonData []byte
+ jsonData, err = json.Marshal(manuf)
+ if err != nil {
+ log.Fatal().Err(err).
+ Msg("EROS_FATAL_MANUFACTURER_JSON_MARSHAL")
+ }
+
+ err := DB["manufacturers"].Put([]byte(manuf.Name), jsonData)
+ if err != nil {
+ log.Fatal().Err(err).
+ Msg("EROS_FATAL_MANUFACTURER_JSON_STORE")
+ }
+
+ log.Debug().Str("Name", manuf.Name).
+ Msg("EROS_MANUFACTURER_STORE")
+ }
}
// Awaken - create the data directory if it does not exist; initialize bitcask in this directory
func Awaken() {
- //log.Debug().Str("DataDir",DataDir).Msg("Initializing eros...")
-
- deviceDb, err = bitcask.Open(DataDir + "devices")
- if err != nil {
- panic(err.Error)
+ DB = make(map[string]*bitcask.Bitcask)
+ for _, name := range dbs {
+ DB[name], err = bitcask.Open(DataDir+name, bitcask.WithMaxValueSize(1024*1024*1024))
+ if err != nil {
+ panic(err.Error())
+ }
}
+}
- attackDb, err = bitcask.Open(DataDir + "exploits")
+func logErr(err error) {
if err != nil {
- panic(err.Error)
- }
-
- serviceDb, err = bitcask.Open(DataDir + "services")
- if err != nil {
- panic(err.Error)
+ log.Error().Err(err).
+ Msg("EROS_DATABASE_ERROR")
}
}
// Slumber - Clean up database entries, sync them to persistent storage, and safelty close the database.
func Slumber() {
- rest := func(db *bitcask.Bitcask) {
- db.Merge()
- db.Sync()
- db.Close()
+ for _, db := range DB {
+ logErr(db.Merge())
+ logErr(db.Sync())
+ logErr(db.Close())
}
-
- rest(deviceDb)
- rest(attackDb)
- rest(serviceDb)
}
// Exists - check if a device is present in the Database
func Exists(Addr string) bool {
- if deviceDb.Has([]byte(Addr)) {
+ if DB["devices"].Has([]byte(Addr)) {
return true
}
return false
}
-// Remember - store device details into the database
-func Remember(dev Device) error {
+// Remember stores device details into the database via an argument
+func Remember(dev *Device) error {
var err error
var rhist map[time.Time]int16
@@ -200,7 +231,7 @@ func Remember(dev Device) error {
return err
}
- err = deviceDb.Put([]byte(dev.Addr), jsonData)
+ err = DB["devices"].Put([]byte(dev.Addr), jsonData)
return err
}
@@ -210,28 +241,148 @@ func Recall(Addr string) (Device, error) {
var bytes []byte
var member Device
- bytes, err = deviceDb.Get([]byte(Addr))
+ bytes, err = DB["devices"].Get([]byte(Addr))
if err != nil {
member = Device{}
return member, err
}
- json.Unmarshal(bytes, &member)
+ if err := json.Unmarshal(bytes, &member); err != nil {
+ log.Panic().Err(err).Msg("Failed to unmarshal device")
+ }
return member, err
}
+func Backup(path string) error {
+ for _, db := range DB {
+ logErr(db.Merge())
+ logErr(db.Sync())
+ err := db.Backup(path)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+/*
// Hypnosis - retrieve new exploits/attacks from a remote http repository
func Hypnosis(repo string) {
- // placeholder
+
+ e:= echo.New()
+ // CORS
+ e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
+ AllowOrigins: []string{"*"},
+ AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE} //TODO: possibly trim the methods
+ }))
+
+ // GET
+ e.GET("/exploits", func(c echo.Context) error { // This will definitely need to be updated
+ // Build request
+ req, err := http.NewRequest("GET", repo, nil)
+ if err != nil {
+ fmt.Println("Error in GET request: ", err)
+ }
+
+ // Certificate sanity checks
+ caCert, err := os.Readfile("server.crt")
+ if err != nil {
+ log.Fatal(err)
+ }
+ cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Add certificates
+ caCertPool := x509.NewCertPool()
+ caCertPool.AppendCertsFromPEM(caCert)
+
+ // Create client
+ client := &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{
+ RootCAs: caCertPool,
+ Certificates: []tls.Certificate{cert},
+ },
+ },
+ }
+
+ // Send request
+ res, err := client.Do(req)
+ if err != nil {
+ fmt.Println("Client Error: ", err)
+ }
+
+ // Defer body close
+ defer res.Body.Close()
+
+ // Late binding data from JSON
+ var exp Exploit
+
+ // Decode JSON stream
+ If err := json.NewDecoder(res.Body).Decode(&exp); err != nil {
+ fmt.Println(err)
+ }
+
+ return c.JSON(http.StatusOK, exp)
+ })
}
// Trauma - store details of an exploit/attack against BLE devices
-func Trauma(name string, targ string, cat string, vec Vector, pay Payload) {
- // placeholder
+func Trauma(exp *Exploit) error {
+ var err error
+ var rhist map[time.Time]int16
+
+ if !Known(exp.Addr) {
+ exp.Discovered = time.Now()
+ rhist = make(map[time.Time]int16)
+ } else {
+ re, _ := Flashback(exp.Addr)
+ exp.Discovered = re.Discovered
+ rhist = re.RSSIhist
+ }
+
+ rhist[time.Now()] = exp.RSSIlast
+ exp.RSSIhist = rhist
+ exp.Seen = time.Now()
+
+ var jsonData []byte
+ jsonData, err = json.Marshal(exp)
+ if err != nil {
+ return err
+ }
+
+ err = DB["exploits"].Put([]byte(exp.Addr), jsonData)
+ return err
+}
+
+func FinalizeExploit(bigidea Exploit) *Exploit {
+ bigidea.mu = &sync.RWMutex{}
+ return &bigidea
+}
+*/
+// Known - check if an exploit is present in the database
+func Known(Addr string) bool {
+ return DB["exploits"].Has([]byte(Addr))
}
// Flashback - retrieve details for the named exploit/attack
-func Flashback(name string) {
- //placeholder
+func Flashback(Addr string) (Exploit, error) {
+ var err error
+ var bytes []byte
+ var member Exploit
+
+ bytes, err = DB["exploits"].Get([]byte(Addr))
+
+ if err != nil {
+ member = Exploit{}
+ return member, err
+ }
+
+ if err := json.Unmarshal(bytes, &member); err != nil {
+ log.Panic().Err(err).Msg("Failed to unmarshal exploit")
+ }
+ return member, err
}
diff --git a/src/eros/structs.go b/src/eros/structs.go
index 0840157..d4bc2ca 100644
--- a/src/eros/structs.go
+++ b/src/eros/structs.go
@@ -1,13 +1,15 @@
package eros
import (
- "git.tcp.direct/kayos/prototooth"
+ bluetooth "git.tcp.direct/kayos/prototooth"
+ "sync"
"time"
)
type Permissions struct {
- Read string
- Write string
+ Read string
+ Write string
+ Notify string
}
type Characteristic struct {
@@ -25,7 +27,6 @@ type Service struct {
Characteristic []Characteristic
}
-
type ManufData struct {
Entries []Manufacturer
}
@@ -45,7 +46,7 @@ type Manufacturer struct {
// Exploit - BLE service exploit details to be marshalled into json before stored in bitcask
type Exploit struct {
Name string
- Target string
+ Target string // Should prabably be a struct of some sort since exploits target service chars
Category string
Vector Vector
Payload Payload
@@ -63,11 +64,14 @@ type Payload struct {
// Device will hold details about the discoverd device
type Device struct {
- Name string
- Addr string
- Manufacturer string
- RSSIlast int16
- RSSIhist map[time.Time]int16
+ Name string // local name of the device
+ Addr string // Broadcast MAC
+ Manufacturer string // Manufacturer Data if broadcast
+ RSSIlast int16 // Most Current RSSI
+ RSSIhist map[time.Time]int16 // Last RSSI readings
+ Alias string // Alias given to device
+ Trusted bool // Is is in the trusted devices
+ WakeAllowed bool // Does the device allow wake
// Services - see Service struct
Services []Service
@@ -75,4 +79,10 @@ type Device struct {
Discovered time.Time
// Seen - timestamp from when the device was last seen
Seen time.Time
+
+ Connected bool
+ Conn *bluetooth.Device
+
+ // The most at-risk type we have so far for concurrency, needs to be mutexed
+ mu *sync.RWMutex
}
diff --git a/src/protogen/ble_scan.go b/src/protogen/ble_scan.go
index 1ba6083..b1ad767 100644
--- a/src/protogen/ble_scan.go
+++ b/src/protogen/ble_scan.go
@@ -1,37 +1,37 @@
package protogen
import (
- projVars "protomolecule/src/vars"
+ "sync"
+ "time"
+
+ projVars "protomolecule/src/config"
- "protomolecule/src/eros"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
- bluetooth "git.tcp.direct/kayos/prototooth"
+ "protomolecule/src/eros"
- "time"
+ bluetooth "git.tcp.direct/kayos/prototooth"
)
// ScanMgr will keep track of concurrently running scans
var ScanMgr *Meta
+// NewScan - here we are creating an "anonymous" instance of a Scan struct
+// You'll notice it doesn't contain most of the data that actually is supposed to live within that struct
+//
+// This works because the integer ID is being used as a key that will have a global scope
+//
+// See: Remember function as it is defined in eros/eros.go
+// Remember usage in this file (scanStuff/scanStuff.go
+//
+// In these examples we are sending an instance of a struct with some data to eros; allowing it to finish filling out the rest
+// This can be done with just structs, but becomes problematic when trying to refer to them later
+//
+// ref: https://gobyexample.com/mutexes
+// ref: https://golang.org/pkg/sync/#Mutex
+// ref: https://www.geeksforgeeks.org/mutex-in-golang-with-examples/
func (m *Meta) NewScan() *Scan {
- // Here we are creating an "anonymous" instance of a Scan struct
- // You'll notice it doesn't contain most of the data that actually is supposed to live within that struct
-
- // This works because the integer ID is being used as a key that will have a global scope
- //
- // See: Remember function as it is defined in eros/eros.go
- // Remember usage in this file (scanStuff/scanStuff.go
- //
- // In these examples we are sending an instance of a struct with some data to eros; allowing it to finish filling out the rest
- // This can be done with just structs, but becomes problematic when trying to refer to them later
- //
- //TODO: implement Mutex locking from sync in most of these structs to prevent concurrent read/write operations from causing a race w̶a̶r̶ condition
- // ref: https://gobyexample.com/mutexes
- // ref: https://golang.org/pkg/sync/#Mutex
- // ref: https://www.geeksforgeeks.org/mutex-in-golang-with-examples/
-
newid := len(m.Scans)
m.Scans[newid] = &Scan{
@@ -40,23 +40,22 @@ func (m *Meta) NewScan() *Scan {
}
scan := m.Scans[newid]
- scan.Devices = make(map[int]*eros.Device)
-
- m.Count = len(m.Scans)
+ scan.Device = make(map[int]*eros.Device)
+ scan.mu = &sync.RWMutex{}
return scan
}
func (s *Scan) NewDevice(name string, addr string, manuf string, rssi int16) *eros.Device {
- newid := len(s.Devices)
- s.Devices[newid] = &eros.Device{
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ draft := eros.Device{
Name: name,
- Addr: addr,
+ Addr: addr,
Manufacturer: manuf,
RSSIlast: rssi,
}
- s.Count = len(s.Devices)
- return s.Devices[newid]
+ return eros.FinalizeDevice(draft)
}
/*
@@ -93,27 +92,22 @@ func ManfCheck(TargetAdvertData bluetooth.AdvertisementPayload) string {
func (s *Scan) resultHandler(scanAdapter *bluetooth.Adapter, result bluetooth.ScanResult) {
var sublog zerolog.Logger
- projVars.ScanAdapter.SetConnectHandler(func(Result bluetooth.Addresser, connected bool) {
- // It seems that because this is a callback that the library calls itself that it is passing us "connected" as an argument
- // therefore I think what we're supposed to do is store that boolean so we can then reference it later
- projVars.ConnectedToTarget = connected
- })
-
payload := result.AdvertisementPayload
addr := result.Address.String()
+
lname := result.LocalName()
- //adbytes := payload.Bytes()
+ // adbytes := payload.Bytes()
rssi := result.RSSI
EnumedManuf := ""
// TODO: probably make this a function or a method in eros(?)
//
- //---attempts to set the manufacturer value----
- //range through the uuids in the payload and compare
- //known manufacturers list
+ // ---attempts to set the manufacturer value----
+ // range through the uuids in the payload and compare
+ // known manufacturers list
uuids := payload.ServiceUUIDOut()
for range uuids {
- //for every element within the manufacturers list
+ // for every element within the manufacturers list
// check each UUID
for _, man := range eros.Manufacturers.Entries {
// pull the UUID the current element of the manufacturer list
@@ -121,7 +115,7 @@ func (s *Scan) resultHandler(scanAdapter *bluetooth.Adapter, result bluetooth.Sc
// for each manufacturer's UUID: compare the advertisment payload for a match
for _, manusss := range currManufErosOut {
manufBool := payload.HasServiceUUID(manusss)
- //if there is a UUID match then assign the Manufacturer's name
+ // if there is a UUID match then assign the Manufacturer's name
// to the output variable EnumedManuf
if manufBool == true {
EnumedManuf = man.Name
@@ -132,11 +126,13 @@ func (s *Scan) resultHandler(scanAdapter *bluetooth.Adapter, result bluetooth.Sc
}
- //ManufOut := ManfCheck(payload)
+ // ManufOut := ManfCheck(payload)
layToRest := func(dev *eros.Device) {
sublog.Debug().Msg("Storing data with Eros")
- eros.Remember(*dev)
+ if err := eros.Remember(dev); err != nil {
+ log.Warn().Err(err).Msg("EROS_REMEMBER_FAILURE")
+ }
// simple eros test
fromEros, err := eros.Recall(addr)
@@ -147,13 +143,13 @@ func (s *Scan) resultHandler(scanAdapter *bluetooth.Adapter, result bluetooth.Sc
log.Info().
Str("Name", fromEros.Name).
Str("Addr", fromEros.Addr).
- Str("ManuF", EnumedManuf). //needs to be changed back to fromEros.Manufacturer
+ Str("ManuF", fromEros.Manufacturer). // needs to be changed back to fromEros.Manufacturer
Int16("Current_RSSI", fromEros.RSSIlast).
Int("Service_Count", len(fromEros.Services)).
Msg("EROS_RECALL")
}
- //Services level logging
+ // Service enumeration and storage
// TODO:
// Fix Logging output here -- Prob shouldnt output this much info
// for each service discovered
@@ -164,8 +160,8 @@ func (s *Scan) resultHandler(scanAdapter *bluetooth.Adapter, result bluetooth.Sc
Int16("RSSI", rssi).Logger()
// Skipping duplicate results unless tracking mode enabled (to store RSSI as devices move)
- if projVars.TrackingMode == false {
- for _, dev := range s.Devices {
+ if !*projVars.GlobalConfig.CooleanFlags.Tracking {
+ for _, dev := range s.Device {
if addr == dev.Addr {
return
}
@@ -176,6 +172,8 @@ func (s *Scan) resultHandler(scanAdapter *bluetooth.Adapter, result bluetooth.Sc
s.Activity = time.Now()
dev := s.NewDevice(lname, addr, EnumedManuf, rssi)
+ projVars.GlobalConfig.GetScanAdapter().SetConnectHandler(dev.ConnectHandler)
+
// Record all the services advertised, append them into the nested struct within Device
for _, uuid := range uuids {
svc := &eros.Service{
@@ -183,89 +181,60 @@ func (s *Scan) resultHandler(scanAdapter *bluetooth.Adapter, result bluetooth.Sc
}
dev.Services = append(dev.Services, *svc)
- sublog.Debug().Str("UUID", svc.UUID).Msg("SERVICE_DISCOVERED")
+ sublog.Info().Str("UUID", svc.UUID).Msg("SERVICE_DISCOVERED")
}
- if !projVars.AttackMode {
+ if !*projVars.GlobalConfig.CooleanFlags.Attack {
layToRest(dev)
return
}
- ////////////////////////////////////////////////////////////////////////
- //var PreTargetHandling type
- //var TargetDeviceService *bluetooth.DeviceService
- //var TargetDeviceServiceChar *bluetooth.DeviceCharacteristic
-
sublog.Info().Str("Adapter", "Attempting Connection").Msg("ADAPTER_STATUS")
+ var connErr error
+ var err error
- // TODO: re-assess the timeout mechanism
- // this is largely going to depend on how concurrent we can get (at least with only one ble adapter)
+ // TODO: make timeout values and intervals command line arguments instead of hard coding them
+ timeout := bluetooth.NewDuration(time.Duration(3) * time.Second)
+ interval := bluetooth.NewDuration(time.Duration(20) * time.Millisecond)
- // creates a new timer of d (time.Duration)
- // outputs time on {timer_Name}.C chan
- // can then be checked against tick (time.Time)
- TimerCounts := time.NewTimer(10 * time.Millisecond)
+ dev.Conn, connErr = projVars.GlobalConfig.GetScanAdapter().
+ Connect(result.Address, bluetooth.ConnectionParams{ConnectionTimeout: timeout, MinInterval: interval})
- var (
- conTimeOut time.Duration
- //tick <-chan time.Time
- //not needed?
- //tock <-chan time.Timer
- err error
- )
-
- TargetDevice, connectError := projVars.ScanAdapter.Connect(result.Address, bluetooth.ConnectionParams{})
+ if connErr != nil {
+ sublog.Error().Err(connErr).Msg("CONNECT_ERROR")
+ layToRest(dev)
+ return
+ }
var targetServices []bluetooth.DeviceService
sublog.Info().
- Str("status", "Attempting to Read Target Services").
+ Str("status", "Connected, attempting to Read Target Services").
Msg("ADAPTER_STATUS")
- // TODO: this will be running concurrently in a goroutine,
- // so rather than set out timeout to be egregiously low we will just let it take its time
- conTimeOut = 50 * time.Millisecond
-
- //attempted to fix but probably didnt help anything
- if TimerCounts.C == nil {
- endTime := time.After(conTimeOut)
- select {
- case <-endTime:
- sublog.Error().Str("Adapter", "Connection Timeout").Msg("ADAPTER_STATUS")
- TargetDevice.Disconnect()
- endTime = nil
- default:
- if connectError != nil {
- sublog.Error().Err(connectError).Msg("CONNECT_ERROR")
- layToRest(dev)
- return
- }
- sublog.Debug().Str("Status", "Connecting...").Msg("ADAPTER_STATUS")
- }
- }
-
- if !projVars.ConnectedToTarget {
+ if !dev.Connected {
layToRest(dev)
return
}
- ServBuf := make([]byte, 255)
+ // ServBuf := make([]byte, 255)
Charbuf := make([]byte, 255)
- targetServices, err = TargetDevice.DiscoverServices(nil)
+ targetServices, err = dev.Conn.DiscoverServices(nil)
if err != nil {
sublog.Error().Err(err).Msg("DISCOVER_SERVICE_ERROR")
}
- for SerReadPos, srvcs := range targetServices {
+ // for SerReadPos, srvcs := range targetServices {
+ for _, srvcs := range targetServices {
charSer := eros.Service{
UUID: srvcs.String(),
}
sublog.Info().Str("Service UUID", charSer.UUID).
- Int("Bytes", SerReadPos).
- Str("Value", string(ServBuf[:SerReadPos])).
+ // Int("Bytes", SerReadPos).
+ // Str("Value", string(ServBuf[:SerReadPos])).
Msg("GATT_SERVICE")
sublog.Debug().Str("status", "Attempting to Retrieve Characteristic List").Msg("ADAPTER_STATUS")
@@ -275,51 +244,62 @@ func (s *Scan) resultHandler(scanAdapter *bluetooth.Adapter, result bluetooth.Sc
for _, char := range chars {
ReadPos, _ := char.Read(Charbuf)
+ // flags := char.Flags
+
sublog.Info().Str("UUID", char.UUID().String()).
- Int("Bytes", ReadPos).Str("Value", string(Charbuf[:ReadPos])).
+ // Int("Bytes", ReadPos).
+ Str("Value", string(Charbuf[:ReadPos])).
+ // Bool("Read", flags.Read()).
+ // Bool("Write", flags.Write()).
Msg("SERVICE_CHARACTERISTIC")
}
}
// finished with this device
-
- TargetDevice.Disconnect()
+ _ = dev.Conn.Disconnect()
+ dev.Connected = false
sublog.Info().Str("Adapter", "Successfully Disconnected From Target").Msg("ADAPTER_STATUS")
- projVars.ConnectedToTarget = false
layToRest(dev)
- //TODO: stop scan and call bluestuff func and pass it the local name value
+ // TODO: stop scan and call bluestuff func and pass it the local name value
+}
+
+func (s *Scan) Stop() error {
+ // TODO: fix this adapterId situation
+ err := projVars.GlobalConfig.GetScanAdapter().StopScan()
+ if err != nil {
+ return err
+ }
+ return nil
}
func (s *Scan) Start() error {
s.Started = time.Now()
// create list of devices in the Area of Operation
- //log.Debug().Msg("Creating Device Map")
+ // log.Debug().Msg("Creating Device Map")
// Enable BLE interface
log.Debug().Msg("Enabling Adapter")
- err := projVars.ScanAdapter.Enable()
+ err := projVars.GlobalConfig.GetScanAdapter().Enable()
if err != nil {
return err
}
- adapterId := projVars.ScanAdapter.ID
-
+ adapterId := projVars.GlobalConfig.GetScanAdapter().ID
log.Info().
Str("ID", adapterId).
- Bool("MODE_TRACK", projVars.TrackingMode).
- Bool("MODE_ATTACK", projVars.AttackMode).
+ Bool("MODE_TRACK", *projVars.GlobalConfig.CooleanFlags.Tracking).
+ Bool("MODE_ATTACK", *projVars.GlobalConfig.CooleanFlags.Attack).
Msg("Starting new ProtoMolecule BLE Scan")
- err = projVars.ScanAdapter.Scan(s.resultHandler)
+ err = projVars.GlobalConfig.GetScanAdapter().Scan(s.resultHandler)
if err != nil {
return err
}
-
return nil
}
diff --git a/src/protogen/structs.go b/src/protogen/structs.go
index 5de1f96..1c263cc 100644
--- a/src/protogen/structs.go
+++ b/src/protogen/structs.go
@@ -1,57 +1,63 @@
package protogen
import (
- "time"
+ bluetooth "git.tcp.direct/kayos/prototooth"
"protomolecule/src/eros"
+ "sync"
+ "time"
)
/*
- Why the Meta struct? This shit is a fucking maze.
-Well in theory we will have multiple types of scans in the future,
-if that's the case this is going to become necessary very quickly
+Why the Meta struct?
+ In theory we will have multiple types of scans in the future,
+ if that's the case this is going to become necessary very quickly
-I've mocked up some commented out examples below.
+ I've mocked up some commented out examples in the source code below.
*/
type Meta struct {
- Count int
- Scans map[int]*Scan
-
+ Scans map[int]*Scan
Mailbox chan Postcard
-
// // // Future Concepts // // //
// BLEScans map[int]*BLEScan
// LoraScans map[int]*LoraScan
// ZigScans map[int]*ZigScan
// WiFiScans map[int]*WiFiScan
+
+ // we definitely need this to be safe for concurrency
+ mu *sync.RWMutex
}
// Postcard will encapsulate interprocess communication messages that we send back to the main thread
type Postcard struct {
-
// From - the ScanID the message originated from
From int
-
// Stamp - the time the IPC message was created
Stamp time.Time
-
// Device ID - relevant DeviceID (if applicable) - Remember to use Scan.Devices[id] for easy referral
DeviceID int
-
// Command - the actual IPC command
Command string
-
// Arguments - augmenting arguments to the command
Arguments []string
-
}
-// Instance of a BLE scan
+// TODO: Form profiles on devices
+// Scan represents an instance of a BLE scan
type Scan struct {
- Count int
-
- // The ID is how we will refer back to running scans during IPC to react or cancel a scan
- ID int
- Started time.Time
+ Count int
+ ID int
+ // Started the scan at this time
+ Started time.Time
+ // Activity was last seen at this time
Activity time.Time
- Devices map[int]*eros.Device
+ // Device represents a bluetooth endpoints
+ Device map[int]*eros.Device
+ // Connection is an active connection to a bluetooth endpoint
+ Connection map[int]*bluetooth.Connection
+ /* Ignore is a map of devices to disregard during a scan,
+ by using the hardware address as a key. (needs disclaimer?) */
+ Ignore map[string]bool
+
+ // Gonna plan on mutexing scan operations just to be safe
+ mu *sync.RWMutex
}
diff --git a/src/vars/projVars.go b/src/vars/projVars.go
deleted file mode 100644
index 862fe22..0000000
--- a/src/vars/projVars.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package projVars
-
-import (
- "flag"
-
- bluetooth "git.tcp.direct/kayos/prototooth"
- "github.com/rs/zerolog"
-)
-
-//The initial list of device in the area
-var ScanList map[string]string
-
-var InitResults []string
-
-// Place holder for
-// File with list of all known Manufacturers
-// is probably unneeded*****
-var ManuFile = "./ManufUUID.json"
-
-//var to hold service UUIDs
-var SrvcUUID bluetooth.UUID
-
-var AdapterInUse = *&bluetooth.Adapter{}
-
-var AttackMode bool = false
-var TrackingMode bool = false
-
-var ConnectedToTarget bool = false
-
-//var SrvcUUIDList map[uint32]string
-
-//hold the values for the initial ble scan results..
-// ... i.e. the mac and broadcast name string
-var C = make(chan bluetooth.ScanResult)
-
-//the BLE adapter --duh
-//var Adapter = bluetooth.DefaultAdapter
-var ScanAdapter = bluetooth.DefaultAdapter
-
-var AttackAdapter = bluetooth.DefaultAdapter
-
-//Device to be targeted --not fully implemented
-//var Target = flag.String("t", "00:00:00:00:00:00", "Target device to attack")
-var DFlag = flag.Bool("d", false, "global debug logging and pretty printing")
-var AFlag = flag.Bool("a", false, "attack mode")
-var TFlag = flag.Bool("t", false, "device tracking mode")
-var MFlag = flag.String("m", "0", "json file with manufacturer identifying data to preload into eros")
-
-//flag.Parse()
-
-//var attacker bluetooth.Addresser
-//var connected bool
-//var disconnected bool = true
-
-var log *zerolog.Logger
-
-//Service UUIDs for testing
-var SonosScanTest = bluetooth.New16BitUUID(0xfe07)
-var AppleScanTest = bluetooth.New16BitUUID(0x004c)
-
-//var ServiceUUID16Out
diff --git a/src/wrath/wrath.go b/src/wrath/wrath.go
new file mode 100644
index 0000000..ea77578
--- /dev/null
+++ b/src/wrath/wrath.go
@@ -0,0 +1,161 @@
+package wrath
+
+import (
+ bluetooth "git.tcp.direct/kayos/prototooth"
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+ "tinygo.org/x/bluetooth/rawterm"
+
+ projVars "protomolecule/src/config"
+)
+
+// used for offensive operations
+/*
+func targetPicture() bluetooth.MAC {
+
+ type targetMac bluetooth.MAC
+
+ for c, err := range projVars.WAFlag {
+ append(targetMac{})
+ }
+
+ println(targetMac)
+
+ target, err := bluetooth.ParseMAC(targetMac)
+ if err != nil {
+ log.Err(err).Msg("Error")
+ }
+
+ a := bluetooth.Address{
+ MACAddress: target,
+ }
+
+ return target
+
+}
+*/
+
+func Start(targetUUID bluetooth.UUID) {
+
+ var sublog zerolog.Logger
+
+ // bluetooth.DefaultAdapter.Connect(targetPicture(), bluetooth.ConnectionParams{})
+
+ // TargetDevice, connectError := projVars.GlobalConfig.GetScanAdapter().Connect(result.Address, bluetooth.ConnectionParams{})
+
+ // Enable BLE interface.
+ err := projVars.GlobalConfig.GetScanAdapter().Enable()
+ if err != nil {
+ log.Error().
+ Err(err).
+ Msg("could not enable the BLE stack")
+ return
+ }
+
+ // The address to connect to. Set during scanning and read afterwards.
+ var foundDevice bluetooth.ScanResult
+ // Scan for NUS peripheral.
+ log.Info().Msg("Scanning...")
+ err = projVars.GlobalConfig.GetScanAdapter().Scan(func(adapter *bluetooth.Adapter, foundDevice bluetooth.ScanResult) {
+ if !foundDevice.AdvertisementPayload.HasServiceUUID(targetUUID) {
+ return
+ }
+
+ // Stop the scan.
+ err := projVars.GlobalConfig.GetScanAdapter().StopScan()
+ if err != nil {
+ // Unlikely, but we can't recover from this.
+ log.Panic().Err(err).
+ Msg("failed to stop scan")
+ }
+ })
+ if err != nil {
+ log.Error().Err(err).
+ Msg("failed to start scan")
+ return
+ }
+
+ // Found a device: print this event.
+ if name := foundDevice.LocalName(); name == "" {
+ sublog = log.With().Str("MAC", foundDevice.Address.String()).Logger()
+ } else {
+ sublog = log.With().Str("MAC", foundDevice.Address.String()).
+ Str("Name", foundDevice.LocalName()).Logger()
+ }
+
+ // Found a NUS peripheral. Connect to it.
+ device, err := projVars.GlobalConfig.GetScanAdapter().Connect(foundDevice.Address, bluetooth.ConnectionParams{})
+ if err != nil {
+ sublog.Error().Err(err).
+ Msg("Failed to connect")
+ return
+ }
+
+ // Connected. Look up the Nordic UART Service.
+ sublog.Info().Msg("Discovering services...")
+ services, err := device.DiscoverServices([]bluetooth.UUID{targetUUID})
+ if err != nil {
+ log.Error().Err(err).
+ Msg("Failed to discover the Nordic UART Service")
+ return
+ }
+ service := services[0]
+
+ // Discover all characteristics (we'll filter later)
+ chars, err := service.DiscoverCharacteristics([]bluetooth.UUID{})
+ if err != nil {
+ log.Error().Err(err).
+ Msg("Failed to discover RX and TX characteristics")
+ return
+ }
+ rx := chars[0]
+ tx := chars[1]
+
+ // Enable notifications to receive incoming data.
+ err = tx.EnableNotifications(func(value []byte) {
+ for _, c := range value {
+ rawterm.Putchar(c)
+ }
+ })
+ if err != nil {
+ sublog.Error().Err(err).
+ Msg("Failed to enable TX notifications")
+ return
+ }
+
+ sublog.Info().Msg("Connected. Exit console using Ctrl-X.")
+ rawterm.Configure()
+ defer rawterm.Restore()
+ var line []byte
+ for {
+ ch := rawterm.Getchar()
+ line = append(line, ch)
+
+ // Send the current line to the central.
+ if ch == '\x18' {
+ // The user pressed Ctrl-X, exit the program.
+ break
+ } else if ch == '\n' {
+ sendbuf := line // copy buffer
+ // Reset the slice while keeping the buffer in place.
+ line = line[:0]
+
+ // Send the sendbuf after breaking it up in pieces.
+ for len(sendbuf) != 0 {
+ // Chop off up to 20 bytes from the sendbuf.
+ partlen := 20
+ if len(sendbuf) < 20 {
+ partlen = len(sendbuf)
+ }
+ part := sendbuf[:partlen]
+ sendbuf = sendbuf[partlen:]
+ // This performs a "write command" aka "write without response".
+ _, err := rx.WriteWithoutResponse(part)
+ if err != nil {
+ sublog.Error().Err(err).
+ Msg("could not send:")
+ }
+ }
+ }
+ }
+}