mirror of
https://github.com/bettercap/bettercap
synced 2025-08-22 14:24:38 -07:00
Merge pull request #1 from GoSecure/rdp-target
Added targets and queue number support to rdp proxy
This commit is contained in:
commit
2dcae9e859
1 changed files with 83 additions and 41 deletions
|
@ -5,9 +5,11 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
golog "log"
|
golog "log"
|
||||||
|
"net"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/bettercap/bettercap/core"
|
"github.com/bettercap/bettercap/core"
|
||||||
|
"github.com/bettercap/bettercap/network"
|
||||||
"github.com/bettercap/bettercap/session"
|
"github.com/bettercap/bettercap/session"
|
||||||
|
|
||||||
"github.com/chifflier/nfqueue-go/nfqueue"
|
"github.com/chifflier/nfqueue-go/nfqueue"
|
||||||
|
@ -17,13 +19,14 @@ import (
|
||||||
|
|
||||||
type RdpProxy struct {
|
type RdpProxy struct {
|
||||||
session.SessionModule
|
session.SessionModule
|
||||||
done chan bool
|
done chan bool
|
||||||
queue *nfqueue.Queue
|
queue *nfqueue.Queue
|
||||||
queueNum int
|
queueNum int
|
||||||
port int
|
port int
|
||||||
startPort int
|
startPort int
|
||||||
cmd string
|
cmd string
|
||||||
active map[string]exec.Cmd
|
active map[string]exec.Cmd
|
||||||
|
targets []net.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
var mod *RdpProxy
|
var mod *RdpProxy
|
||||||
|
@ -31,6 +34,7 @@ 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),
|
||||||
|
targets: make([]net.IP, 0),
|
||||||
done: make(chan bool),
|
done: make(chan bool),
|
||||||
queue: nil,
|
queue: nil,
|
||||||
queueNum: 0,
|
queueNum: 0,
|
||||||
|
@ -50,11 +54,12 @@ func NewRdpProxy(s *session.Session) *RdpProxy {
|
||||||
return mod.Stop()
|
return mod.Stop()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
mod.AddParam(session.NewIntParameter("rdp.proxy.queue.num", "0", "NFQUEUE number to bind to."))
|
mod.AddParam(session.NewIntParameter("rdp.proxy.queue.num", "0", "NFQUEUE number to bind to."))
|
||||||
mod.AddParam(session.NewIntParameter("rdp.proxy.port", "3389", "RDP port to intercept."))
|
mod.AddParam(session.NewIntParameter("rdp.proxy.port", "3389", "RDP port to intercept."))
|
||||||
mod.AddParam(session.NewIntParameter("rdp.proxy.start", "40000", "Starting port for pyrdp sessionss"))
|
mod.AddParam(session.NewIntParameter("rdp.proxy.start", "40000", "Starting port for pyrdp sessions."))
|
||||||
mod.AddParam(session.NewStringParameter("rdp.proxy.command", "pyrdp-mitm.py", "", "The PyRDP base command to launch the man-in-the-middle."))
|
mod.AddParam(session.NewStringParameter("rdp.proxy.command", "pyrdp-mitm.py", "", "The PyRDP base command to launch the man-in-the-middle."))
|
||||||
mod.AddParam(session.NewStringParameter("rdp.proxy.out", "./", "", "The output directory for PyRDP artifcats."))
|
mod.AddParam(session.NewStringParameter("rdp.proxy.out", "./", "", "The output directory for PyRDP artifacts."))
|
||||||
|
mod.AddParam(session.NewStringParameter("rdp.proxy.targets", session.ParamSubnet, "", "Comma separated list of IP addresses to proxy to, also supports nmap style IP ranges."))
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,13 +72,25 @@ func (mod RdpProxy) Description() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mod RdpProxy) Author() string {
|
func (mod RdpProxy) Author() string {
|
||||||
return "Alexandre Beaulieu <alex@segfault.me>"
|
return "Alexandre Beaulieu <alex@segfault.me> && Maxime Carbonneau <pourliver@gmail.com>"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod *RdpProxy) isTarget(ip string) bool {
|
||||||
|
|
||||||
|
for _, addr := range mod.targets {
|
||||||
|
if addr.String() == ip {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds the firewall rule for proxy instance.
|
// Adds the firewall rule for proxy instance.
|
||||||
func (mod *RdpProxy) doProxy(addr string, port string, enable bool) (err error) {
|
func (mod *RdpProxy) doProxy(addr string, port string, enable bool) (err error) {
|
||||||
_, err = core.Exec("iptables", []string { "-t", "nat",
|
_, err = core.Exec("iptables", []string{
|
||||||
"-I", "BCAPRDP", "1",
|
"-t", "nat",
|
||||||
|
"-I", "BCAPRDP", "1",
|
||||||
"-d", addr,
|
"-d", addr,
|
||||||
"-p", "tcp",
|
"-p", "tcp",
|
||||||
"--dport", fmt.Sprintf("%d", mod.port),
|
"--dport", fmt.Sprintf("%d", mod.port),
|
||||||
|
@ -83,6 +100,18 @@ func (mod *RdpProxy) doProxy(addr string, port string, enable bool) (err error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mod *RdpProxy) doReturn(dst string, dport gopacket.Endpoint, enable bool) (err error) {
|
||||||
|
_, err = core.Exec("iptables", []string{
|
||||||
|
"-t", "nat",
|
||||||
|
"-I", "BCAPRDP", "1",
|
||||||
|
"-p", "tcp",
|
||||||
|
"-d", dst,
|
||||||
|
"--dport", fmt.Sprintf("%v", dport),
|
||||||
|
"-j", "RETURN",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (mod *RdpProxy) configureFirewall(enable bool) (err error) {
|
func (mod *RdpProxy) configureFirewall(enable bool) (err error) {
|
||||||
rules := [][]string{}
|
rules := [][]string{}
|
||||||
|
|
||||||
|
@ -92,9 +121,10 @@ func (mod *RdpProxy) configureFirewall(enable bool) (err error) {
|
||||||
{ "-t", "nat", "-I", "PREROUTING", "1", "-j", "BCAPRDP" },
|
{ "-t", "nat", "-I", "PREROUTING", "1", "-j", "BCAPRDP" },
|
||||||
{ "-t", "nat", "-A", "BCAPRDP",
|
{ "-t", "nat", "-A", "BCAPRDP",
|
||||||
"-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", mod.port),
|
"-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", mod.port),
|
||||||
"-j", "NFQUEUE", "--queue-num", "0", "--queue-bypass",
|
"-j", "NFQUEUE", "--queue-num", fmt.Sprintf("%d", mod.queueNum), "--queue-bypass",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if !enable {
|
} else if !enable {
|
||||||
rules = [][]string{
|
rules = [][]string{
|
||||||
{ "-t", "nat", "-D", "PREROUTING", "-j", "BCAPRDP" },
|
{ "-t", "nat", "-D", "PREROUTING", "-j", "BCAPRDP" },
|
||||||
|
@ -113,6 +143,8 @@ func (mod *RdpProxy) configureFirewall(enable bool) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mod *RdpProxy) Configure() (err error) {
|
func (mod *RdpProxy) Configure() (err error) {
|
||||||
|
var targets string
|
||||||
|
|
||||||
golog.SetOutput(ioutil.Discard)
|
golog.SetOutput(ioutil.Discard)
|
||||||
mod.destroyQueue()
|
mod.destroyQueue()
|
||||||
|
|
||||||
|
@ -123,11 +155,16 @@ func (mod *RdpProxy) Configure() (err error) {
|
||||||
return
|
return
|
||||||
} else if err, mod.queueNum = mod.IntParam("rdp.proxy.queue.num"); err != nil {
|
} else if err, mod.queueNum = mod.IntParam("rdp.proxy.queue.num"); err != nil {
|
||||||
return
|
return
|
||||||
|
} else if err, targets = mod.StringParam("rdp.proxy.targets"); err != nil {
|
||||||
|
return
|
||||||
|
} else if mod.targets, _, err = network.ParseTargets(targets, mod.Session.Lan.Aliases()); err != nil {
|
||||||
|
return
|
||||||
} else if _, err = exec.LookPath(mod.cmd); err != nil {
|
} else if _, err = exec.LookPath(mod.cmd); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mod.Info("Starting RDP Proxy")
|
mod.Info("Starting RDP Proxy")
|
||||||
|
mod.Debug("Targets=%v", mod.targets)
|
||||||
|
|
||||||
// Create the NFQUEUE handler.
|
// Create the NFQUEUE handler.
|
||||||
mod.queue = new(nfqueue.Queue)
|
mod.queue = new(nfqueue.Queue)
|
||||||
|
@ -156,35 +193,43 @@ func (mod *RdpProxy) handleRdpConnection(payload *nfqueue.Payload) int {
|
||||||
src, sport := p.NetworkLayer().NetworkFlow().Src(), p.TransportLayer().TransportFlow().Src()
|
src, sport := p.NetworkLayer().NetworkFlow().Src(), p.TransportLayer().TransportFlow().Src()
|
||||||
dst, dport := p.NetworkLayer().NetworkFlow().Dst(), p.TransportLayer().TransportFlow().Dst()
|
dst, dport := p.NetworkLayer().NetworkFlow().Dst(), p.TransportLayer().TransportFlow().Dst()
|
||||||
|
|
||||||
// TODO: Don't log here and connect a pipe to the process instead.
|
if mod.isTarget(dst.String()) {
|
||||||
mod.Info("CONNECT [%s:%v -> %v:%v]", src, sport, dst, dport)
|
// TODO: Don't log here and connect a pipe to the process instead.
|
||||||
target := fmt.Sprintf("%v:%v", dst, dport)
|
mod.Info("CONNECT [%s:%v -> %v:%v]", src, sport, dst, dport)
|
||||||
|
target := fmt.Sprintf("%v:%v", dst, dport)
|
||||||
|
|
||||||
// 2. Check if the destination IP already has a PYRDP session active, if so, do nothing.
|
// 2. Check if the destination IP already has a PYRDP session active, if so, do nothing.
|
||||||
if _, ok := mod.active[target]; !ok {
|
if _, ok := mod.active[target]; !ok {
|
||||||
// 3.1. Otherwise, create a proxy agent and firewall rules.
|
// 3.1. Otherwise, create a proxy agent and firewall rules.
|
||||||
args := []string{
|
args := []string{
|
||||||
"-l", fmt.Sprintf("%d", mod.startPort),
|
"-l", fmt.Sprintf("%d", mod.startPort),
|
||||||
// "-o", mod.outpath,
|
// "-o", mod.outpath,
|
||||||
// "-i", "-d"
|
// "-i", "-d"
|
||||||
target,
|
target,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.2. Spawn pyrdp proxy instance
|
||||||
|
cmd := exec.Command(mod.cmd, args...)
|
||||||
|
// _stderr, _ := cmd.StderrPipe()
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
// XXX: Failed to start the rdp proxy... accept connection transparently and log?
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.3. Add a NAT rule in the firewall for this particular target IP
|
||||||
|
mod.doProxy(dst.String(), fmt.Sprintf("%d", mod.startPort), true)
|
||||||
|
mod.active[target] = *cmd
|
||||||
|
mod.startPort += 1
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mod.Info("Non-target, won't intercept [%s:%v -> %v:%v]", src, sport, dst, dport)
|
||||||
|
|
||||||
// 3.2. Spawn pyrdp proxy instance
|
// Add an exception in the firewall to avoid intercepting packets to this destination and port
|
||||||
cmd := exec.Command(mod.cmd, args...)
|
mod.doReturn(dst.String(), dport, true)
|
||||||
// _stderr, _ := cmd.StderrPipe()
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
// XXX: Failed to start the rdp proxy... accept connection transparently and log?
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3.3. Add a NAT rule in the firewall for this particular target IP
|
|
||||||
mod.doProxy(dst.String(), fmt.Sprintf("%d", mod.startPort), true)
|
|
||||||
mod.active[target] = *cmd
|
|
||||||
mod.startPort += 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force a retransmit to trigger the new firewall rules. (TODO: Find a more efficient way to do this.)
|
// Force a retransmit to trigger the new firewall rules. (TODO: Find a more efficient way to do this.)
|
||||||
payload.SetVerdict(nfqueue.NF_DROP)
|
payload.SetVerdict(nfqueue.NF_DROP)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,8 +257,6 @@ func (mod *RdpProxy) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
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.configureFirewall(false)
|
mod.configureFirewall(false)
|
||||||
|
@ -234,4 +277,3 @@ func (mod *RdpProxy) destroyQueue() {
|
||||||
mod.queue.Close()
|
mod.queue.Close()
|
||||||
mod.queue = nil
|
mod.queue = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue