wip: rough edges of rdp proxy

This commit is contained in:
Alexandre Beaulieu 2019-04-15 08:44:41 -04:00
commit 64f16ce418
No known key found for this signature in database
GPG key ID: 8B02EA7AE3FC7081

View file

@ -1,68 +1,74 @@
package packet_proxy package packet_proxy
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
golog "log" golog "log"
"plugin" "plugin"
"strings" "strings"
"syscall" "syscall"
"github.com/bettercap/bettercap/core" "github.com/bettercap/bettercap/core"
"github.com/bettercap/bettercap/session" "github.com/bettercap/bettercap/session"
"github.com/chifflier/nfqueue-go/nfqueue" "github.com/chifflier/nfqueue-go/nfqueue"
"github.com/evilsocket/islazy/fs" "github.com/evilsocket/islazy/fs"
"github.com/evilsocket/islazy/tui" "github.com/evilsocket/islazy/tui"
) )
type RdpProxy struct { type RdpProxy struct {
session.SessionModule session.SessionModule
done chan bool done chan bool
chainName string queue *nfqueue.Queue
rule string queueNum int
queue *nfqueue.Queue queueCb nfqueue.Callback
queueNum int port int
queueCb nfqueue.Callback start_port int
pluginPath string targets net.IPv4[]
plugin *plugin.Plugin
} }
var mod *RdpProxy var mod *RdpProxy
func NewRdpProxy(s *session.Session) *RdpProxy { func NewRdpProxy(s *session.Session) *RdpProxy {
mod = &RdpProxy{ mod = &RdpProxy{
SessionModule: session.NewSessionModule("rdp.proxy", s), SessionModule: session.NewSessionModule("rdp.proxy", s),
done: make(chan bool), done: make(chan bool),
queue: nil, queue: nil,
queueCb: nil, queueCb: nil,
queueNum: 0, port: 0,
chainName: "OUTPUT", start_port: 40000,
}
mod.AddHandler(session.NewModuleHandler("rdp.proxy on", "", queueNum: 0,
"Start the RDP proxy.", chainName: "OUTPUT",
func(args []string) error { }
return mod.Start()
}))
mod.AddHandler(session.NewModuleHandler("rdp.proxy off", "", mod.AddHandler(session.NewModuleHandler("rdp.proxy on", "", "Start the RDP proxy.",
"Stop the RDP proxy.", func(args []string) error {
func(args []string) error { return mod.Start()
return mod.Stop() }))
}))
mod.AddParam(session.NewIntParameter("rdp.proxy.queue.num", mod.AddHandler(session.NewModuleHandler("rdp.proxy off", "", "Stop the RDP proxy.",
"0", func(args []string) error {
"NFQUEUE number to bind to.")) return mod.Stop()
}))
mod.AddParam(session.NewIntParameter("rdp.proxy.port", mod.AddParam(session.NewIntParameter("rdp.proxy.queue.num", "0", "NFQUEUE number to bind to."))
"3389", mod.AddParam(session.NewIntParameter("rdp.proxy.port", "3389", "RDP port to intercept."))
"RDP port to intercept."
))
// mod.AddParam(session.NewStringParameter("rdp.proxy.targets", /* NOTES
* - The RDP port
* - The target source IPs (This can actually be handled by ARP.Spoof)
* - The target destination IPs
* - Starting Port
* - Maximum Instances (future)
* - RDP Command (if not pyrdp-mitm)
*
* FUTURE WORK:
* - Centralized Instance of pyrdp
*/
// mod.AddParam(session.NewStringParameter("rdp.proxy.targets",
// session.ParamSubnet, // session.ParamSubnet,
// "", // "",
// "Comma separated list of IP addresses, also supports nmap style IP ranges.")) // "Comma separated list of IP addresses, also supports nmap style IP ranges."))
@ -70,150 +76,148 @@ func NewRdpProxy(s *session.Session) *RdpProxy {
// TODO: Should support comma separated subnets // TODO: Should support comma separated subnets
// mod.AddParam(session.NewStringParameter("rdp.proxy.targets", "3389", session.IPv4RangeValidator "RDP port to intercept.")) // mod.AddParam(session.NewStringParameter("rdp.proxy.targets", "3389", session.IPv4RangeValidator "RDP port to intercept."))
mod.AddParam(session.NewIntParameter("rdp.proxy.start", mod.AddParam(session.NewIntParameter("rdp.proxy.start", "40000", "", "Starting port for pyrdp sessionss"))
"40000", mod.AddParam(session.NewStringParameter("rdp.proxy.command", "pyrdp-mitm", "The PyRDP base command to launch the man-in-the-middle."))
"", mod.AddParam(session.NewStringParameter("rdp.proxy.targets", "<All Subnets>", "A comma delimited list of destination IPs or CIDRs to target."))
"Starting port for pyrdp sessionss"))
mod.AddParam(session.NewStringParameter("rdp.proxy.command", return mod
"pyrdp-mitm",
"The PyRDP base command to launch the man-in-the-middle."))
return mod
} }
func (mod RdpProxy) Name() string { func (mod RdpProxy) Name() string {
return "rdp.proxy" return "rdp.proxy"
} }
func (mod RdpProxy) Description() string { func (mod RdpProxy) Description() string {
return "A Linux only module that relies on NFQUEUEs in order to man-in-the-middle RDP sessions." return "A Linux only module that relies on NFQUEUEs in order to man-in-the-middle RDP sessions."
} }
func (mod RdpProxy) Author() string { func (mod RdpProxy) Author() string {
return "Alexandre Beaulieu <alex@segfault.me>" return "Alexandre Beaulieu <alex@segfault.me>"
} }
func (mod *RdpProxy) destroyQueue() { func (mod *RdpProxy) destroyQueue() {
if mod.queue == nil { if mod.queue == nil {
return return
} }
mod.queue.DestroyQueue() mod.queue.DestroyQueue()
mod.queue.Close() mod.queue.Close()
mod.queue = nil mod.queue = nil
} }
func (mod *RdpProxy) runRule(enable bool) (err error) { func (mod *RdpProxy) runRule(enable bool) (err error) {
action := "-A" action := "-A"
if !enable { if !enable {
action = "-D" action = "-D"
} }
args := []string{ args := []string{
action, mod.chainName, action, mod.chainName,
} }
if mod.rule != "" { if mod.rule != "" {
rule := strings.Split(mod.rule, " ") rule := strings.Split(mod.rule, " ")
args = append(args, rule...) args = append(args, rule...)
} }
args = append(args, []string{ args = append(args, []string{
"-j", "NFQUEUE", "-j", "NFQUEUE",
"--queue-num", fmt.Sprintf("%d", mod.queueNum), "--queue-num", fmt.Sprintf("%d", mod.queueNum),
"--queue-bypass", "--queue-bypass",
}...) }...)
mod.Debug("iptables %s", args) mod.Debug("iptables %s", args)
_, err = core.Exec("iptables", args) _, err = core.Exec("iptables", args)
return return
} }
func (mod *RdpProxy) Configure() (err error) { func (mod *RdpProxy) Configure() (err error) {
golog.SetOutput(ioutil.Discard) golog.SetOutput(ioutil.Discard)
mod.destroyQueue() mod.destroyQueue()
if err, mod.queueNum = mod.IntParam("packet.proxy.queue.num"); err != nil { if err, mod.queueNum = mod.IntParam("packet.proxy.queue.num"); err != nil {
return return
} else if err, mod.chainName = mod.StringParam("packet.proxy.chain"); err != nil { } else if err, mod.chainName = mod.StringParam("packet.proxy.chain"); err != nil {
return return
} else if err, mod.rule = mod.StringParam("packet.proxy.rule"); err != nil { } else if err, mod.rule = mod.StringParam("packet.proxy.rule"); err != nil {
return return
} else if err, mod.pluginPath = mod.StringParam("packet.proxy.plugin"); err != nil { } else if err, mod.pluginPath = mod.StringParam("packet.proxy.plugin"); err != nil {
return return
} }
if mod.pluginPath == "" { if mod.pluginPath == "" {
return fmt.Errorf("The parameter %s can not be empty.", tui.Bold("packet.proxy.plugin")) return fmt.Errorf("The parameter %s can not be empty.", tui.Bold("packet.proxy.plugin"))
} else if !fs.Exists(mod.pluginPath) { } else if !fs.Exists(mod.pluginPath) {
return fmt.Errorf("%s does not exist.", mod.pluginPath) return fmt.Errorf("%s does not exist.", mod.pluginPath)
} }
mod.Info("loading packet proxy plugin from %s ...", mod.pluginPath) mod.Info("loading packet proxy plugin from %s ...", mod.pluginPath)
var ok bool var ok bool
var sym plugin.Symbol var sym plugin.Symbol
if mod.plugin, err = plugin.Open(mod.pluginPath); err != nil { if mod.plugin, err = plugin.Open(mod.pluginPath); err != nil {
return return
} else if sym, err = mod.plugin.Lookup("OnPacket"); err != nil { } else if sym, err = mod.plugin.Lookup("OnPacket"); err != nil {
return return
} else if mod.queueCb, ok = sym.(func(*nfqueue.Payload) int); !ok { } else if mod.queueCb, ok = sym.(func(*nfqueue.Payload) int); !ok {
return fmt.Errorf("Symbol OnPacket is not a valid callback function.") return fmt.Errorf("Symbol OnPacket is not a valid callback function.")
} }
mod.queue = new(nfqueue.Queue) mod.queue = new(nfqueue.Queue)
if err = mod.queue.SetCallback(dummyCallback); err != nil { if err = mod.queue.SetCallback(dummyCallback); err != nil {
return return
} else if err = mod.queue.Init(); err != nil { } else if err = mod.queue.Init(); err != nil {
return return
} else if err = mod.queue.Unbind(syscall.AF_INET); err != nil { } else if err = mod.queue.Unbind(syscall.AF_INET); err != nil {
return return
} else if err = mod.queue.Bind(syscall.AF_INET); err != nil { } else if err = mod.queue.Bind(syscall.AF_INET); err != nil {
return return
} else if err = mod.queue.CreateQueue(mod.queueNum); err != nil { } else if err = mod.queue.CreateQueue(mod.queueNum); err != nil {
return return
} else if err = mod.queue.SetMode(nfqueue.NFQNL_COPY_PACKET); err != nil { } else if err = mod.queue.SetMode(nfqueue.NFQNL_COPY_PACKET); err != nil {
return return
} else if err = mod.runRule(true); err != nil { } else if err = mod.runRule(true); err != nil {
return return
} }
return nil return nil
} }
// we need this because for some reason we can't directly func OnRDPConnection(payload *nfqueue.Payload) int {
// pass the symbol loaded from the plugin as a direct log.Info("New Connection: %v", payload)
// CGO callback ... ¯\_(ツ)_/¯ // TODO: Find a more efficient way to do this.
payload.SetVerdict(nfqueue.NF_DROP) // Force a retransmit to trigger the new firewall rules.
return 0
}
func dummyCallback(payload *nfqueue.Payload) int { func dummyCallback(payload *nfqueue.Payload) int {
return mod.queueCb(payload) return mod.queueCb(payload)
} }
func (mod *RdpProxy) Start() error { func (mod *RdpProxy) Start() error {
if mod.Running() { if mod.Running() {
return session.ErrAlreadyStarted(mod.Name()) return session.ErrAlreadyStarted(mod.Name())
} else if err := mod.Configure(); err != nil { } else if err := mod.Configure(); err != nil {
return err return err
} }
return mod.SetRunning(true, func() { return mod.SetRunning(true, func() {
mod.Info("started on queue number %d", mod.queueNum) mod.Info("started on queue number %d", mod.queueNum)
defer mod.destroyQueue() defer mod.destroyQueue()
mod.queue.Loop() mod.queue.Loop()
mod.done <- true mod.done <- true
}) })
} }
func (mod *RdpProxy) Stop() error { func (mod *RdpProxy) Stop() error {
return mod.SetRunning(false, func() { return mod.SetRunning(false, func() {
mod.queue.StopLoop() mod.queue.StopLoop()
mod.runRule(false) mod.runRule(false)
<-mod.done <-mod.done
}) })
} }