Exemplo n.º 1
0
def main():
    config = configure()
    logger = logging.getLogger(LOGGER_NAMES.PYRDP)

    # Create a listening socket to accept connections.
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setblocking(0)

    if config.transparent:
        try:
            if not s.getsockopt(socket.SOL_IP, socket.IP_TRANSPARENT):
                s.setsockopt(socket.SOL_IP, socket.IP_TRANSPARENT, 1)
                s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        except Exception:
            logger.warning('Unable to set transparent socket. Are you running as root?')

    s.bind(('0.0.0.0', config.listenPort))
    s.listen()  # Non-blocking.
    reactor.adoptStreamPort(s.fileno(), socket.AF_INET, MITMServerFactory(config))
    s.close()  # reactor creates a copy of the fd.

    logger.info("MITM Server listening on port %(port)d", {"port": config.listenPort})

    reactor.run()

    logger.info("MITM terminated")
    showConfiguration(config)
Exemplo n.º 2
0
    def makeService(self, options):
        """
        Construct a TCPServer from a MITMServerFactory
        """
        outDir = Path(options["output"])
        outDir.mkdir(exist_ok=True)

        logLevel = options["log-level"]
        prepareLoggers(logLevel, options["log-filter"], options["sensor-id"],
                       outDir)

        # Warning: only implemented a minimal subset of available config
        #          see bin/pyrdp-mitm.py for the full list
        config = MITMConfig()
        targetHost, targetPort = parseTarget(options["target"])
        config.targetHost = targetHost
        config.targetPort = targetPort

        config.disableActiveClipboardStealing = options[
            "disable-active-clipboard"]

        key, certificate = validateKeyAndCertificate(options["private-key"],
                                                     options["certificate"])
        config.privateKeyFileName = key
        config.certificateFileName = certificate

        config.replacementUsername = options["username"]
        config.replacementPassword = options["password"]
        config.outDir = outDir

        return internet.TCPServer(int(options["listen"]),
                                  MITMServerFactory(config))
Exemplo n.º 3
0
def main():
    config = configure()
    reactor.listenTCP(config.listenPort, MITMServerFactory(config))
    logger = logging.getLogger(LOGGER_NAMES.PYRDP)

    logger.info("MITM Server listening on port %(port)d",
                {"port": config.listenPort})
    reactor.run()

    logger.info("MITM terminated")
    showConfiguration(config)
