diff --git a/modules/rdp_proxy/rdp_proxy_linux_amd64.go b/modules/rdp_proxy/rdp_proxy_linux_amd64.go index d514960f..2303044d 100644 --- a/modules/rdp_proxy/rdp_proxy_linux_amd64.go +++ b/modules/rdp_proxy/rdp_proxy_linux_amd64.go @@ -7,6 +7,10 @@ import ( golog "log" "net" "syscall" + "bufio" + "io" + "regexp" + "bytes" "github.com/bettercap/bettercap/core" "github.com/bettercap/bettercap/network" @@ -19,14 +23,16 @@ import ( type RdpProxy struct { session.SessionModule + targets []net.IP done chan bool queue *nfqueue.Queue queueNum int port int startPort int cmd string + regexp string + compiled *regexp.Regexp active map[string]exec.Cmd - targets []net.IP } var mod *RdpProxy @@ -41,6 +47,7 @@ func NewRdpProxy(s *session.Session) *RdpProxy { port: 3389, startPort: 40000, cmd: "pyrdp-mitm.py", + regexp: "(?i)(cookie:|mstshash=|clipboard data|client info|username|password)", active: make(map[string]exec.Cmd), } @@ -56,15 +63,17 @@ func NewRdpProxy(s *session.Session) *RdpProxy { 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.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.")) +mod.AddParam(session.NewStringParameter("rdp.proxy.regexp", "(?i)(cookie:|mstshash=|clipboard data|client info|username|password)", "", "Print PyRDP logs matching this regular expression.")) return mod } func (mod RdpProxy) Name() string { return "rdp.proxy" + } func (mod RdpProxy) Description() string { @@ -86,6 +95,25 @@ func (mod *RdpProxy) isTarget(ip string) bool { return false } +// Filter PyRDP logs to only show those that matches mod.regexp +func (mod *RdpProxy) filterLogs(prefix string, output io.ReadCloser) { + scanner := bufio.NewScanner(output) + + // For every log in the queue + for scanner.Scan() { + text := scanner.Bytes() + if mod.compiled == nil || mod.compiled.Match(text) { + // Extract the meaningful part of the log + chunks := bytes.Split(text, []byte(" - ")) + + // Get last element + data := chunks[len(chunks) - 1] + + mod.Info("%s %s", prefix, data) + } + } +} + // Adds the firewall rule for proxy instance. func (mod *RdpProxy) doProxy(addr string, port string, enable bool) (err error) { _, err = core.Exec("iptables", []string{ @@ -159,6 +187,12 @@ func (mod *RdpProxy) Configure() (err error) { return } else if mod.targets, _, err = network.ParseTargets(targets, mod.Session.Lan.Aliases()); err != nil { return + } else if err, mod.regexp = mod.StringParam("rdp.proxy.regexp"); err != nil { + return + } else if mod.regexp != "" { + if mod.compiled, err = regexp.Compile(mod.regexp); err != nil { + return + } } else if _, err = exec.LookPath(mod.cmd); err != nil { return } @@ -193,12 +227,12 @@ 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() + ips := fmt.Sprintf("[%v:%v -> %v:%v]", src, sport, 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. + // 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{ @@ -208,20 +242,24 @@ func (mod *RdpProxy) handleRdpConnection(payload *nfqueue.Payload) int { target, } - // 3.2. Spawn pyrdp proxy instance + // 3.2. Spawn PyRDP proxy instance cmd := exec.Command(mod.cmd, args...) - // _stderr, _ := cmd.StderrPipe() + stderrPipe, _ := cmd.StderrPipe() + if err := cmd.Start(); err != nil { // XXX: Failed to start the rdp proxy... accept connection transparently and log? } + // Use goroutines to keep logging each instance of PyRDP + go mod.filterLogs(ips, stderrPipe) + // 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) + mod.Info("Non-target, won't intercept %s", ips) // Add an exception in the firewall to avoid intercepting packets to this destination and port mod.doReturn(dst.String(), dport, true)