new: implemented GET /api/events route (ref #5).

This commit is contained in:
evilsocket 2018-01-08 05:39:34 +01:00
commit 269d7d845b
14 changed files with 172 additions and 38 deletions

View file

@ -1,6 +1,7 @@
# change these!
set api.rest.username bcap
set api.rest.password bcap
# set api.rest.port 8082
net.probe on
net.recon on

View file

@ -1,6 +1,7 @@
package main
import (
"fmt"
"runtime"
"strings"
@ -20,8 +21,8 @@ func main() {
panic(err)
}
log.Infof("Starting %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("%s v%s\n", core.Name, core.Version)
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.NewDiscovery(sess))

View file

@ -8,19 +8,19 @@ import (
"github.com/evilsocket/bettercap-ng/core"
)
type OnHostResolvedCallback func(e *Endpoint)
type Endpoint struct {
IP net.IP
HW net.HardwareAddr
IpAddress string
SubnetBits uint32
IpAddressUint32 uint32
HwAddress string
Hostname string
Vendor string
IP net.IP `json:"-"`
HW net.HardwareAddr `json:"-"`
IpAddress string `json:"address"`
SubnetBits uint32 `json:"-"`
IpAddressUint32 uint32 `json:"-"`
HwAddress string `json:"mac"`
Hostname string `json:"hostname"`
Vendor string `json:"vendor"`
ResolvedCallback OnHostResolvedCallback `json:"-"`
}
type OnHostResolvedAction func(e *Endpoint)
func NewEndpointNoResolve(ip, mac, name string, bits uint32) *Endpoint {
hw, err := net.ParseMAC(mac)
if err != nil {
@ -36,6 +36,7 @@ func NewEndpointNoResolve(ip, mac, name string, bits uint32) *Endpoint {
HwAddress: mac,
Hostname: name,
Vendor: OuiLookup(mac),
ResolvedCallback: nil,
}
return e
@ -48,7 +49,10 @@ func NewEndpoint(ip, mac string) *Endpoint {
go func() {
if names, err := net.LookupAddr(e.IpAddress); err == nil {
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)
}
}
}()

View file

@ -11,13 +11,15 @@ type Environment struct {
Padding int `json:"-"`
Storage map[string]string `json:"storage"`
lock *sync.Mutex
sess *Session
}
func NewEnvironment() *Environment {
func NewEnvironment(s *Session) *Environment {
env := &Environment{
Padding: 0,
Storage: make(map[string]string),
lock: &sync.Mutex{},
sess: s,
}
return env
@ -39,6 +41,14 @@ func (env *Environment) Set(name, value string) string {
old, _ := env.Storage[name]
env.Storage[name] = value
env.sess.Events.Add("env.change", struct {
name string
value string
}{
name,
value,
})
width := len(name)
if width > env.Padding {
env.Padding = width

53
session/events.go Normal file
View 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
}

View file

@ -18,16 +18,18 @@ type Module interface {
}
type SessionModule struct {
Session *Session
Started bool
StatusLock *sync.Mutex
Name string `json:"name"`
Session *Session `json:"-"`
Started bool `json:"started"`
StatusLock *sync.Mutex `json:"-"`
handlers []ModuleHandler
params map[string]*ModuleParam
}
func NewSessionModule(s *Session) SessionModule {
func NewSessionModule(name string, s *Session) SessionModule {
m := SessionModule{
Name: name,
Session: s,
Started: false,
StatusLock: &sync.Mutex{},
@ -70,6 +72,12 @@ func (m *SessionModule) SetRunning(running bool) {
m.StatusLock.Lock()
defer m.StatusLock.Unlock()
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) {

View file

@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/evilsocket/bettercap-ng/core"
@ -20,7 +21,7 @@ type RestAPI struct {
func NewRestAPI(s *session.Session) *RestAPI {
api := &RestAPI{
SessionModule: session.NewSessionModule(s),
SessionModule: session.NewSessionModule("api.rest", s),
server: &http.Server{},
username: "",
password: "",
@ -58,6 +59,7 @@ func NewRestAPI(s *session.Session) *RestAPI {
}))
http.HandleFunc("/api/session", api.sessRoute)
http.HandleFunc("/api/events", api.eventsRoute)
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 {
if api.Authenticated(w, r) == false {
log.Warningf("Unauthenticated access!")

View file

@ -17,7 +17,7 @@ type ArpSpoofer struct {
func NewArpSpoofer(s *session.Session) *ArpSpoofer {
p := &ArpSpoofer{
SessionModule: session.NewSessionModule(s),
SessionModule: session.NewSessionModule("arp.spoof", s),
Done: make(chan bool),
}

View file

@ -38,7 +38,7 @@ func (p HttpProxy) logAction(req *http.Request, jsres *JSResponse) {
func NewHttpProxy(s *session.Session) *HttpProxy {
p := &HttpProxy{
SessionModule: session.NewSessionModule(s),
SessionModule: session.NewSessionModule("http.proxy", s),
proxy: nil,
address: "",
redirection: nil,

View file

@ -15,7 +15,7 @@ type Prober struct {
func NewProber(s *session.Session) *Prober {
p := &Prober{
SessionModule: session.NewSessionModule(s),
SessionModule: session.NewSessionModule("net.probe", s),
}
p.AddParam(session.NewIntParameter("net.probe.throttle",

View file

@ -18,7 +18,7 @@ type Discovery struct {
func NewDiscovery(s *session.Session) *Discovery {
d := &Discovery{
SessionModule: session.NewSessionModule(s),
SessionModule: session.NewSessionModule("net.recon", s),
refresh: 1,
before: nil,

View file

@ -117,7 +117,7 @@ type Sniffer struct {
func NewSniffer(s *session.Session) *Sniffer {
sniff := &Sniffer{
SessionModule: session.NewSessionModule(s),
SessionModule: session.NewSessionModule("net.sniffer", s),
Stats: nil,
}

View file

@ -32,25 +32,30 @@ type Session struct {
Input *readline.Instance `json:"-"`
Active bool `json:"active"`
// Watcher *discovery.Watcher
CoreHandlers []CommandHandler `json:"-"`
Modules []Module `json:"-"`
HelpPadding int `json:"-"`
Events *EventPool `json:"-"`
}
func New() (*Session, error) {
var err error
s := &Session{
Env: NewEnvironment(),
Env: nil,
Active: false,
Queue: nil,
CoreHandlers: make([]CommandHandler, 0),
Modules: make([]Module, 0),
HelpPadding: 0,
Events: NewEventPool(),
}
s.Env = NewEnvironment(s)
if s.Options, err = core.ParseOptions(); err != nil {
return nil, err
}
@ -265,6 +270,8 @@ func (s *Session) setupInput() error {
}
func (s *Session) Close() {
s.Events.Add("session.closing", nil)
for _, m := range s.Modules {
m.OnSessionEnded(s)
}
@ -311,7 +318,7 @@ func (s *Session) Start() error {
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()
if err := s.setupInput(); err != nil {
@ -354,6 +361,8 @@ func (s *Session) Start() error {
m.OnSessionStarted(s)
}
s.Events.Add("session.started", nil)
return nil
}

View file

@ -14,14 +14,16 @@ import (
var log = logging.MustGetLogger("mitm")
type Targets struct {
Session *Session `json:"-"`
Interface *net.Endpoint
Gateway *net.Endpoint
Targets map[string]*net.Endpoint
lock sync.Mutex
}
func NewTargets(iface, gateway *net.Endpoint) *Targets {
func NewTargets(s *Session, iface, gateway *net.Endpoint) *Targets {
return &Targets{
Session: s,
Interface: iface,
Gateway: gateway,
Targets: make(map[string]*net.Endpoint),
@ -33,7 +35,7 @@ func (tp *Targets) Remove(ip, mac string) {
defer tp.lock.Unlock()
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)
return
}
@ -69,9 +71,14 @@ func (tp *Targets) AddIfNotExist(ip, mac string) *net.Endpoint {
}
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.Session.Events.Add("target.new", e)
return nil
}