refact: session/modules -> modules

This commit is contained in:
evilsocket 2018-01-08 09:28:46 +01:00
commit 64221a126d
13 changed files with 1 additions and 1 deletions

Binary file not shown.

View file

@ -1,206 +0,0 @@
package modules
import (
"context"
"fmt"
"net/http"
"time"
"github.com/evilsocket/bettercap-ng/core"
"github.com/evilsocket/bettercap-ng/session"
"github.com/evilsocket/bettercap-ng/tls"
)
type RestAPI struct {
session.SessionModule
server *http.Server
username string
password string
certFile string
keyFile string
}
func NewRestAPI(s *session.Session) *RestAPI {
api := &RestAPI{
SessionModule: session.NewSessionModule("api.rest", s),
server: &http.Server{},
username: "",
password: "",
}
api.AddParam(session.NewStringParameter("api.rest.address",
"<interface address>",
`^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`,
"Address to bind the API REST server to."))
api.AddParam(session.NewIntParameter("api.rest.port",
"8083",
"Port to bind the API REST server to."))
api.AddParam(session.NewStringParameter("api.rest.username",
"",
"",
"API authentication username."))
api.AddParam(session.NewStringParameter("api.rest.certificate",
"~/.bettercap-ng.certificate.pem",
"",
"API TLS certificate."))
api.AddParam(session.NewStringParameter("api.rest.key",
"~/.bettercap-ng.key.pem",
"",
"API TLS key"))
api.AddParam(session.NewStringParameter("api.rest.password",
"",
"",
"API authentication password."))
api.AddHandler(session.NewModuleHandler("api.rest on", "",
"Start REST API server.",
func(args []string) error {
return api.Start()
}))
api.AddHandler(session.NewModuleHandler("api.rest off", "",
"Stop REST API server.",
func(args []string) error {
return api.Stop()
}))
api.setupRoutes()
return api
}
type JSSessionRequest struct {
Command string `json:"cmd"`
}
type JSSessionResponse struct {
Error string `json:"error"`
}
func (api *RestAPI) Name() string {
return "REST API"
}
func (api *RestAPI) Description() string {
return "Expose a RESTful API."
}
func (api *RestAPI) Author() string {
return "Simone Margaritelli <evilsocket@protonmail.com>"
}
func (api *RestAPI) OnSessionStarted(s *session.Session) {
// refresh the address after session has been created
s.Env.Set("api.rest.address", s.Interface.IpAddress)
}
func (api *RestAPI) OnSessionEnded(s *session.Session) {
if api.Running() {
api.Stop()
}
}
func (api *RestAPI) configure() error {
var address string
var port int
if err, v := api.Param("api.rest.address").Get(api.Session); err != nil {
return err
} else {
address = v.(string)
}
if err, v := api.Param("api.rest.port").Get(api.Session); err != nil {
return err
} else {
port = v.(int)
}
api.server.Addr = fmt.Sprintf("%s:%d", address, port)
if err, v := api.Param("api.rest.certificate").Get(api.Session); err != nil {
return err
} else {
api.certFile = v.(string)
if api.certFile, err = core.ExpandPath(api.certFile); err != nil {
return err
}
}
if err, v := api.Param("api.rest.key").Get(api.Session); err != nil {
return err
} else {
api.keyFile = v.(string)
if api.keyFile, err = core.ExpandPath(api.keyFile); err != nil {
return err
}
}
if err, v := api.Param("api.rest.username").Get(api.Session); err != nil {
return err
} else {
api.username = v.(string)
if api.username == "" {
return fmt.Errorf("api.rest.username is empty.")
}
}
if err, v := api.Param("api.rest.password").Get(api.Session); err != nil {
return err
} else {
api.password = v.(string)
if api.password == "" {
return fmt.Errorf("api.rest.password is empty.")
}
}
if core.Exists(api.certFile) == false || core.Exists(api.keyFile) == false {
api.Session.Events.Log(session.INFO, "Generating RSA key to %s", api.keyFile)
api.Session.Events.Log(session.INFO, "Generating TLS certificate to %s", api.certFile)
if err := tls.Generate(api.certFile, api.keyFile); err != nil {
return err
}
} else {
api.Session.Events.Log(session.INFO, "Loading RSA key from %s", api.keyFile)
api.Session.Events.Log(session.INFO, "Loading TLS certificate from %s", api.certFile)
}
return nil
}
func (api *RestAPI) Start() error {
if err := api.configure(); err != nil {
return err
}
if api.Running() == false {
api.SetRunning(true)
go func() {
api.Session.Events.Log(session.INFO, "API server starting on https://%s", api.server.Addr)
err := api.server.ListenAndServeTLS(api.certFile, api.keyFile)
if err != nil && err != http.ErrServerClosed {
panic(err)
}
}()
return nil
}
return fmt.Errorf("REST API server already started.")
}
func (api *RestAPI) Stop() error {
if api.Running() == true {
api.SetRunning(false)
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
return api.server.Shutdown(ctx)
} else {
return fmt.Errorf("REST API server already stopped.")
}
}

View file