Exemplo n.º 4
0
def main():
    # Warning: keep in sync with twisted/plugins/pyrdp_plugin.py
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "target",
        help="IP:port of the target RDP machine (ex: 192.168.1.10:3390)")
    parser.add_argument("-l",
                        "--listen",
                        help="Port number to listen on (default: 3389)",
                        default=3389)
    parser.add_argument("-o",
                        "--output",
                        help="Output folder",
                        default="pyrdp_output")
    parser.add_argument(
        "-i",
        "--destination-ip",
        help=
        "Destination IP address of the PyRDP player.If not specified, RDP events are not sent over the network."
    )
    parser.add_argument(
        "-d",
        "--destination-port",
        help="Listening port of the PyRDP player (default: 3000).",
        default=3000)
    parser.add_argument("-k",
                        "--private-key",
                        help="Path to private key (for SSL)")
    parser.add_argument("-c",
                        "--certificate",
                        help="Path to certificate (for SSL)")
    parser.add_argument(
        "-u",
        "--username",
        help="Username that will replace the client's username",
        default=None)
    parser.add_argument(
        "-p",
        "--password",
        help="Password that will replace the client's password",
        default=None)
    parser.add_argument(
        "-L",
        "--log-level",
        help="Console logging level. Logs saved to file are always verbose.",
        default="INFO",
        choices=["INFO", "DEBUG", "WARNING", "ERROR", "CRITICAL"])
    parser.add_argument(
        "-F",
        "--log-filter",
        help="Only show logs from this logger name (accepts '*' wildcards)",
        default="")
    parser.add_argument(
        "-s",
        "--sensor-id",
        help=
        "Sensor ID (to differentiate multiple instances of the MITM where logs are aggregated at one place)",
        default="PyRDP")
    parser.add_argument("--payload",
                        help="Command to run automatically upon connection",
                        default=None)
    parser.add_argument(
        "--payload-powershell",
        help="PowerShell command to run automatically upon connection",
        default=None)
    parser.add_argument(
        "--payload-powershell-file",
        help=
        "PowerShell script to run automatically upon connection (as -EncodedCommand)",
        default=None)
    parser.add_argument(
        "--payload-delay",
        help=
        "Time to wait after a new connection before sending the payload, in milliseconds",
        default=None)
    parser.add_argument(
        "--payload-duration",
        help=
        "Amount of time for which input / output should be dropped, in milliseconds. This can be used to hide the payload screen.",
        default=None)
    parser.add_argument("--crawl",
                        help="Enable automatic shared drive scraping",
                        action="store_true")
    parser.add_argument(
        "--crawler-match-file",
        help=
        "File to be used by the crawler to chose what to download when scraping the client shared drives.",
        default=None)
    parser.add_argument(
        "--crawler-ignore-file",
        help=
        "File to be used by the crawler to chose what folders to avoid when scraping the client shared drives.",
        default=None)
    parser.add_argument("--no-replay",
                        help="Disable replay recording",
                        action="store_true")

    args = parser.parse_args()
    outDir = Path(args.output)
    outDir.mkdir(exist_ok=True)

    logLevel = getattr(logging, args.log_level)
    pyrdpLogger = prepareLoggers(logLevel, args.log_filter, args.sensor_id,
                                 outDir)

    targetHost, targetPort = parseTarget(args.target)
    key, certificate = validateKeyAndCertificate(args.private_key,
                                                 args.certificate)

    listenPort = int(args.listen)

    config = MITMConfig()
    config.targetHost = targetHost
    config.targetPort = targetPort
    config.privateKeyFileName = key
    config.certificateFileName = certificate
    config.attackerHost = args.destination_ip
    config.attackerPort = int(args.destination_port)
    config.replacementUsername = args.username
    config.replacementPassword = args.password
    config.outDir = outDir
    config.enableCrawler = args.crawl
    config.crawlerMatchFileName = args.crawler_match_file
    config.crawlerIgnoreFileName = args.crawler_ignore_file
    config.recordReplays = not args.no_replay

    payload = None
    powershell = None

    if int(args.payload is not None) + int(
            args.payload_powershell is not None) + int(
                args.payload_powershell_file is not None) > 1:
        pyrdpLogger.error(
            "Only one of --payload, --payload-powershell and --payload-powershell-file may be supplied."
        )
        sys.exit(1)

    if args.payload is not None:
        payload = args.payload
        pyrdpLogger.info("Using payload: %(payload)s",
                         {"payload": args.payload})
    elif args.payload_powershell is not None:
        powershell = args.payload_powershell
        pyrdpLogger.info("Using powershell payload: %(payload)s",
                         {"payload": args.payload_powershell})
    elif args.payload_powershell_file is not None:
        if not os.path.exists(args.payload_powershell_file):
            pyrdpLogger.error("Powershell file %(path)s does not exist.",
                              {"path": args.payload_powershell_file})
            sys.exit(1)

        try:
            with open(args.payload_powershell_file, "r") as f:
                powershell = f.read()
        except IOError as e:
            pyrdpLogger.error(
                "Error when trying to read powershell file: %(error)s",
                {"error": e})
            sys.exit(1)

        pyrdpLogger.info("Using payload from powershell file: %(path)s",
                         {"path": args.payload_powershell_file})

    if powershell is not None:
        payload = "powershell -EncodedCommand " + b64encode(
            powershell.encode("utf-16le")).decode()

    if payload is not None:
        if args.payload_delay is None:
            pyrdpLogger.error(
                "--payload-delay must be provided if a payload is provided.")
            sys.exit(1)

        if args.payload_duration is None:
            pyrdpLogger.error(
                "--payload-duration must be provided if a payload is provided."
            )
            sys.exit(1)

        try:
            config.payloadDelay = int(args.payload_delay)
        except ValueError:
            pyrdpLogger.error(
                "Invalid payload delay. Payload delay must be an integral number of milliseconds."
            )
            sys.exit(1)

        if config.payloadDelay < 0:
            pyrdpLogger.error("Payload delay must not be negative.")
            sys.exit(1)

        if config.payloadDelay < 1000:
            pyrdpLogger.warning(
                "You have provided a payload delay of less than 1 second. We recommend you use a slightly longer delay to make sure it runs properly."
            )

        try:
            config.payloadDuration = int(args.payload_duration)
        except ValueError:
            pyrdpLogger.error(
                "Invalid payload duration. Payload duration must be an integral number of milliseconds."
            )
            sys.exit(1)

        if config.payloadDuration < 0:
            pyrdpLogger.error("Payload duration must not be negative.")
            sys.exit(1)

        config.payload = payload
    elif args.payload_delay is not None:
        pyrdpLogger.error(
            "--payload-delay was provided but no payload was set.")
        sys.exit(1)

    logConfiguration(config)

    reactor.listenTCP(listenPort, MITMServerFactory(config))
    pyrdpLogger.info("MITM Server listening on port %(port)d",
                     {"port": listenPort})
    reactor.run()

    pyrdpLogger.info("MITM terminated")
    logConfiguration(config)
Exemplo n.º 5
0
 def makeService(self, options):
     """
     Construct a TCPServer from a MITMServerFactory
     """
     config = options['config']
     return internet.TCPServer(config.listenPort, MITMServerFactory(config))
Exemplo n.º 6
0
#!/usr/bin/python3

#
# This file is part of the PyRDP project.
# Copyright (C) 2020 GoSecure Inc.
# Licensed under the GPLv3 or later.
#
"""
Integration test for the initialization of pyrdp-mitm.py.
It could be enhanced with relevant assertions but for now only executes the code to catch
potential basic errors/import problems.
"""
from pyrdp.core.mitm import MITMServerFactory
from pyrdp.mitm.cli import configure

if __name__ == "__main__":
    config = configure()
    server_factory = MITMServerFactory(config)
    server_factory.buildProtocol(addr=None)