diff --git a/modules/rdp_proxy/rdp_proxy_linux_amd64.go b/modules/rdp_proxy/rdp_proxy_linux_amd64.go index dd703e8b..569efbc6 100644 --- a/modules/rdp_proxy/rdp_proxy_linux_amd64.go +++ b/modules/rdp_proxy/rdp_proxy_linux_amd64.go @@ -5,9 +5,11 @@ import ( "os/exec" "io/ioutil" golog "log" + "net" "syscall" "github.com/bettercap/bettercap/core" + "github.com/bettercap/bettercap/network" "github.com/bettercap/bettercap/session" "github.com/chifflier/nfqueue-go/nfqueue" @@ -17,13 +19,14 @@ import ( type RdpProxy struct { session.SessionModule - done chan bool - queue *nfqueue.Queue - queueNum int - port int - startPort int - cmd string - active map[string]exec.Cmd + done chan bool + queue *nfqueue.Queue + queueNum int + port int + startPort int + cmd string + active map[string]exec.Cmd + addresses []net.IP } var mod *RdpProxy @@ -31,6 +34,7 @@ var mod *RdpProxy func NewRdpProxy(s *session.Session) *RdpProxy { mod = &RdpProxy{ SessionModule: session.NewSessionModule("rdp.proxy", s), + addresses: make([]net.IP, 0), done: make(chan bool), queue: nil, queueNum: 0, @@ -50,11 +54,12 @@ func NewRdpProxy(s *session.Session) *RdpProxy { return mod.Stop() })) - 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.start", "40000", "Starting port for pyrdp sessionss")) - 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.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.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.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 } @@ -70,10 +75,22 @@ func (mod RdpProxy) Author() string { return "Alexandre Beaulieu " } +func (mod *RdpProxy) isTarget(ip string) bool { + + for _, addr := range mod.addresses { + if addr.String() == ip { + return true + } + } + + return false +} + // Adds the firewall rule for proxy instance. func (mod *RdpProxy) doProxy(addr string, port string, enable bool) (err error) { - _, err = core.Exec("iptables", []string { "-t", "nat", - "-I", "BCAPRDP", "1", + _, err = core.Exec("iptables", []string{ + "-t", "nat", + "-I", "BCAPRDP", "1", "-d", addr, "-p", "tcp", "--dport", fmt.Sprintf("%d", mod.port), @@ -83,6 +100,18 @@ func (mod *RdpProxy) doProxy(addr string, port string, enable bool) (err error) 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) { rules := [][]string{} @@ -92,9 +121,10 @@ func (mod *RdpProxy) configureFirewall(enable bool) (err error) { { "-t", "nat", "-I", "PREROUTING", "1", "-j", "BCAPRDP" }, { "-t", "nat", "-A", "BCAPRDP", "-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 { rules = [][]string{ { "-t", "nat", "-D", "PREROUTING", "-j", "BCAPRDP" }, @@ -113,6 +143,8 @@ func (mod *RdpProxy) configureFirewall(enable bool) (err error) { } func (mod *RdpProxy) Configure() (err error) { + var targets string + golog.SetOutput(ioutil.Discard) mod.destroyQueue() @@ -123,11 +155,16 @@ func (mod *RdpProxy) Configure() (err error) { return } else if err, mod.queueNum = mod.IntParam("rdp.proxy.queue.num"); err != nil { return + } else if err, targets = mod.StringParam("rdp.proxy.targets"); err != nil { + return + } else if mod.addresses, _, err = network.ParseTargets(targets, mod.Session.Lan.Aliases()); err != nil { + return } else if _, err = exec.LookPath(mod.cmd); err != nil { return } mod.Info("Starting RDP Proxy") + mod.Debug("addresses=%v", mod.addresses) // Create the NFQUEUE handler. 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() dst, dport := p.NetworkLayer().NetworkFlow().Dst(), p.TransportLayer().TransportFlow().Dst() - // TODO: Don't log here and connect a pipe to the process instead. - mod.Info("CONNECT [%s:%v -> %v:%v]", src, sport, dst, dport) - target := fmt.Sprintf("%v:%v", dst, dport) + if mod.isTarget(dst.String()) { + // TODO: Don't log here and connect a pipe to the process instead. + 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. - if _, ok := mod.active[target]; !ok { - // 3.1. Otherwise, create a proxy agent and firewall rules. - args := []string{ - "-l", fmt.Sprintf("%d", mod.startPort), - // "-o", mod.outpath, - // "-i", "-d" - target, + // 2. Check if the destination IP already has a PYRDP session active, if so, do nothing. + if _, ok := mod.active[target]; !ok { + // 3.1. Otherwise, create a proxy agent and firewall rules. + args := []string{ + "-l", fmt.Sprintf("%d", mod.startPort), + // "-o", mod.outpath, + // "-i", "-d" + 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 - 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 + // Add an exception in the firewall to avoid intercepting packets to this destination and port + mod.doReturn(dst.String(), dport, true) } // Force a retransmit to trigger the new firewall rules. (TODO: Find a more efficient way to do this.) payload.SetVerdict(nfqueue.NF_DROP) + return 0 } @@ -212,8 +257,6 @@ func (mod *RdpProxy) Start() error { } func (mod *RdpProxy) Stop() error { - - return mod.SetRunning(false, func() { mod.queue.StopLoop() mod.configureFirewall(false) @@ -234,4 +277,3 @@ func (mod *RdpProxy) destroyQueue() { mod.queue.Close() mod.queue = nil } -