@ -1,135 +0,0 @@
package modules
import (
"encoding/base64"
"encoding/json"
"net/http"
"strconv"
"strings"
"github.com/evilsocket/bettercap-ng/session"
)
func (api *RestAPI) setupRoutes() {
http.HandleFunc("/api/session", api.sessRoute)
http.HandleFunc("/api/events", api.eventsRoute)
}
func (api RestAPI) checkAuth(w http.ResponseWriter, r *http.Request) bool {
if api.Authenticated(w, r) == false {
api.Session.Events.Log(session.WARNING, "Unauthenticated access!")
http.Error(w, "Not authorized", 401)
return false
}
return true
}
func (api RestAPI) Authenticated(w http.ResponseWriter, r *http.Request) bool {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
parts := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
if len(parts) != 2 {
return false
}
b, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
return false
}
pair := strings.SplitN(string(b), ":", 2)
if len(pair) != 2 {
return false
}
if pair[0] != api.username || pair[1] != api.password {
return false
}
return true
}
func (api *RestAPI) sessRoute(w http.ResponseWriter, r *http.Request) {
if api.checkAuth(w, r) == false {
return
}
if r.Method == "GET" {
js, err := json.Marshal(api.Session)
if err != nil {
api.Session.Events.Log(session.ERROR, "Error while returning session: %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(js)
} else if r.Method == "POST" && r.Body != nil {
var req JSSessionRequest
var res JSSessionResponse
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
http.Error(w, err.Error(), 400)
return
}
err = api.Session.Run(req.Command)
if err != nil {
res.Error = err.Error()
}
js, err := json.Marshal(res)
if err != nil {
api.Session.Events.Log(session.ERROR, "Error while returning response: %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) 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 {
api.Session.Events.Log(session.ERROR, "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 if r.Method == "DELETE" {
api.Session.Events.Clear()
api.Session.Events.Add("sys.log.cleared", nil)
} else {
http.Error(w, "Not Found", 404)
}
}

View file

@ -1,215 +0,0 @@
package modules
import (
"fmt"
network "github.com/evilsocket/bettercap-ng/net"
"github.com/evilsocket/bettercap-ng/packets"
"github.com/evilsocket/bettercap-ng/session"
"github.com/malfunkt/iprange"
"net"
"time"
)
type ArpSpoofer struct {
session.SessionModule
Done chan bool
}
func NewArpSpoofer(s *session.Session) *ArpSpoofer {
p := &ArpSpoofer{
SessionModule: session.NewSessionModule("arp.spoof", s),
Done: make(chan bool),
}
p.AddParam(session.NewStringParameter("arp.spoof.targets", "<entire subnet>", "", "IP addresses to spoof."))
p.AddHandler(session.NewModuleHandler("arp.spoof on", "",
"Start ARP spoofer.",
func(args []string) error {
return p.Start()
}))
p.AddHandler(session.NewModuleHandler("arp.spoof off", "",
"Stop ARP spoofer.",
func(args []string) error {
return p.Stop()
}))
return p
}
func (p *ArpSpoofer) OnSessionStarted(s *session.Session) {
// refresh the subnet after session has been created
s.Env.Set("arp.spoof.targets", s.Interface.CIDR())
}
func (p *ArpSpoofer) OnSessionEnded(s *session.Session) {
if p.Running() {
p.Stop()
}
}
func (p ArpSpoofer) Name() string {
return "ARP Spoofer"
}
func (p ArpSpoofer) Description() string {
return "Keep spoofing selected hosts on the network."
}
func (p ArpSpoofer) Author() string {
return "Simone Margaritelli <evilsocket@protonmail.com>"
}
func (p *ArpSpoofer) shouldSpoof(ip net.IP) bool {
addr := ip.String()
if ip.IsLoopback() == true {
return false
} else if addr == p.Session.Interface.IpAddress {
return false
} else if addr == p.Session.Gateway.IpAddress {
return false
}
return true
}
func (p *ArpSpoofer) getMAC(ip net.IP, probe bool) (net.HardwareAddr, error) {
var mac string
var hw net.HardwareAddr
var err error
// do we have this ip mac address?
mac, err = network.ArpLookup(p.Session.Interface.Name(), ip.String(), false)
if err != nil && probe == true {
from := p.Session.Interface.IP
from_hw := p.Session.Interface.HW
if err, probe := packets.NewUDPProbe(from, from_hw, ip, 139); err != nil {
p.Session.Events.Log(session.ERROR, "Error while creating UDP probe packet for %s: %s\n", ip.String(), err)
} else {
p.Session.Queue.Send(probe)
}
time.Sleep(500 * time.Millisecond)
mac, err = network.ArpLookup(p.Session.Interface.Name(), ip.String(), false)
}
if mac == "" {
return nil, fmt.Errorf("Could not find hardware address for %s.", ip.String())
}
hw, err = net.ParseMAC(mac)
if err != nil {
return nil, fmt.Errorf("Error while parsing hardware address '%s' for %s: %s", mac, ip.String(), err)
}
return hw, nil
}
func (p *ArpSpoofer) sendArp(addresses []net.IP, saddr net.IP, smac net.HardwareAddr, check_running bool, probe bool) {
for _, ip := range addresses {
if p.shouldSpoof(ip) == false {
p.Session.Events.Log(session.DEBUG, "Skipping address %s from ARP spoofing.\n", ip)
continue
}
// do we have this ip mac address?
hw, err := p.getMAC(ip, probe)
if err != nil {
p.Session.Events.Log(session.DEBUG, "Error while looking up hardware address for %s: %s\n", ip.String(), err)
continue
}
if err, pkt := packets.NewARPReply(saddr, smac, ip, hw); err != nil {
p.Session.Events.Log(session.ERROR, "Error while creating ARP spoof packet for %s: %s\n", ip.String(), err)
} else {
p.Session.Events.Log(session.DEBUG, "Sending %d bytes of ARP packet to %s:%s.\n", len(pkt), ip.String(), hw.String())
p.Session.Queue.Send(pkt)
}
if check_running && p.Running() == false {
return
}
}
}
func (p *ArpSpoofer) unSpoof() error {
var targets string
if err, v := p.Param("arp.spoof.targets").Get(p.Session); err != nil {
return err
} else {
targets = v.(string)
}
list, err := iprange.Parse(targets)
if err != nil {
return fmt.Errorf("Error while parsing arp.spoof.targets variable '%s': %s.", targets, err)
}
addresses := list.Expand()
from := p.Session.Gateway.IP
from_hw := p.Session.Gateway.HW
p.Session.Events.Log(session.INFO, "Restoring ARP cache of %d targets (%s).\n", len(addresses), targets)
p.sendArp(addresses, from, from_hw, false, false)
return nil
}
func (p *ArpSpoofer) Start() error {
if p.Running() == false {
var targets string
if err, v := p.Param("arp.spoof.targets").Get(p.Session); err != nil {
return err
} else {
targets = v.(string)
}
list, err := iprange.Parse(targets)
if err != nil {
return fmt.Errorf("Error while parsing arp.spoof.targets variable '%s': %s.", targets, err)
}
addresses := list.Expand()
p.SetRunning(true)
go func() {
from := p.Session.Gateway.IP
from_hw := p.Session.Interface.HW
p.Session.Events.Log(session.INFO, "ARP spoofer started, probing %d targets (%s).\n", len(addresses), targets)
for p.Running() {
p.sendArp(addresses, from, from_hw, true, false)
time.Sleep(1 * time.Second)
}
p.Done <- true
}()
return nil
} else {
return fmt.Errorf("ARP spoofer already started.")
}
}
func (p *ArpSpoofer) Stop() error {
if p.Running() == true {
p.SetRunning(false)
p.Session.Events.Log(session.INFO, "Waiting for ARP spoofer to stop ...")
<-p.Done
p.unSpoof()
return nil
} else {
return fmt.Errorf("ARP spoofer already stopped.")
}
}

View file

@ -1,98 +0,0 @@
package modules
import (
"fmt"
"github.com/evilsocket/bettercap-ng/core"
"github.com/evilsocket/bettercap-ng/session"
)
type EventsStream struct {
session.SessionModule
quit chan bool
}
func NewEventsStream(s *session.Session) *EventsStream {
stream := &EventsStream{
SessionModule: session.NewSessionModule("events.stream", s),
quit: make(chan bool),
}
stream.AddHandler(session.NewModuleHandler("events.stream on", "",
"Start events stream.",
func(args []string) error {
return stream.Start()
}))
stream.AddHandler(session.NewModuleHandler("events.stream off", "",
"Stop events stream.",
func(args []string) error {
return stream.Stop()
}))
stream.AddHandler(session.NewModuleHandler("events.clear", "",
"Clear events stream.",
func(args []string) error {
stream.Session.Events.Clear()
return nil
}))
return stream
}
func (s EventsStream) Name() string {
return "Events Stream"
}
func (s EventsStream) Description() string {
return "Print events as a continuous stream."
}
func (s EventsStream) Author() string {
return "Simone Margaritelli <evilsocket@protonmail.com>"
}
func (s *EventsStream) Start() error {
if s.Running() == false {
s.SetRunning(true)
go func() {
for {
var e session.Event
select {
case e = <-s.Session.Events.NewEvents:
tm := e.Time.Format("2006-01-02 15:04:05")
if e.Tag == "sys.log" {
fmt.Printf("[%s] %s %v\n", tm, e.Label(), e.Data.(session.LogMessage).Message)
} else {
fmt.Printf("[%s] [%s] %v\n", tm, core.Green(e.Tag), e.Data)
}
break
case <-s.quit:
return
}
}
}()
return nil
}
return fmt.Errorf("Events stream already started.")
}
func (s *EventsStream) Stop() error {
if s.Running() == true {
s.SetRunning(false)
s.quit <- true
return nil
}
return fmt.Errorf("Events stream already stopped.")
}
func (s *EventsStream) OnSessionEnded(sess *session.Session) {
if s.Running() {
s.Stop()
}
}

View file

@ -1,245 +0,0 @@
package modules
import (
"fmt"
"net/http"
"strings"
"github.com/elazarl/goproxy"
"github.com/evilsocket/bettercap-ng/firewall"
"github.com/evilsocket/bettercap-ng/session"
)
type HttpProxy struct {
session.SessionModule
address string
redirection *firewall.Redirection
server http.Server
proxy *goproxy.ProxyHttpServer
script *ProxyScript
}
func (p *HttpProxy) logAction(req *http.Request, jsres *JSResponse) {
p.Session.Events.Add("http.proxy.spoofed-response", struct {
To string
Method string
Host string
Path string
Size int
}{
strings.Split(req.RemoteAddr, ":")[0],
req.Method,
req.Host,
req.URL.Path,
len(jsres.Body),
})
}
func NewHttpProxy(s *session.Session) *HttpProxy {
p := &HttpProxy{
SessionModule: session.NewSessionModule("http.proxy", s),
proxy: nil,
address: "",
redirection: nil,
script: nil,
}
p.AddParam(session.NewIntParameter("http.port",
"80",
"HTTP port to redirect when the proxy is activated."))
p.AddParam(session.NewStringParameter("http.proxy.address",
"<interface address>",
`^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`,
"Address to bind the HTTP proxy to."))
p.AddParam(session.NewIntParameter("http.proxy.port",
"8080",
"Port to bind the HTTP proxy to."))
p.AddParam(session.NewStringParameter("http.proxy.script",
"",
"",
"Path of a proxy JS script."))
p.AddHandler(session.NewModuleHandler("http.proxy on", "",
"Start HTTP proxy.",
func(args []string) error {
return p.Start()
}))
p.AddHandler(session.NewModuleHandler("http.proxy off", "",
"Stop HTTP proxy.",
func(args []string) error {
return p.Stop()
}))
proxy := goproxy.NewProxyHttpServer()
proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if p.doProxy(req) == true {
req.URL.Scheme = "http"
req.URL.Host = req.Host
p.proxy.ServeHTTP(w, req)
}
})
proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
if p.script != nil {
jsres := p.script.OnRequest(req)
if jsres != nil {
p.logAction(req, jsres)
return req, jsres.ToResponse(req)
}
}
return req, nil
})
proxy.OnResponse().DoFunc(func(res *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
if p.script != nil {
jsres := p.script.OnResponse(res)
if jsres != nil {
p.logAction(res.Request, jsres)
return jsres.ToResponse(res.Request)
}
}
return res
})
p.proxy = proxy
return p
}
func (p *HttpProxy) Name() string {
return "HTTP Proxy"
}
func (p *HttpProxy) Description() string {
return "A full featured HTTP proxy that can be used to inject malicious contents into webpages, all HTTP traffic will be redirected to it."
}
func (p *HttpProxy) Author() string {
return "Simone Margaritelli <evilsocket@protonmail.com>"
}
func (p *HttpProxy) OnSessionStarted(s *session.Session) {
// refresh the address after session has been created
s.Env.Set("http.proxy.address", s.Interface.IpAddress)
}
func (p *HttpProxy) OnSessionEnded(s *session.Session) {
if p.Running() {
p.Stop()
}
}
func (p *HttpProxy) Start() error {
var http_port int
var proxy_port int
if p.Running() == true {
return fmt.Errorf("HTTP proxy already started.")
}
if err, v := p.Param("http.proxy.address").Get(p.Session); err != nil {
return err
} else {
p.address = v.(string)
}
if err, v := p.Param("http.proxy.port").Get(p.Session); err != nil {
return err
} else {
proxy_port = v.(int)
}
if err, v := p.Param("http.port").Get(p.Session); err != nil {
return err
} else {
http_port = v.(int)
}
if err, v := p.Param("http.proxy.script").Get(p.Session); err != nil {
return err
} else {
scriptPath := v.(string)
if scriptPath != "" {
if err, p.script = LoadProxyScript(scriptPath, p.Session); err != nil {
return err
} else {
p.Session.Events.Log(session.DEBUG, "Proxy script %s loaded.", scriptPath)
}
}
}
if p.Session.Firewall.IsForwardingEnabled() == false {
p.Session.Events.Log(session.INFO, "Enabling forwarding.")
p.Session.Firewall.EnableForwarding(true)
}
p.redirection = firewall.NewRedirection(p.Session.Interface.Name(),
"TCP",
http_port,
p.address,
proxy_port)
if err := p.Session.Firewall.EnableRedirection(p.redirection, true); err != nil {
return err
}
p.Session.Events.Log(session.DEBUG, "Applied redirection %s", p.redirection.String())
address := fmt.Sprintf("%s:%d", p.address, proxy_port)
p.server = http.Server{Addr: address, Handler: p.proxy}
go func() {
p.SetRunning(true)
if err := p.server.ListenAndServe(); err != nil {
p.SetRunning(false)
p.Session.Events.Log(session.WARNING, "%s", err)
}
}()
return nil
}
func (p *HttpProxy) Stop() error {
if p.Running() == true {
p.SetRunning(false)
p.server.Shutdown(nil)
if p.redirection != nil {
p.Session.Events.Log(session.DEBUG, "Disabling redirection %s", p.redirection.String())
if err := p.Session.Firewall.EnableRedirection(p.redirection, false); err != nil {
return err
}
p.redirection = nil
}
return nil
} else {
return fmt.Errorf("HTTP proxy stopped.")
}
}
func (p *HttpProxy) doProxy(req *http.Request) bool {
blacklist := []string{
"localhost",
"127.0.0.1",
p.address,
}
if req.Host == "" {
p.Session.Events.Log(session.ERROR, "Got request with empty host: %v", req)
return false
}
for _, blacklisted := range blacklist {
if strings.HasPrefix(req.Host, blacklisted) {
p.Session.Events.Log(session.ERROR, "Got request with blacklisted host: %s", req.Host)
return false
}
}
return true
}

View file

@ -1,51 +0,0 @@
package modules
import (
"fmt"
"io/ioutil"
"net/http"
)
type JSHeader struct {
Name string
Value string
}
type JSRequest struct {
Method string
Version string
Path string
Hostname string
Headers []JSHeader
Body string
req *http.Request
}
func NewJSRequest(req *http.Request) JSRequest {
headers := make([]JSHeader, 0)
for key, values := range req.Header {
for _, value := range values {
headers = append(headers, JSHeader{key, value})
}
}
return JSRequest{
Method: req.Method,
Version: fmt.Sprintf("%d.%d", req.ProtoMajor, req.ProtoMinor),
Path: req.URL.Path,
Hostname: req.Host,
Headers: headers,
req: req,
}
}
func (j *JSRequest) ReadBody() string {
raw, err := ioutil.ReadAll(j.req.Body)
if err != nil {
return ""
}
j.Body = string(raw)
return j.Body
}

View file

@ -1,74 +0,0 @@
package modules
import (
"io/ioutil"
"net/http"
"strings"
"github.com/elazarl/goproxy"
)
type JSResponse struct {
Status int
ContentType string
Headers string
Body string
wasUpdated bool
resp *http.Response
}
func NewJSResponse(res *http.Response) *JSResponse {
cType := ""
headers := ""
for name, values := range res.Header {
for _, value := range values {
if name == "Content-Type" {
cType = value
}
headers += name + ": " + value + "\r\n"
}
}
return &JSResponse{
Status: res.StatusCode,
ContentType: cType,
Headers: headers,
resp: res,
}
}
func (j *JSResponse) Updated() {
j.wasUpdated = true
}
func (j *JSResponse) ToResponse(req *http.Request) (resp *http.Response) {
resp = goproxy.NewResponse(req, j.ContentType, j.Status, j.Body)
if j.Headers != "" {
for _, header := range strings.Split(j.Headers, "\n") {
header = strings.Trim(header, "\n\r\t ")
if header == "" {
continue
}
parts := strings.SplitN(header, ":", 2)
if len(parts) == 2 {
resp.Header.Add(parts[0], parts[1])
}
}
}
return
}
func (j *JSResponse) ReadBody() string {
defer j.resp.Body.Close()
raw, err := ioutil.ReadAll(j.resp.Body)
if err != nil {
return ""
}
j.Body = string(raw)
return j.Body
}

View file

@ -1,192 +0,0 @@
package modules
import (
"io/ioutil"
"net/http"
"sync"
"github.com/evilsocket/bettercap-ng/session"
"github.com/robertkrimen/otto"
)
type ProxyScript struct {
Path string
Source string
VM *otto.Otto
sess *session.Session
gil *sync.Mutex
onRequestScript *otto.Script
onResponseScript *otto.Script
cbCacheLock *sync.Mutex
cbCache map[string]bool
}
func LoadProxyScript(path string, sess *session.Session) (err error, s *ProxyScript) {
sess.Events.Log(session.INFO, "Loading proxy script %s ...", path)
raw, err := ioutil.ReadFile(path)
if err != nil {
return
}
s = &ProxyScript{
Path: path,
Source: string(raw),
VM: otto.New(),
sess: sess,
gil: &sync.Mutex{},
onRequestScript: nil,
onResponseScript: nil,
cbCacheLock: &sync.Mutex{},
cbCache: make(map[string]bool),
}
// this will define callbacks and global objects
_, err = s.VM.Run(s.Source)
if err != nil {
return
}
// define session pointer
err = s.VM.Set("env", sess.Env.Storage)
if err != nil {
sess.Events.Log(session.ERROR, "Error while defining environment: %s", err)
return
}
// run onLoad if defined
if s.hasCallback("onLoad") {
_, err = s.VM.Run("onLoad()")
if err != nil {
sess.Events.Log(session.ERROR, "Error while executing onLoad callback: %s", err)
return
}
}
// compile call to onRequest if defined
if s.hasCallback("onRequest") {
s.onRequestScript, err = s.VM.Compile("", "onRequest(req, res)")
if err != nil {
sess.Events.Log(session.ERROR, "Error while compiling onRequest callback: %s", err)
return
}
}
// compile call to onResponse if defined
if s.hasCallback("onResponse") {
s.onResponseScript, err = s.VM.Compile("", "onResponse(req, res)")
if err != nil {
sess.Events.Log(session.ERROR, "Error while compiling onResponse callback: %s", err)
return
}
}
return
}
func (s *ProxyScript) hasCallback(name string) bool {
s.cbCacheLock.Lock()
defer s.cbCacheLock.Unlock()
// check the cache
has, found := s.cbCache[name]
if found == false {
// check the VM
cb, err := s.VM.Get(name)
if err == nil && cb.IsFunction() {
has = true
} else {
has = false
}
s.cbCache[name] = has
}
return has
}
func (s *ProxyScript) doRequestDefines(req *http.Request) (err error, jsres *JSResponse) {
// convert request and define empty response to be optionally filled
jsreq := NewJSRequest(req)
if err = s.VM.Set("req", &jsreq); err != nil {
s.sess.Events.Log(session.ERROR, "Error while defining request: %s", err)
return
}
jsres = &JSResponse{}
if err = s.VM.Set("res", jsres); err != nil {
s.sess.Events.Log(session.ERROR, "Error while defining response: %s", err)
return
}
return
}
func (s *ProxyScript) doResponseDefines(res *http.Response) (err error, jsres *JSResponse) {
// convert both request and response
jsreq := NewJSRequest(res.Request)
if err = s.VM.Set("req", jsreq); err != nil {
s.sess.Events.Log(session.ERROR, "Error while defining request: %s", err)
return
}
jsres = NewJSResponse(res)
if err = s.VM.Set("res", jsres); err != nil {
s.sess.Events.Log(session.ERROR, "Error while defining response: %s", err)
return
}
return
}
func (s *ProxyScript) OnRequest(req *http.Request) *JSResponse {
if s.onRequestScript != nil {
s.gil.Lock()
defer s.gil.Unlock()
err, jsres := s.doRequestDefines(req)
if err != nil {
s.sess.Events.Log(session.ERROR, "Error while running bootstrap definitions: %s", err)
return nil
}
_, err = s.VM.Run(s.onRequestScript)
if err != nil {
s.sess.Events.Log(session.ERROR, "Error while executing onRequest callback: %s", err)
return nil
}
if jsres.wasUpdated == true {
return jsres
}
}
return nil
}
func (s *ProxyScript) OnResponse(res *http.Response) *JSResponse {
if s.onResponseScript != nil {
s.gil.Lock()
defer s.gil.Unlock()
err, jsres := s.doResponseDefines(res)
if err != nil {
s.sess.Events.Log(session.ERROR, "Error while running bootstrap definitions: %s", err)
return nil
}
_, err = s.VM.Run(s.onResponseScript)
if err != nil {
s.sess.Events.Log(session.ERROR, "Error while executing onRequest callback: %s", err)
return nil
}
if jsres.wasUpdated == true {
return jsres
}
}
return nil
}

View file

@ -1,139 +0,0 @@
package modules
import (
"fmt"
"net"
"time"
"github.com/evilsocket/bettercap-ng/session"
"github.com/malfunkt/iprange"
)
type Prober struct {
session.SessionModule
}
func NewProber(s *session.Session) *Prober {
p := &Prober{
SessionModule: session.NewSessionModule("net.probe", s),
}
p.AddParam(session.NewIntParameter("net.probe.throttle",
"10",
"If greater than 0, probe packets will be throttled by this value in milliseconds."))
p.AddHandler(session.NewModuleHandler("net.probe on", "",
"Start network hosts probing in background.",
func(args []string) error {
return p.Start()
}))
p.AddHandler(session.NewModuleHandler("net.probe off", "",
"Stop network hosts probing in background.",
func(args []string) error {
return p.Stop()
}))
return p
}
func (p Prober) Name() string {
return "Network Prober"
}
func (p Prober) Description() string {
return "Keep probing for new hosts on the network by sending dummy UDP packets to every possible IP on the subnet."
}
func (p Prober) Author() string {
return "Simone Margaritelli <evilsocket@protonmail.com>"
}
func (p *Prober) shouldProbe(ip net.IP) bool {
addr := ip.String()
if ip.IsLoopback() == true {
return false
} else if addr == p.Session.Interface.IpAddress {
return false
} else if addr == p.Session.Gateway.IpAddress {
return false
} else if p.Session.Targets.Has(addr) == true {
return false
}
return true
}
func (p Prober) OnSessionEnded(s *session.Session) {
if p.Running() {
p.Stop()
}
}
func (p *Prober) sendProbe(from net.IP, from_hw net.HardwareAddr, ip net.IP) {
name := fmt.Sprintf("%s:137", ip)
if addr, err := net.ResolveUDPAddr("udp", name); err != nil {
p.Session.Events.Log(session.ERROR, "Could not resolve %s.", name)
} else if con, err := net.DialUDP("udp", nil, addr); err != nil {
p.Session.Events.Log(session.ERROR, "Could not dial %s.", name)
} else {
// p.Session.Events.Log(session.DEBUG,"UDP connection to %s enstablished.\n", name)
defer con.Close()
con.Write([]byte{0xde, 0xad, 0xbe, 0xef})
}
}
func (p *Prober) Start() error {
if p.Running() == false {
throttle := int(0)
if err, v := p.Param("net.probe.throttle").Get(p.Session); err != nil {
return err
} else {
throttle = v.(int)
p.Session.Events.Log(session.DEBUG, "Throttling packets of %d ms.", throttle)
}
p.SetRunning(true)
go func() {
list, err := iprange.Parse(p.Session.Interface.CIDR())
if err != nil {
p.Session.Events.Log(session.FATAL, "%s", err)
}
from := p.Session.Interface.IP
from_hw := p.Session.Interface.HW
addresses := list.Expand()
for p.Running() {
for _, ip := range addresses {
if p.shouldProbe(ip) == false {
p.Session.Events.Log(session.DEBUG, "Skipping address %s from UDP probing.", ip)
continue
}
p.sendProbe(from, from_hw, ip)
if throttle > 0 {
time.Sleep(time.Duration(throttle) * time.Millisecond)
}
}
time.Sleep(5 * time.Second)
}
}()
return nil
} else {
return fmt.Errorf("Network prober already started.")
}
}
func (p *Prober) Stop() error {
if p.Running() == true {
p.SetRunning(false)
return nil
} else {
return fmt.Errorf("Network prober already stopped.")
}
}

View file

@ -1,160 +0,0 @@
package modules
import (
"fmt"
"github.com/evilsocket/bettercap-ng/net"
"github.com/evilsocket/bettercap-ng/session"
"time"
)
type Discovery struct {
session.SessionModule
refresh int
before net.ArpTable
current net.ArpTable
quit chan bool
}
func NewDiscovery(s *session.Session) *Discovery {
d := &Discovery{
SessionModule: session.NewSessionModule("net.recon", s),
refresh: 1,
before: nil,
current: nil,
quit: make(chan bool),
}
d.AddHandler(session.NewModuleHandler("net.recon on", "",
"Start network hosts discovery.",
func(args []string) error {
return d.Start()
}))
d.AddHandler(session.NewModuleHandler("net.recon off", "",
"Stop network hosts discovery.",
func(args []string) error {
return d.Stop()
}))
d.AddHandler(session.NewModuleHandler("net.show", "",
"Show current hosts list.",
func(args []string) error {
return d.Show()
}))
return d
}
func (d Discovery) Name() string {
return "Network Recon"
}
func (d Discovery) Description() string {
return "Read periodically the ARP cache in order to monitor for new hosts on the network."
}
func (d Discovery) Author() string {
return "Simone Margaritelli <evilsocket@protonmail.com>"
}
func (d Discovery) OnSessionEnded(s *session.Session) {
if d.Running() {
d.Stop()
}
}
func (d *Discovery) checkShared(new net.ArpTable) {
n_gw_shared := 0
for ip, mac := range new {
if ip != d.Session.Gateway.IpAddress && mac == d.Session.Gateway.HwAddress {
n_gw_shared++
}
}
if n_gw_shared > 0 {
a := ""
b := ""
if n_gw_shared == 1 {
a = ""
b = "s"
} else {
a = "s"
b = ""
}
d.Session.Events.Log(session.WARNING, "Found %d endpoint%s which share%s the same MAC of the gateway (%s), there're might be some IP isolation going on, skipping.", n_gw_shared, a, b, d.Session.Gateway.HwAddress)
}
}
func (d *Discovery) runDiff() {
var new net.ArpTable = make(net.ArpTable)
var rem net.ArpTable = make(net.ArpTable)
if d.before != nil {
new = net.ArpDiff(d.current, d.before)
rem = net.ArpDiff(d.before, d.current)
} else {
new = d.current
}
if len(new) > 0 || len(rem) > 0 {
d.checkShared(new)
// refresh target pool
for ip, mac := range rem {
d.Session.Targets.Remove(ip, mac)
}
for ip, mac := range new {
d.Session.Targets.AddIfNotExist(ip, mac)
}
}
}
func (d *Discovery) Start() error {
if d.Running() == false {
d.SetRunning(true)
go func() {
for {
select {
case <-time.After(time.Duration(d.refresh) * time.Second):
var err error
if d.current, err = net.ArpUpdate(d.Session.Interface.Name()); err != nil {
d.Session.Events.Log(session.ERROR, "%s", err)
continue
}
d.runDiff()
d.before = d.current
case <-d.quit:
return
}
}
}()
return nil
} else {
return fmt.Errorf("Network discovery already started.")
}
}
func (d *Discovery) Show() error {
d.Session.Targets.Dump()
return nil
}
func (d *Discovery) Stop() error {
if d.Running() == true {
d.SetRunning(false)
d.quit <- true
return nil
} else {
return fmt.Errorf("Network discovery already stopped.")
}
}

View file

@ -1,363 +0,0 @@
package modules
import (
"fmt"
"github.com/evilsocket/bettercap-ng/core"
"github.com/evilsocket/bettercap-ng/session"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"github.com/google/gopacket/pcapgo"
"net"
"os"
"regexp"
"time"
)
type SnifferContext struct {
Handle *pcap.Handle
DumpLocal bool
Verbose bool
Filter string
Expression string
Compiled *regexp.Regexp
Output string
OutputFile *os.File
OutputWriter *pcapgo.Writer
}
func NewSnifferContext() *SnifferContext {
return &SnifferContext{
Handle: nil,
DumpLocal: false,
Verbose: true,
Filter: "",
Expression: "",
Compiled: nil,
Output: "",
OutputFile: nil,
OutputWriter: nil,
}
}
var (
no = core.Red("no")
yes = core.Green("yes")
)
func (c *SnifferContext) Log(sess *session.Session) {
if c.DumpLocal {
sess.Events.Log(session.INFO, "Skip local packets : %s", no)
} else {
sess.Events.Log(session.INFO, "Skip local packets : %s", yes)
}
if c.Verbose {
sess.Events.Log(session.INFO, "Verbose : %s", yes)
} else {
sess.Events.Log(session.INFO, "Verbose : %s", no)
}
if c.Filter != "" {
sess.Events.Log(session.INFO, "BPF Filter : '%s'", core.Yellow(c.Filter))
}
if c.Expression != "" {
sess.Events.Log(session.INFO, "Regular expression : '%s'", core.Yellow(c.Expression))
}
if c.Output != "" {
sess.Events.Log(session.INFO, "File output : '%s'", core.Yellow(c.Output))
}
}
func (c *SnifferContext) Close() {
if c.Handle != nil {
c.Handle.Close()
c.Handle = nil
}
if c.OutputFile != nil {
c.OutputFile.Close()
c.OutputFile = nil
}
}
type SnifferStats struct {
NumLocal uint64
NumMatched uint64
NumDumped uint64
NumWrote uint64
Started time.Time
FirstPacket time.Time
LastPacket time.Time
}
func NewSnifferStats() *SnifferStats {
return &SnifferStats{
NumLocal: 0,
NumMatched: 0,
NumDumped: 0,
NumWrote: 0,
Started: time.Now(),
FirstPacket: time.Time{},
LastPacket: time.Time{},
}
}
type Sniffer struct {
session.SessionModule
Stats *SnifferStats
Ctx *SnifferContext
}
func NewSniffer(s *session.Session) *Sniffer {
sniff := &Sniffer{
SessionModule: session.NewSessionModule("net.sniffer", s),
Stats: nil,
}
sniff.AddParam(session.NewBoolParameter("net.sniffer.verbose",
"true",
"Print captured packets to screen."))
sniff.AddParam(session.NewBoolParameter("net.sniffer.local",
"false",
"If true it will consider packets from/to this computer, otherwise it will skip them."))
sniff.AddParam(session.NewStringParameter("net.sniffer.filter",
"not arp",
"",
"BPF filter for the sniffer."))
sniff.AddParam(session.NewStringParameter("net.sniffer.regexp",
"",
"",
"If filled, only packets matching this regular expression will be considered."))
sniff.AddParam(session.NewStringParameter("net.sniffer.output",
"",
"",
"If set, the sniffer will write captured packets to this file."))
sniff.AddHandler(session.NewModuleHandler("net.sniffer stats", "",
"Print sniffer session configuration and statistics.",
func(args []string) error {
return sniff.PrintStats()
}))
sniff.AddHandler(session.NewModuleHandler("net.sniffer on", "",
"Start network sniffer in background.",
func(args []string) error {
return sniff.Start()
}))
sniff.AddHandler(session.NewModuleHandler("net.sniffer off", "",
"Stop network sniffer in background.",
func(args []string) error {
return sniff.Stop()
}))
return sniff
}
func (s Sniffer) Name() string {
return "Network Sniffer"
}
func (s Sniffer) Description() string {
return "Sniff packets from the network."
}
func (s Sniffer) Author() string {
return "Simone Margaritelli <evilsocket@protonmail.com>"
}
func (sn Sniffer) OnSessionEnded(s *session.Session) {
if sn.Running() {
sn.Stop()
}
}
func same(a, b net.HardwareAddr) bool {
if len(a) != len(b) {
return false
}
for idx, v := range a {
if b[idx] != v {
return false
}
}
return true
}
func (s Sniffer) isLocalPacket(packet gopacket.Packet) bool {
local_hw := s.Session.Interface.HW
eth := packet.Layer(layers.LayerTypeEthernet)
if eth != nil {
eth_packet, _ := eth.(*layers.Ethernet)
if same(eth_packet.SrcMAC, local_hw) || same(eth_packet.DstMAC, local_hw) {
return true
}
}
return false
}
func (s *Sniffer) GetContext() (error, *SnifferContext) {
var err error
ctx := NewSnifferContext()
if ctx.Handle, err = pcap.OpenLive(s.Session.Interface.Name(), 65536, true, pcap.BlockForever); err != nil {
return err, ctx
}
if err, v := s.Param("net.sniffer.verbose").Get(s.Session); err != nil {
return err, ctx
} else {
ctx.Verbose = v.(bool)
}
if err, v := s.Param("net.sniffer.local").Get(s.Session); err != nil {
return err, ctx
} else {
ctx.DumpLocal = v.(bool)
}
if err, v := s.Param("net.sniffer.filter").Get(s.Session); err != nil {
return err, ctx
} else {
if ctx.Filter = v.(string); ctx.Filter != "" {
err = ctx.Handle.SetBPFFilter(ctx.Filter)
if err != nil {
return err, ctx
}
}
}
if err, v := s.Param("net.sniffer.regexp").Get(s.Session); err != nil {
return err, ctx
} else {
if ctx.Expression = v.(string); ctx.Expression != "" {
if ctx.Compiled, err = regexp.Compile(ctx.Expression); err != nil {
return err, ctx
}
}
}
if err, v := s.Param("net.sniffer.output").Get(s.Session); err != nil {
return err, ctx
} else {
if ctx.Output = v.(string); ctx.Output != "" {
if ctx.OutputFile, err = os.Create(ctx.Output); err != nil {
return err, ctx
}
ctx.OutputWriter = pcapgo.NewWriter(ctx.OutputFile)
ctx.OutputWriter.WriteFileHeader(65536, layers.LinkTypeEthernet)
}
}
return nil, ctx
}
func (s *Sniffer) PrintStats() error {
if s.Stats == nil {
return fmt.Errorf("No stats yet.")
}
s.Ctx.Log(s.Session)
first := "never"
last := "never"
if s.Stats.FirstPacket.IsZero() == false {
first = s.Stats.FirstPacket.String()
}
if s.Stats.LastPacket.IsZero() == false {
last = s.Stats.LastPacket.String()
}
s.Session.Events.Log(session.INFO, "Sniffer Started : %s", s.Stats.Started)
s.Session.Events.Log(session.INFO, "First Packet Seen : %s", first)
s.Session.Events.Log(session.INFO, "Last Packet Seen : %s", last)
s.Session.Events.Log(session.INFO, "Local Packets : %d", s.Stats.NumLocal)
s.Session.Events.Log(session.INFO, "Matched Packets : %d", s.Stats.NumMatched)
s.Session.Events.Log(session.INFO, "Dumped Packets : %d", s.Stats.NumDumped)
s.Session.Events.Log(session.INFO, "Wrote Packets : %d", s.Stats.NumWrote)
return nil
}
func (s *Sniffer) Start() error {
if s.Running() == false {
var err error
if err, s.Ctx = s.GetContext(); err != nil {
if s.Ctx != nil {
s.Ctx.Close()
s.Ctx = nil
}
return err
}
s.Stats = NewSnifferStats()
s.SetRunning(true)
go func() {
defer s.Ctx.Close()
src := gopacket.NewPacketSource(s.Ctx.Handle, s.Ctx.Handle.LinkType())
for packet := range src.Packets() {
if s.Running() == false {
break
}
now := time.Now()
if s.Stats.FirstPacket.IsZero() {
s.Stats.FirstPacket = now
}
s.Stats.LastPacket = now
is_local := false
if s.isLocalPacket(packet) {
is_local = true
s.Stats.NumLocal++
}
if s.Ctx.DumpLocal == true || is_local == false {
data := packet.Data()
if s.Ctx.Compiled == nil || s.Ctx.Compiled.Match(data) == true {
s.Stats.NumMatched++
if s.Ctx.Verbose {
fmt.Println(packet.Dump())
s.Stats.NumDumped++
}
if s.Ctx.OutputWriter != nil {
s.Ctx.OutputWriter.WritePacket(packet.Metadata().CaptureInfo, data)
s.Stats.NumWrote++
}
}
}
}
}()
return nil
} else {
return fmt.Errorf("Network sniffer already started.")
}
}
func (s *Sniffer) Stop() error {
if s.Running() == true {
s.SetRunning(false)
return nil
} else {
return fmt.Errorf("Network sniffer already stopped.")
}
}