mirror of
https://github.com/bettercap/bettercap
synced 2025-07-08 05:51:37 -07:00
new: implemented tcp.proxy (closes #33)
This commit is contained in:
parent
0f8be49beb
commit
129f87f8f9
7 changed files with 410 additions and 78 deletions
1
main.go
1
main.go
|
@ -47,6 +47,7 @@ func main() {
|
|||
sess.Register(modules.NewHttpServer(sess))
|
||||
sess.Register(modules.NewHttpProxy(sess))
|
||||
sess.Register(modules.NewHttpsProxy(sess))
|
||||
sess.Register(modules.NewTcpProxy(sess))
|
||||
sess.Register(modules.NewRestAPI(sess))
|
||||
sess.Register(modules.NewWOL(sess))
|
||||
sess.Register(modules.NewWiFiRecon(sess))
|
||||
|
|
|
@ -2,8 +2,10 @@ package modules
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
|
||||
"github.com/bettercap/bettercap/log"
|
||||
"github.com/bettercap/bettercap/session"
|
||||
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
@ -15,7 +17,68 @@ func errOtto(format string, args ...interface{}) otto.Value {
|
|||
return nullOtto
|
||||
}
|
||||
|
||||
// define functions available to proxy scripts
|
||||
type ProxyScript struct {
|
||||
sync.Mutex
|
||||
|
||||
Path string
|
||||
Source string
|
||||
VM *otto.Otto
|
||||
|
||||
sess *session.Session
|
||||
cbCacheLock *sync.Mutex
|
||||
cbCache map[string]bool
|
||||
}
|
||||
|
||||
func LoadProxyScriptSource(path, source string, sess *session.Session) (err error, s *ProxyScript) {
|
||||
s = &ProxyScript{
|
||||
Path: path,
|
||||
Source: source,
|
||||
VM: otto.New(),
|
||||
sess: sess,
|
||||
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.Data)
|
||||
if err != nil {
|
||||
log.Error("Error while defining environment: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.defineBuiltins()
|
||||
if err != nil {
|
||||
log.Error("Error while defining builtin functions: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// run onLoad if defined
|
||||
if s.hasCallback("onLoad") {
|
||||
_, err = s.VM.Run("onLoad()")
|
||||
if err != nil {
|
||||
log.Error("Error while executing onLoad callback: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func LoadProxyScript(path string, sess *session.Session) (err error, s *ProxyScript) {
|
||||
raw, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return LoadProxyScriptSource(path, string(raw), sess)
|
||||
}
|
||||
|
||||
func (s *ProxyScript) defineBuiltins() error {
|
||||
// used to read a file ... doh
|
||||
s.VM.Set("readFile", func(call otto.FunctionCall) otto.Value {
|
||||
|
@ -76,3 +139,23 @@ func (s *ProxyScript) defineBuiltins() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
|
@ -36,7 +36,7 @@ type HTTPProxy struct {
|
|||
Server *http.Server
|
||||
Redirection *firewall.Redirection
|
||||
Proxy *goproxy.ProxyHttpServer
|
||||
Script *ProxyScript
|
||||
Script *HttpProxyScript
|
||||
CertFile string
|
||||
KeyFile string
|
||||
|
||||
|
@ -147,7 +147,7 @@ func (p *HTTPProxy) Configure(address string, proxyPort int, httpPort int, scrip
|
|||
p.Address = address
|
||||
|
||||
if scriptPath != "" {
|
||||
if err, p.Script = LoadProxyScript(scriptPath, p.sess); err != nil {
|
||||
if err, p.Script = LoadHttpProxyScript(scriptPath, p.sess); err != nil {
|
||||
return err
|
||||
} else {
|
||||
log.Debug("Proxy script %s loaded.", scriptPath)
|
||||
|
|
|
@ -3,7 +3,6 @@ package modules
|
|||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/bettercap/bettercap/log"
|
||||
"github.com/bettercap/bettercap/session"
|
||||
|
@ -11,59 +10,22 @@ import (
|
|||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
||||
type ProxyScript struct {
|
||||
sync.Mutex
|
||||
|
||||
Path string
|
||||
Source string
|
||||
VM *otto.Otto
|
||||
|
||||
sess *session.Session
|
||||
type HttpProxyScript struct {
|
||||
*ProxyScript
|
||||
onRequestScript *otto.Script
|
||||
onResponseScript *otto.Script
|
||||
cbCacheLock *sync.Mutex
|
||||
cbCache map[string]bool
|
||||
}
|
||||
|
||||
func LoadProxyScriptSource(path, source string, sess *session.Session) (err error, s *ProxyScript) {
|
||||
s = &ProxyScript{
|
||||
Path: path,
|
||||
Source: source,
|
||||
VM: otto.New(),
|
||||
func LoadHttpProxyScriptSource(path, source string, sess *session.Session) (err error, s *HttpProxyScript) {
|
||||
err, ps := LoadProxyScriptSource(path, source, sess)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sess: sess,
|
||||
s = &HttpProxyScript{
|
||||
ProxyScript: ps,
|
||||
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.Data)
|
||||
if err != nil {
|
||||
log.Error("Error while defining environment: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.defineBuiltins()
|
||||
if err != nil {
|
||||
log.Error("Error while defining builtin functions: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// run onLoad if defined
|
||||
if s.hasCallback("onLoad") {
|
||||
_, err = s.VM.Run("onLoad()")
|
||||
if err != nil {
|
||||
log.Error("Error while executing onLoad callback: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// compile call to onRequest if defined
|
||||
|
@ -87,7 +49,7 @@ func LoadProxyScriptSource(path, source string, sess *session.Session) (err erro
|
|||
return
|
||||
}
|
||||
|
||||
func LoadProxyScript(path string, sess *session.Session) (err error, s *ProxyScript) {
|
||||
func LoadHttpProxyScript(path string, sess *session.Session) (err error, s *HttpProxyScript) {
|
||||
log.Info("Loading proxy script %s ...", path)
|
||||
|
||||
raw, err := ioutil.ReadFile(path)
|
||||
|
@ -95,30 +57,10 @@ func LoadProxyScript(path string, sess *session.Session) (err error, s *ProxyScr
|
|||
return
|
||||
}
|
||||
|
||||
return LoadProxyScriptSource(path, string(raw), sess)
|
||||
return LoadHttpProxyScriptSource(path, string(raw), sess)
|
||||
}
|
||||
|
||||
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) {
|
||||
func (s *HttpProxyScript) 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 {
|
||||
|
@ -134,7 +76,7 @@ func (s *ProxyScript) doRequestDefines(req *http.Request) (err error, jsres *JSR
|
|||
return
|
||||
}
|
||||
|
||||
func (s *ProxyScript) doResponseDefines(res *http.Response) (err error, jsres *JSResponse) {
|
||||
func (s *HttpProxyScript) 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 {
|
||||
|
@ -151,7 +93,7 @@ func (s *ProxyScript) doResponseDefines(res *http.Response) (err error, jsres *J
|
|||
return
|
||||
}
|
||||
|
||||
func (s *ProxyScript) OnRequest(req *http.Request) *JSResponse {
|
||||
func (s *HttpProxyScript) OnRequest(req *http.Request) *JSResponse {
|
||||
if s.onRequestScript != nil {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
@ -177,7 +119,7 @@ func (s *ProxyScript) OnRequest(req *http.Request) *JSResponse {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *ProxyScript) OnResponse(res *http.Response) *JSResponse {
|
||||
func (s *HttpProxyScript) OnResponse(res *http.Response) *JSResponse {
|
||||
if s.onResponseScript != nil {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
|
|
@ -8,11 +8,11 @@ import (
|
|||
"github.com/bettercap/bettercap/session"
|
||||
)
|
||||
|
||||
func getScript(src string) *ProxyScript {
|
||||
func getScript(src string) *HttpProxyScript {
|
||||
sess := session.Session{}
|
||||
sess.Env = session.NewEnvironment(&sess)
|
||||
|
||||
err, script := LoadProxyScriptSource("", src, &sess)
|
||||
err, script := LoadHttpProxyScriptSource("", src, &sess)
|
||||
if err != nil {
|
||||
log.Fatal("%s", err)
|
||||
}
|
||||
|
|
214
modules/tcp_proxy.go
Normal file
214
modules/tcp_proxy.go
Normal file
|
@ -0,0 +1,214 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/bettercap/bettercap/firewall"
|
||||
"github.com/bettercap/bettercap/log"
|
||||
"github.com/bettercap/bettercap/session"
|
||||
)
|
||||
|
||||
type TcpProxy struct {
|
||||
session.SessionModule
|
||||
Redirection *firewall.Redirection
|
||||
localAddr *net.TCPAddr
|
||||
remoteAddr *net.TCPAddr
|
||||
listener *net.TCPListener
|
||||
script *TcpProxyScript
|
||||
}
|
||||
|
||||
func NewTcpProxy(s *session.Session) *TcpProxy {
|
||||
p := &TcpProxy{
|
||||
SessionModule: session.NewSessionModule("tcp.proxy", s),
|
||||
}
|
||||
|
||||
p.AddParam(session.NewIntParameter("tcp.port",
|
||||
"443",
|
||||
"Remote port to redirect when the TCP proxy is activated."))
|
||||
|
||||
p.AddParam(session.NewStringParameter("tcp.address",
|
||||
"",
|
||||
session.IPv4Validator,
|
||||
"Remote address of the TCP proxy."))
|
||||
|
||||
p.AddParam(session.NewStringParameter("tcp.proxy.address",
|
||||
session.ParamIfaceAddress,
|
||||
session.IPv4Validator,
|
||||
"Address to bind the TCP proxy to."))
|
||||
|
||||
p.AddParam(session.NewIntParameter("tcp.proxy.port",
|
||||
"8443",
|
||||
"Port to bind the TCP proxy to."))
|
||||
|
||||
p.AddParam(session.NewStringParameter("tcp.proxy.script",
|
||||
"",
|
||||
"",
|
||||
"Path of a TCP proxy JS script."))
|
||||
|
||||
p.AddHandler(session.NewModuleHandler("tcp.proxy on", "",
|
||||
"Start HTTP proxy.",
|
||||
func(args []string) error {
|
||||
return p.Start()
|
||||
}))
|
||||
|
||||
p.AddHandler(session.NewModuleHandler("tcp.proxy off", "",
|
||||
"Stop HTTP proxy.",
|
||||
func(args []string) error {
|
||||
return p.Stop()
|
||||
}))
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *TcpProxy) Name() string {
|
||||
return "tcp.proxy"
|
||||
}
|
||||
|
||||
func (p *TcpProxy) Description() string {
|
||||
return "A full featured TCP proxy, all TCP traffic to a given remote address and port will be redirected to it."
|
||||
}
|
||||
|
||||
func (p *TcpProxy) Author() string {
|
||||
return "Simone Margaritelli <evilsocket@protonmail.com>"
|
||||
}
|
||||
|
||||
func (p *TcpProxy) Configure() error {
|
||||
var err error
|
||||
var port int
|
||||
var proxyPort int
|
||||
var address string
|
||||
var proxyAddress string
|
||||
var scriptPath string
|
||||
|
||||
if err, address = p.StringParam("tcp.address"); err != nil {
|
||||
return err
|
||||
} else if err, proxyAddress = p.StringParam("tcp.proxy.address"); err != nil {
|
||||
return err
|
||||
} else if err, proxyPort = p.IntParam("tcp.proxy.port"); err != nil {
|
||||
return err
|
||||
} else if err, port = p.IntParam("tcp.port"); err != nil {
|
||||
return err
|
||||
} else if err, scriptPath = p.StringParam("tcp.proxy.script"); err != nil {
|
||||
return err
|
||||
} else if p.localAddr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", proxyAddress, proxyPort)); err != nil {
|
||||
return err
|
||||
} else if p.remoteAddr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", address, port)); err != nil {
|
||||
return err
|
||||
} else if p.listener, err = net.ListenTCP("tcp", p.localAddr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if scriptPath != "" {
|
||||
if err, p.script = LoadTcpProxyScript(scriptPath, p.Session); err != nil {
|
||||
return err
|
||||
} else {
|
||||
log.Debug("TCP proxy script %s loaded.", scriptPath)
|
||||
}
|
||||
}
|
||||
|
||||
if p.Session.Firewall.IsForwardingEnabled() == false {
|
||||
log.Info("Enabling forwarding.")
|
||||
p.Session.Firewall.EnableForwarding(true)
|
||||
}
|
||||
|
||||
p.Redirection = firewall.NewRedirection(p.Session.Interface.Name(),
|
||||
"TCP",
|
||||
port,
|
||||
proxyAddress,
|
||||
proxyPort)
|
||||
|
||||
p.Redirection.SrcAddress = address
|
||||
|
||||
if err := p.Session.Firewall.EnableRedirection(p.Redirection, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("Applied redirection %s", p.Redirection.String())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TcpProxy) doPipe(from, to net.Addr, src, dst io.ReadWriter, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
buff := make([]byte, 0xffff)
|
||||
for {
|
||||
n, err := src.Read(buff)
|
||||
if err != nil {
|
||||
if err.Error() != "EOF" {
|
||||
log.Warning("Read failed: %s", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
b := buff[:n]
|
||||
|
||||
ret := p.script.OnData(from, to, b)
|
||||
if ret != nil {
|
||||
nret := len(ret)
|
||||
log.Info("Overriding %d bytes of data from %s to %s with %d bytes of new data.", n, from.String(), to.String(), nret)
|
||||
b = make([]byte, nret)
|
||||
copy(b, ret)
|
||||
}
|
||||
|
||||
n, err = dst.Write(b)
|
||||
if err != nil {
|
||||
log.Warning("Write failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("%s -> %s : %d bytes", from.String(), to.String(), n)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *TcpProxy) handleConnection(c *net.TCPConn) {
|
||||
defer c.Close()
|
||||
|
||||
log.Info("TCP proxy got a connection from %s", c.RemoteAddr().String())
|
||||
|
||||
remote, err := net.DialTCP("tcp", nil, p.remoteAddr)
|
||||
if err != nil {
|
||||
log.Warning("Error while connecting to remote %s: %s", p.remoteAddr.String(), err)
|
||||
return
|
||||
}
|
||||
defer remote.Close()
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
// start pipeing
|
||||
go p.doPipe(c.RemoteAddr(), p.remoteAddr, c, remote, &wg)
|
||||
go p.doPipe(p.remoteAddr, c.RemoteAddr(), remote, c, &wg)
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (p *TcpProxy) Start() error {
|
||||
if p.Running() == true {
|
||||
return session.ErrAlreadyStarted
|
||||
} else if err := p.Configure(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.SetRunning(true, func() {
|
||||
log.Info("TCP proxy started ( x -> %s -> %s )", p.localAddr, p.remoteAddr)
|
||||
|
||||
for p.Running() {
|
||||
conn, err := p.listener.AcceptTCP()
|
||||
if err != nil {
|
||||
log.Warning("Error while accepting TCP connection: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go p.handleConnection(conn)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (p *TcpProxy) Stop() error {
|
||||
return p.SetRunning(false, func() {
|
||||
p.listener.Close()
|
||||
})
|
||||
}
|
92
modules/tcp_proxy_script.go
Normal file
92
modules/tcp_proxy_script.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/bettercap/bettercap/log"
|
||||
"github.com/bettercap/bettercap/session"
|
||||
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
||||
type TcpProxyScript struct {
|
||||
*ProxyScript
|
||||
onDataScript *otto.Script
|
||||
}
|
||||
|
||||
func LoadTcpProxyScriptSource(path, source string, sess *session.Session) (err error, s *TcpProxyScript) {
|
||||
err, ps := LoadProxyScriptSource(path, source, sess)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s = &TcpProxyScript{
|
||||
ProxyScript: ps,
|
||||
onDataScript: nil,
|
||||
}
|
||||
|
||||
if s.hasCallback("onData") {
|
||||
s.onDataScript, err = s.VM.Compile("", "onData(from, to, data)")
|
||||
if err != nil {
|
||||
log.Error("Error while compiling onData callback: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func LoadTcpProxyScript(path string, sess *session.Session) (err error, s *TcpProxyScript) {
|
||||
log.Info("Loading TCP proxy script %s ...", path)
|
||||
|
||||
raw, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return LoadTcpProxyScriptSource(path, string(raw), sess)
|
||||
}
|
||||
|
||||
func (s *TcpProxyScript) doDefines(from, to net.Addr, data []byte) (err error) {
|
||||
addrFrom := strings.Split(from.String(), ":")[0]
|
||||
addrTo := strings.Split(to.String(), ":")[0]
|
||||
|
||||
if err = s.VM.Set("from", addrFrom); err != nil {
|
||||
log.Error("Error while defining from: %s", err)
|
||||
return
|
||||
} else if err = s.VM.Set("to", addrTo); err != nil {
|
||||
log.Error("Error while defining to: %s", err)
|
||||
return
|
||||
} else if err = s.VM.Set("data", string(data)); err != nil {
|
||||
log.Error("Error while defining data: %s", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *TcpProxyScript) OnData(from, to net.Addr, data []byte) []byte {
|
||||
if s.onDataScript != nil {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
err := s.doDefines(from, to, data)
|
||||
if err != nil {
|
||||
log.Error("Error while running bootstrap definitions: %s", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
ret, err := s.VM.Run(s.onDataScript)
|
||||
if err != nil {
|
||||
log.Error("Error while executing onData callback: %s", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if ret.IsUndefined() == false && ret.IsString() {
|
||||
return []byte(ret.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue