mirror of
https://github.com/bettercap/bettercap
synced 2025-08-19 04:59:25 -07:00
new: implemented GET /api/events route (ref #5).
This commit is contained in:
parent
ee4b783015
commit
269d7d845b
14 changed files with 172 additions and 38 deletions
|
@ -1,6 +1,7 @@
|
||||||
# change these!
|
# change these!
|
||||||
set api.rest.username bcap
|
set api.rest.username bcap
|
||||||
set api.rest.password bcap
|
set api.rest.password bcap
|
||||||
|
# set api.rest.port 8082
|
||||||
|
|
||||||
net.probe on
|
net.probe on
|
||||||
net.recon on
|
net.recon on
|
||||||
|
|
5
main.go
5
main.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -20,8 +21,8 @@ func main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Starting %s v%s\n", core.Name, core.Version)
|
fmt.Printf("%s v%s\n", core.Name, core.Version)
|
||||||
log.Infof("Build: date=%s os=%s arch=%s\n", core.BuildDate, runtime.GOOS, runtime.GOARCH)
|
fmt.Printf("Build: date=%s os=%s arch=%s\n\n", core.BuildDate, runtime.GOOS, runtime.GOARCH)
|
||||||
|
|
||||||
sess.Register(session_modules.NewProber(sess))
|
sess.Register(session_modules.NewProber(sess))
|
||||||
sess.Register(session_modules.NewDiscovery(sess))
|
sess.Register(session_modules.NewDiscovery(sess))
|
||||||
|
|
|
@ -8,19 +8,19 @@ import (
|
||||||
"github.com/evilsocket/bettercap-ng/core"
|
"github.com/evilsocket/bettercap-ng/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type OnHostResolvedCallback func(e *Endpoint)
|
||||||
type Endpoint struct {
|
type Endpoint struct {
|
||||||
IP net.IP
|
IP net.IP `json:"-"`
|
||||||
HW net.HardwareAddr
|
HW net.HardwareAddr `json:"-"`
|
||||||
IpAddress string
|
IpAddress string `json:"address"`
|
||||||
SubnetBits uint32
|
SubnetBits uint32 `json:"-"`
|
||||||
IpAddressUint32 uint32
|
IpAddressUint32 uint32 `json:"-"`
|
||||||
HwAddress string
|
HwAddress string `json:"mac"`
|
||||||
Hostname string
|
Hostname string `json:"hostname"`
|
||||||
Vendor string
|
Vendor string `json:"vendor"`
|
||||||
|
ResolvedCallback OnHostResolvedCallback `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OnHostResolvedAction func(e *Endpoint)
|
|
||||||
|
|
||||||
func NewEndpointNoResolve(ip, mac, name string, bits uint32) *Endpoint {
|
func NewEndpointNoResolve(ip, mac, name string, bits uint32) *Endpoint {
|
||||||
hw, err := net.ParseMAC(mac)
|
hw, err := net.ParseMAC(mac)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -36,6 +36,7 @@ func NewEndpointNoResolve(ip, mac, name string, bits uint32) *Endpoint {
|
||||||
HwAddress: mac,
|
HwAddress: mac,
|
||||||
Hostname: name,
|
Hostname: name,
|
||||||
Vendor: OuiLookup(mac),
|
Vendor: OuiLookup(mac),
|
||||||
|
ResolvedCallback: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
return e
|
return e
|
||||||
|
@ -48,7 +49,10 @@ func NewEndpoint(ip, mac string) *Endpoint {
|
||||||
go func() {
|
go func() {
|
||||||
if names, err := net.LookupAddr(e.IpAddress); err == nil {
|
if names, err := net.LookupAddr(e.IpAddress); err == nil {
|
||||||
e.Hostname = names[0]
|
e.Hostname = names[0]
|
||||||
log.Debugf("Endpoint %s is now known as %s\n", e.IpAddress, core.Green(e.Hostname))
|
if e.ResolvedCallback != nil {
|
||||||
|
// log.Debugf("Endpoint %s is now known as %s\n", e.IpAddress, core.Green(e.Hostname))
|
||||||
|
e.ResolvedCallback(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,15 @@ type Environment struct {
|
||||||
Padding int `json:"-"`
|
Padding int `json:"-"`
|
||||||
Storage map[string]string `json:"storage"`
|
Storage map[string]string `json:"storage"`
|
||||||
lock *sync.Mutex
|
lock *sync.Mutex
|
||||||
|
sess *Session
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEnvironment() *Environment {
|
func NewEnvironment(s *Session) *Environment {
|
||||||
env := &Environment{
|
env := &Environment{
|
||||||
Padding: 0,
|
Padding: 0,
|
||||||
Storage: make(map[string]string),
|
Storage: make(map[string]string),
|
||||||
lock: &sync.Mutex{},
|
lock: &sync.Mutex{},
|
||||||
|
sess: s,
|
||||||
}
|
}
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
@ -39,6 +41,14 @@ func (env *Environment) Set(name, value string) string {
|
||||||
old, _ := env.Storage[name]
|
old, _ := env.Storage[name]
|
||||||
env.Storage[name] = value
|
env.Storage[name] = value
|
||||||
|
|
||||||
|
env.sess.Events.Add("env.change", struct {
|
||||||
|
name string
|
||||||
|
value string
|
||||||
|
}{
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
})
|
||||||
|
|
||||||
width := len(name)
|
width := len(name)
|
||||||
if width > env.Padding {
|
if width > env.Padding {
|
||||||
env.Padding = width
|
env.Padding = width
|
||||||
|
|
53
session/events.go
Normal file
53
session/events.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/evilsocket/bettercap-ng/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
Time time.Time `json:"time"`
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEvent(tag string, data interface{}) Event {
|
||||||
|
return Event{
|
||||||
|
Tag: tag,
|
||||||
|
Time: time.Now(),
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Event) Print() {
|
||||||
|
fmt.Printf("[%s] [%s] [%s] %+v\n", e.Time, core.Bold("event"), core.Green(e.Tag), e.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventPool struct {
|
||||||
|
events []Event
|
||||||
|
lock *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEventPool() *EventPool {
|
||||||
|
return &EventPool{
|
||||||
|
events: make([]Event, 0),
|
||||||
|
lock: &sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EventPool) Add(tag string, data interface{}) {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
e := NewEvent(tag, data)
|
||||||
|
p.events = append([]Event{e}, p.events...)
|
||||||
|
e.Print()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EventPool) Events() []Event {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
return p.events
|
||||||
|
}
|
|
@ -18,16 +18,18 @@ type Module interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionModule struct {
|
type SessionModule struct {
|
||||||
Session *Session
|
Name string `json:"name"`
|
||||||
Started bool
|
Session *Session `json:"-"`
|
||||||
StatusLock *sync.Mutex
|
Started bool `json:"started"`
|
||||||
|
StatusLock *sync.Mutex `json:"-"`
|
||||||
|
|
||||||
handlers []ModuleHandler
|
handlers []ModuleHandler
|
||||||
params map[string]*ModuleParam
|
params map[string]*ModuleParam
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSessionModule(s *Session) SessionModule {
|
func NewSessionModule(name string, s *Session) SessionModule {
|
||||||
m := SessionModule{
|
m := SessionModule{
|
||||||
|
Name: name,
|
||||||
Session: s,
|
Session: s,
|
||||||
Started: false,
|
Started: false,
|
||||||
StatusLock: &sync.Mutex{},
|
StatusLock: &sync.Mutex{},
|
||||||
|
@ -70,6 +72,12 @@ func (m *SessionModule) SetRunning(running bool) {
|
||||||
m.StatusLock.Lock()
|
m.StatusLock.Lock()
|
||||||
defer m.StatusLock.Unlock()
|
defer m.StatusLock.Unlock()
|
||||||
m.Started = running
|
m.Started = running
|
||||||
|
|
||||||
|
if running {
|
||||||
|
m.Session.Events.Add("mod.started", m.Name)
|
||||||
|
} else {
|
||||||
|
m.Session.Events.Add("mod.stopped", m.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SessionModule) OnSessionStarted(s *Session) {
|
func (m *SessionModule) OnSessionStarted(s *Session) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/evilsocket/bettercap-ng/core"
|
"github.com/evilsocket/bettercap-ng/core"
|
||||||
|
@ -20,7 +21,7 @@ type RestAPI struct {
|
||||||
|
|
||||||
func NewRestAPI(s *session.Session) *RestAPI {
|
func NewRestAPI(s *session.Session) *RestAPI {
|
||||||
api := &RestAPI{
|
api := &RestAPI{
|
||||||
SessionModule: session.NewSessionModule(s),
|
SessionModule: session.NewSessionModule("api.rest", s),
|
||||||
server: &http.Server{},
|
server: &http.Server{},
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
|
@ -58,6 +59,7 @@ func NewRestAPI(s *session.Session) *RestAPI {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
http.HandleFunc("/api/session", api.sessRoute)
|
http.HandleFunc("/api/session", api.sessRoute)
|
||||||
|
http.HandleFunc("/api/events", api.eventsRoute)
|
||||||
|
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
@ -113,6 +115,45 @@ func (api *RestAPI) sessRoute(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *RestAPI) eventsRoute(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if api.checkAuth(w, r) == false {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == "GET" {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
events := api.Session.Events.Events()
|
||||||
|
nmax := len(events)
|
||||||
|
n := nmax
|
||||||
|
|
||||||
|
keys, ok := r.URL.Query()["n"]
|
||||||
|
if len(keys) == 1 && ok {
|
||||||
|
sn := keys[0]
|
||||||
|
n, err = strconv.Atoi(sn)
|
||||||
|
if err == nil {
|
||||||
|
if n > nmax {
|
||||||
|
n = nmax
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n = nmax
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
js, err := json.Marshal(events[0:n])
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error while returning events: %s", err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write(js)
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Not Found", 404)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (api RestAPI) checkAuth(w http.ResponseWriter, r *http.Request) bool {
|
func (api RestAPI) checkAuth(w http.ResponseWriter, r *http.Request) bool {
|
||||||
if api.Authenticated(w, r) == false {
|
if api.Authenticated(w, r) == false {
|
||||||
log.Warningf("Unauthenticated access!")
|
log.Warningf("Unauthenticated access!")
|
||||||
|
|
|
@ -17,7 +17,7 @@ type ArpSpoofer struct {
|
||||||
|
|
||||||
func NewArpSpoofer(s *session.Session) *ArpSpoofer {
|
func NewArpSpoofer(s *session.Session) *ArpSpoofer {
|
||||||
p := &ArpSpoofer{
|
p := &ArpSpoofer{
|
||||||
SessionModule: session.NewSessionModule(s),
|
SessionModule: session.NewSessionModule("arp.spoof", s),
|
||||||
Done: make(chan bool),
|
Done: make(chan bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (p HttpProxy) logAction(req *http.Request, jsres *JSResponse) {
|
||||||
|
|
||||||
func NewHttpProxy(s *session.Session) *HttpProxy {
|
func NewHttpProxy(s *session.Session) *HttpProxy {
|
||||||
p := &HttpProxy{
|
p := &HttpProxy{
|
||||||
SessionModule: session.NewSessionModule(s),
|
SessionModule: session.NewSessionModule("http.proxy", s),
|
||||||
proxy: nil,
|
proxy: nil,
|
||||||
address: "",
|
address: "",
|
||||||
redirection: nil,
|
redirection: nil,
|
||||||
|
|
|
@ -15,7 +15,7 @@ type Prober struct {
|
||||||
|
|
||||||
func NewProber(s *session.Session) *Prober {
|
func NewProber(s *session.Session) *Prober {
|
||||||
p := &Prober{
|
p := &Prober{
|
||||||
SessionModule: session.NewSessionModule(s),
|
SessionModule: session.NewSessionModule("net.probe", s),
|
||||||
}
|
}
|
||||||
|
|
||||||
p.AddParam(session.NewIntParameter("net.probe.throttle",
|
p.AddParam(session.NewIntParameter("net.probe.throttle",
|
||||||
|
|
|
@ -18,7 +18,7 @@ type Discovery struct {
|
||||||
|
|
||||||
func NewDiscovery(s *session.Session) *Discovery {
|
func NewDiscovery(s *session.Session) *Discovery {
|
||||||
d := &Discovery{
|
d := &Discovery{
|
||||||
SessionModule: session.NewSessionModule(s),
|
SessionModule: session.NewSessionModule("net.recon", s),
|
||||||
|
|
||||||
refresh: 1,
|
refresh: 1,
|
||||||
before: nil,
|
before: nil,
|
||||||
|
|
|
@ -117,7 +117,7 @@ type Sniffer struct {
|
||||||
|
|
||||||
func NewSniffer(s *session.Session) *Sniffer {
|
func NewSniffer(s *session.Session) *Sniffer {
|
||||||
sniff := &Sniffer{
|
sniff := &Sniffer{
|
||||||
SessionModule: session.NewSessionModule(s),
|
SessionModule: session.NewSessionModule("net.sniffer", s),
|
||||||
Stats: nil,
|
Stats: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,25 +32,30 @@ type Session struct {
|
||||||
Input *readline.Instance `json:"-"`
|
Input *readline.Instance `json:"-"`
|
||||||
Active bool `json:"active"`
|
Active bool `json:"active"`
|
||||||
|
|
||||||
// Watcher *discovery.Watcher
|
|
||||||
CoreHandlers []CommandHandler `json:"-"`
|
CoreHandlers []CommandHandler `json:"-"`
|
||||||
Modules []Module `json:"-"`
|
Modules []Module `json:"-"`
|
||||||
HelpPadding int `json:"-"`
|
HelpPadding int `json:"-"`
|
||||||
|
|
||||||
|
Events *EventPool `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() (*Session, error) {
|
func New() (*Session, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
s := &Session{
|
s := &Session{
|
||||||
Env: NewEnvironment(),
|
Env: nil,
|
||||||
Active: false,
|
Active: false,
|
||||||
Queue: nil,
|
Queue: nil,
|
||||||
|
|
||||||
CoreHandlers: make([]CommandHandler, 0),
|
CoreHandlers: make([]CommandHandler, 0),
|
||||||
Modules: make([]Module, 0),
|
Modules: make([]Module, 0),
|
||||||
HelpPadding: 0,
|
HelpPadding: 0,
|
||||||
|
|
||||||
|
Events: NewEventPool(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.Env = NewEnvironment(s)
|
||||||
|
|
||||||
if s.Options, err = core.ParseOptions(); err != nil {
|
if s.Options, err = core.ParseOptions(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -265,6 +270,8 @@ func (s *Session) setupInput() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Close() {
|
func (s *Session) Close() {
|
||||||
|
s.Events.Add("session.closing", nil)
|
||||||
|
|
||||||
for _, m := range s.Modules {
|
for _, m := range s.Modules {
|
||||||
m.OnSessionEnded(s)
|
m.OnSessionEnded(s)
|
||||||
}
|
}
|
||||||
|
@ -311,7 +318,7 @@ func (s *Session) Start() error {
|
||||||
|
|
||||||
log.Debugf("[%sgateway%s] %s\n", core.GREEN, core.RESET, s.Gateway)
|
log.Debugf("[%sgateway%s] %s\n", core.GREEN, core.RESET, s.Gateway)
|
||||||
|
|
||||||
s.Targets = NewTargets(s.Interface, s.Gateway)
|
s.Targets = NewTargets(s, s.Interface, s.Gateway)
|
||||||
s.Firewall = firewall.Make()
|
s.Firewall = firewall.Make()
|
||||||
|
|
||||||
if err := s.setupInput(); err != nil {
|
if err := s.setupInput(); err != nil {
|
||||||
|
@ -354,6 +361,8 @@ func (s *Session) Start() error {
|
||||||
m.OnSessionStarted(s)
|
m.OnSessionStarted(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.Events.Add("session.started", nil)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,14 +14,16 @@ import (
|
||||||
var log = logging.MustGetLogger("mitm")
|
var log = logging.MustGetLogger("mitm")
|
||||||
|
|
||||||
type Targets struct {
|
type Targets struct {
|
||||||
|
Session *Session `json:"-"`
|
||||||
Interface *net.Endpoint
|
Interface *net.Endpoint
|
||||||
Gateway *net.Endpoint
|
Gateway *net.Endpoint
|
||||||
Targets map[string]*net.Endpoint
|
Targets map[string]*net.Endpoint
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTargets(iface, gateway *net.Endpoint) *Targets {
|
func NewTargets(s *Session, iface, gateway *net.Endpoint) *Targets {
|
||||||
return &Targets{
|
return &Targets{
|
||||||
|
Session: s,
|
||||||
Interface: iface,
|
Interface: iface,
|
||||||
Gateway: gateway,
|
Gateway: gateway,
|
||||||
Targets: make(map[string]*net.Endpoint),
|
Targets: make(map[string]*net.Endpoint),
|
||||||
|
@ -33,7 +35,7 @@ func (tp *Targets) Remove(ip, mac string) {
|
||||||
defer tp.lock.Unlock()
|
defer tp.lock.Unlock()
|
||||||
|
|
||||||
if e, found := tp.Targets[mac]; found {
|
if e, found := tp.Targets[mac]; found {
|
||||||
log.Infof("[%slost%s] %s\n", core.RED, core.RESET, e)
|
tp.Session.Events.Add("target-lost", e)
|
||||||
delete(tp.Targets, mac)
|
delete(tp.Targets, mac)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -69,9 +71,14 @@ func (tp *Targets) AddIfNotExist(ip, mac string) *net.Endpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
e := net.NewEndpoint(ip, mac)
|
e := net.NewEndpoint(ip, mac)
|
||||||
log.Infof("[%snew%s] %s\n", core.GREEN, core.RESET, e)
|
e.ResolvedCallback = func(e *net.Endpoint) {
|
||||||
|
tp.Session.Events.Add("target.resolved", e)
|
||||||
|
}
|
||||||
|
|
||||||
tp.Targets[mac] = e
|
tp.Targets[mac] = e
|
||||||
|
|
||||||
|
tp.Session.Events.Add("target.new", e)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue