Exemple #1
0
    def start(self):
        """
        Initialize pymisp module and ObjectWrapper (Abstract event and object creation)
        """
        host: str = CowrieConfig.get("output_redis", "host")
        port: int = CowrieConfig.getint("output_redis", "port")

        try:
            db = CowrieConfig.getint("output_redis", "db")
        except NoOptionError:
            db = 0

        try:
            password = CowrieConfig.get("output_redis", "password")
        except NoOptionError:
            password = None

        self.redis = redis.StrictRedis(host=host, port=port, db=db, password=password)

        self.keyname = CowrieConfig.get("output_redis", "keyname")

        try:
            self.send_method = SEND_METHODS[
                CowrieConfig.get("output_redis", "send_method")
            ]
        except (NoOptionError, KeyError):
            self.send_method = SEND_METHODS["lpush"]
Exemple #2
0
    def send_initialisation(self):
        """
        Used only by the PoolHandler on the first connection, to set the pool up.
        """
        max_vms = CowrieConfig.getint("proxy", "pool_max_vms", fallback=2)
        vm_unused_timeout = CowrieConfig.getint(
            "proxy", "pool_vm_unused_timeout", fallback=600
        )
        share_guests = CowrieConfig.getboolean(
            "proxy", "pool_share_guests", fallback=True
        )

        buf = struct.pack("!cII?", b"i", max_vms, vm_unused_timeout, share_guests)
        self.transport.write(buf)
Exemple #3
0
 def start(self):
     """
     Start Output Plugin
     """
     self.timeout = [
         CowrieConfig.getint("output_reversedns", "timeout", fallback=3)
     ]
Exemple #4
0
    def connectionMade(self):
        pt = self.getProtoTransport()

        self.sessionno = pt.transport.sessionno
        self.realClientIP = pt.transport.getPeer().host
        self.realClientPort = pt.transport.getPeer().port
        self.logintime = time.time()

        log.msg(eventid="cowrie.session.params", arch=self.user.server.arch)

        timeout = CowrieConfig.getint("honeypot",
                                      "interactive_timeout",
                                      fallback=180)
        self.setTimeout(timeout)

        # Source IP of client in user visible reports (can be fake or real)
        try:
            self.clientIP = CowrieConfig.get("honeypot", "fake_addr")
        except Exception:
            self.clientIP = self.realClientIP

        # Source IP of server in user visible reports (can be fake or real)
        if CowrieConfig.has_option("honeypot", "internet_facing_ip"):
            self.kippoIP = CowrieConfig.get("honeypot", "internet_facing_ip")
        else:
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.connect(("8.8.8.8", 80))
                self.kippoIP = s.getsockname()[0]
            except Exception:
                self.kippoIP = "192.168.0.1"
            finally:
                s.close()
Exemple #5
0
    def backend_connection_success(self, backendTransport):
        log.msg("Connected to honeypot backend")

        self.startTime = time.time()
        self.setTimeout(
            CowrieConfig.getint("honeypot", "authentication_timeout", fallback=120)
        )
Exemple #6
0
    def start(self):
        log.msg(
            "WARNING: Beta version of new hpfeeds enabled. This will become hpfeeds in a future release."
        )

        if CowrieConfig.has_option("output_hpfeeds3", "channel"):
            self.channel = CowrieConfig.get("output_hpfeeds3", "channel")

        if CowrieConfig.has_option("output_hpfeeds3", "endpoint"):
            endpoint = CowrieConfig.get("output_hpfeeds3", "endpoint")
        else:
            server = CowrieConfig.get("output_hpfeeds3", "server")
            port = CowrieConfig.getint("output_hpfeeds3", "port")

            if CowrieConfig.has_option("output_hpfeeds3", "tlscert"):
                with open(CowrieConfig.get("output_hpfeeds3",
                                           "tlscert")) as fp:
                    authority = ssl.Certificate.loadPEM(fp.read())
                options = ssl.optionsForClientTLS(server, authority)
                endpoint = endpoints.SSL4ClientEndpoint(
                    reactor, server, port, options)
            else:
                endpoint = endpoints.HostnameEndpoint(reactor, server, port)

        ident = CowrieConfig.get("output_hpfeeds3", "identifier")
        secret = CowrieConfig.get("output_hpfeeds3", "secret")

        self.meta = {}

        self.client = ClientSessionService(endpoint, ident, secret)
        self.client.startService()
Exemple #7
0
    def __init__(self, nat_service):
        self.qemu = backend_pool.libvirt.backend_service.LibvirtBackendService(
        )
        self.nat_service = nat_service

        self.guests = []
        self.guest_id: int = 0
        self.guest_lock = Lock()

        # time in seconds between each loop iteration
        self.loop_sleep_time: int = 5
        self.loop_next_call = None

        # default configs; custom values will come from the client when they connect to the pool
        self.max_vm: int = 2
        self.vm_unused_timeout: int = 600
        self.share_guests: bool = True

        # file configs
        self.ssh_port: int = CowrieConfig.getint("backend_pool",
                                                 "guest_ssh_port",
                                                 fallback=-1)
        self.telnet_port: int = CowrieConfig.getint("backend_pool",
                                                    "guest_telnet_port",
                                                    fallback=-1)

        self.local_pool: str = (CowrieConfig.get("proxy",
                                                 "pool",
                                                 fallback="local") == "local")
        self.pool_only: bool = CowrieConfig.getboolean("backend_pool",
                                                       "pool_only",
                                                       fallback=False)
        self.use_nat: bool = CowrieConfig.getboolean("backend_pool",
                                                     "use_nat",
                                                     fallback=True)

        # detect invalid config
        if not self.ssh_port > 0 and not self.telnet_port > 0:
            log.msg(
                eventid="cowrie.backend_pool.service",
                format=
                "Invalid configuration: one of SSH or Telnet ports must be defined!",
            )
            os._exit(1)

        self.any_vm_up: bool = False  # TODO fix for no VM available
Exemple #8
0
 def start(self):
     self.auth_key = CowrieConfig.get("output_dshield", "auth_key")
     self.userid = CowrieConfig.get("output_dshield", "userid")
     self.batch_size = CowrieConfig.getint("output_dshield", "batch_size")
     self.debug = CowrieConfig.getboolean("output_dshield",
                                          "debug",
                                          fallback=False)
     self.batch = []  # This is used to store login attempts in batches
Exemple #9
0
 def start(self):
     log.msg("Early version of hpfeeds-output, untested!")
     server = CowrieConfig.get("output_hpfeeds", "server")
     port = CowrieConfig.getint("output_hpfeeds", "port")
     ident = CowrieConfig.get("output_hpfeeds", "identifier")
     secret = CowrieConfig.get("output_hpfeeds", "secret")
     debug = CowrieConfig.getboolean("output_hpfeeds", "debug")
     self.client = hpclient(server, port, ident, secret, debug)
     self.meta = {}
Exemple #10
0
    def start(self):
        self.timeout = CowrieConfig.getint("output_socketlog", "timeout")
        addr = CowrieConfig.get("output_socketlog", "address")
        self.host = addr.split(":")[0]
        self.port = int(addr.split(":")[1])

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.settimeout(self.timeout)
        self.sock.connect((self.host, self.port))
    def backend_connection_success(self, backendTransport):
        log.msg("Connected to honeypot backend")

        self.startTime = time.time()

        # this timeout is replaced with `interactive_timeout` in ssh.py
        self.setTimeout(
            CowrieConfig.getint("honeypot",
                                "authentication_timeout",
                                fallback=120))
Exemple #12
0
 def start(self):
     self.host = CowrieConfig.get(RETHINK_DB_SEGMENT, "host")
     self.port = CowrieConfig.getint(RETHINK_DB_SEGMENT, "port")
     self.db = CowrieConfig.get(RETHINK_DB_SEGMENT, "db")
     self.table = CowrieConfig.get(RETHINK_DB_SEGMENT, "table")
     self.password = CowrieConfig.get(RETHINK_DB_SEGMENT, "password", raw=True)
     self.connection = r.connect(
         host=self.host, port=self.port, db=self.db, password=self.password
     )
     try:
         r.db_create(self.db).run(self.connection)
         r.db(self.db).table_create(self.table).run(self.connection)
     except r.RqlRuntimeError:
         pass
Exemple #13
0
    def start(self):
        self.api_url = CowrieConfig.get(
            "output_threatjammer",
            "api_url",
            fallback=THREATJAMMER_REPORT_URL,
        )
        self.default_ttl = CowrieConfig.getint(
            "output_threatjammer", "ttl", fallback=THREATJAMMER_DEFAULT_TTL)
        self.default_category = CowrieConfig.get(
            "output_threatjammer",
            "category",
            fallback=THREATJAMMER_DEFAULT_CATEGORY,
        )
        self.track_login = CowrieConfig.getboolean(
            "output_threatjammer",
            "track_login",
            fallback=THREATJAMMER_DEFAULT_TRACK_LOGIN,
        )
        self.track_session = CowrieConfig.getboolean(
            "output_threatjammer",
            "track_session",
            fallback=THREATJAMMER_DEFAULT_TRACK_SESSION,
        )
        self.bearer_token = CowrieConfig.get("output_threatjammer",
                                             "bearer_token")
        self.tags = CowrieConfig.get("output_threatjammer", "tags").split(",")

        self.last_report: int = -1
        self.report_bucket: int = BUFFER_FLUSH_MAX_SIZE
        self.ip_set: Set[str] = set()

        self.track_events = []
        if self.track_login:
            self.track_events.append("cowrie.login")

        if self.track_session:
            self.track_events.append("cowrie.session")

        self.http_client = HTTPClient(self.api_url, self.bearer_token)
        log.msg(
            eventid="cowrie.threatjammer.reporterinitialized",
            format="ThreatJammer.com output plugin successfully initialized.\
 Category=%(category)s. TTL=%(ttl)s. Session Tracking=%(session_tracking)s. Login Tracking=%(login_tracking)s",
            category=self.default_category,
            ttl=self.default_ttl,
            session_tracking=self.track_session,
            login_tracking=self.track_login,
        )
Exemple #14
0
 def __init__(self, tolerance_attempts, state_dump):
     self.sleeping = False
     self.sleep_until = 0
     self.tolerance_attempts = tolerance_attempts
     self.tolerance_window = 60 * CowrieConfig.getint(
         "output_abuseipdb", "tolerance_window", fallback=120)
     self.rereport_after = 3600 * CowrieConfig.getfloat(
         "output_abuseipdb", "rereport_after", fallback=24)
     if self.rereport_after < REREPORT_MINIMUM:
         self.rereport_after = REREPORT_MINIMUM
     self.state_dump = state_dump
     # To write our dump to disk we have a method we call in a thread so we
     # don't block if we get slow io. This is a cheap hack to get a lock on
     # the file. See self.write_dump_file()
     self._writing = False
     super().__init__()
    def connectionMade(self):
        """
        Called when the connection is made to the other side.  We sent our
        version and the MSG_KEXINIT packet.
        """
        self.sshParse = ssh.SSH(self)
        self.transportId = uuid.uuid4().hex[:12]

        self.peer_ip = self.transport.getPeer().host
        self.peer_port = self.transport.getPeer().port + 1
        self.local_ip = self.transport.getHost().host
        self.local_port = self.transport.getHost().port

        self.transport.write(f"{self.ourVersionString}\r\n".encode())
        self.currentEncryptions = transport.SSHCiphers(b"none", b"none",
                                                       b"none", b"none")
        self.currentEncryptions.setKeys(b"", b"", b"", b"", b"", b"")
        self.otherVersionString = "Unknown"

        log.msg(
            eventid="cowrie.session.connect",
            format=
            "New connection: %(src_ip)s:%(src_port)s (%(dst_ip)s:%(dst_port)s) [session: %(session)s]",
            src_ip=self.peer_ip,
            src_port=self.transport.getPeer().port,
            dst_ip=self.local_ip,
            dst_port=self.transport.getHost().port,
            session=self.transportId,
            sessionno=f"S{self.transport.sessionno}",
            protocol="ssh",
        )

        # if we have a pool connect to it and later request a backend, else just connect to a simple backend
        # when pool is set we can just test self.pool_interface to the same effect of getting the CowrieConfig
        proxy_backend = CowrieConfig.get("proxy", "backend", fallback="simple")

        if proxy_backend == "pool":
            # request a backend
            d = self.factory.pool_handler.request_interface()
            d.addCallback(self.pool_connection_success)
            d.addErrback(self.pool_connection_error)
        else:
            # simply a proxy, no pool
            backend_ip = CowrieConfig.get("proxy", "backend_ssh_host")
            backend_port = CowrieConfig.getint("proxy", "backend_ssh_port")
            self.connect_to_backend(backend_ip, backend_port)
Exemple #16
0
    def processPasswordInput(self) -> None:
        self.sendData = False  # withold data until input is complete

        if self.prePasswordData:
            self.sendBackend(self.currentData[:3])
            self.prePasswordData = False

        # remove control characters
        control_chars = [b"\xff", b"\xfd", b"\x01", b"\r", b"\x00", b"\n"]
        self.passwordState += remove_all(self.currentData, control_chars)

        # check if done inputing
        if b"\r" in self.currentData:
            terminatingChar = chr(
                self.currentData[self.currentData.index(b"\r") +
                                 1]).encode()  # usually \n or \x00

            # cleanup
            self.passwordState = process_backspaces(self.passwordState)

            log.msg(
                f"User input password: {self.passwordState.decode('unicode-escape')}"
            )
            self.inputingPassword = False

            # having the password (and the username, either empy or set before), we can check the login
            # on the database, and if valid authenticate or else, if invalid send a fake password to get
            # the login failed prompt
            src_ip = self.server.transport.getPeer().host
            if HoneypotPasswordChecker().checkUserPass(self.usernameState,
                                                       self.passwordState,
                                                       src_ip):
                passwordToSend = self.backendPassword
                self.authDone = True
                self.server.setTimeout(
                    CowrieConfig.getint("honeypot",
                                        "interactive_timeout",
                                        fallback=300))
            else:
                log.msg("Sending invalid auth to backend")
                passwordToSend = self.backendPassword + b"fake"

            # actually send to backend
            self.currentData = passwordToSend + b"\r" + terminatingChar
            self.sendData = True
Exemple #17
0
    def _cbLogin(self, ial):
        """
        Fired on a successful login
        """
        interface, protocol, logout = ial
        protocol.windowSize = self.windowSize
        self.protocol = protocol
        self.logout = logout
        self.state = "Command"

        self.transport.write(b"\n")

        # Remove the short timeout of the login prompt.
        self.transport.setTimeout(
            CowrieConfig.getint("honeypot", "interactive_timeout", fallback=300)
        )

        # replace myself with avatar protocol
        protocol.makeConnection(self.transport)
        self.transport.protocol = protocol
Exemple #18
0
 def start(self):
     self.debug = CowrieConfig.getboolean("output_mysql",
                                          "debug",
                                          fallback=False)
     port = CowrieConfig.getint("output_mysql", "port", fallback=3306)
     try:
         self.db = ReconnectingConnectionPool(
             "MySQLdb",
             host=CowrieConfig.get("output_mysql", "host"),
             db=CowrieConfig.get("output_mysql", "database"),
             user=CowrieConfig.get("output_mysql", "username"),
             passwd=CowrieConfig.get("output_mysql", "password", raw=True),
             port=port,
             cp_min=1,
             cp_max=1,
             charset="utf8mb4",
             cp_reconnect=True,
             use_unicode=True,
         )
     except (MySQLdb.Error, MySQLdb._exceptions.Error) as e:
         log.msg("output_mysql: Error %d: %s" % (e.args[0], e.args[1]))
Exemple #19
0
    def connectionMade(self):
        self.transportId = uuid.uuid4().hex[:12]
        sessionno = self.transport.sessionno

        self.startTime = time.time()
        self.setTimeout(
            CowrieConfig.getint("honeypot", "authentication_timeout", fallback=120)
        )

        log.msg(
            eventid="cowrie.session.connect",
            format="New connection: %(src_ip)s:%(src_port)s (%(dst_ip)s:%(dst_port)s) [session: %(session)s]",
            src_ip=self.transport.getPeer().host,
            src_port=self.transport.getPeer().port,
            dst_ip=self.transport.getHost().host,
            dst_port=self.transport.getHost().port,
            session=self.transportId,
            sessionno="T{}".format(str(sessionno)),
            protocol="telnet",
        )
        TelnetTransport.connectionMade(self)
    def start_pool(self):
        # cleanup older qemu objects
        self.qemu.destroy_all_cowrie()

        # start backend qemu environment
        self.qemu.start_backend()

        # cleanup references if restarting
        self.guests = []
        self.guest_id = 0

        self.any_vm_up = False  # TODO fix for no VM available

        # start producer
        threads.deferToThread(self.producer_loop)

        # recycle myself after some time
        recycle_period = CowrieConfig.getint("backend_pool",
                                             "recycle_period",
                                             fallback=-1)
        if recycle_period > 0:
            reactor.callLater(recycle_period, self.restart_pool)
Exemple #21
0
    def connectionMade(self):
        self.transportId = uuid.uuid4().hex[:12]
        sessionno = self.transport.sessionno

        self.peer_ip = self.transport.getPeer().host
        self.peer_port = self.transport.getPeer().port + 1
        self.local_ip = self.transport.getHost().host
        self.local_port = self.transport.getHost().port

        log.msg(
            eventid="cowrie.session.connect",
            format=
            "New connection: %(src_ip)s:%(src_port)s (%(dst_ip)s:%(dst_port)s) [session: %(session)s]",
            src_ip=self.transport.getPeer().host,
            src_port=self.transport.getPeer().port,
            dst_ip=self.transport.getHost().host,
            dst_port=self.transport.getHost().port,
            session=self.transportId,
            sessionno=f"T{str(sessionno)}",
            protocol="telnet",
        )

        TelnetTransport.connectionMade(self)

        # if we have a pool connect to it and later request a backend, else just connect to a simple backend
        # when pool is set we can just test self.pool_interface to the same effect of getting the config
        proxy_backend = CowrieConfig.get("proxy", "backend", fallback="simple")

        if proxy_backend == "pool":
            # request a backend
            d = self.factory.pool_handler.request_interface()
            d.addCallback(self.pool_connection_success)
            d.addErrback(self.pool_connection_error)
        else:
            # simply a proxy, no pool
            backend_ip = CowrieConfig.get("proxy", "backend_telnet_host")
            backend_port = CowrieConfig.getint("proxy", "backend_telnet_port")
            self.connect_to_backend(backend_ip, backend_port)
Exemple #22
0
    def start(self):
        self.debug = CowrieConfig.getboolean("output_mysql", "debug", fallback=False)
        port = CowrieConfig.getint("output_mysql", "port", fallback=3306)
        try:
            self.db = ReconnectingConnectionPool(
                "mysql.connector",
                host=CowrieConfig.get("output_mysql", "host"),
                db=CowrieConfig.get("output_mysql", "database"),
                user=CowrieConfig.get("output_mysql", "username"),
                passwd=CowrieConfig.get("output_mysql", "password", raw=True),
                port=port,
                cp_min=1,
                cp_max=1,
                charset="utf8mb4",
                cp_reconnect=True,
                use_unicode=True,
            )
        # except (MySQLdb.Error, MySQLdb._exceptions.Error) as e:
        except Exception as e:
            log.msg(f"output_mysql: Error {e.args[0]}: {e.args[1]}")

        self.lc = LoopingCall(self.check_wait)
        self.lc.start(30)
        self.versions = {}
Exemple #23
0
    def dataReceived(self, data):
        res_op = struct.unpack("!c", bytes([
            data[0]
        ]))[0]  # yes, this needs to be done to extract the op code correctly
        response = None

        if res_op == b"i":
            recv = struct.unpack("!II?", data[1:])

            # set the pool service thread configs
            max_vm = recv[0]
            vm_unused_timeout = recv[1]
            share_guests = recv[2]
            self.factory.pool_service.set_configs(max_vm, vm_unused_timeout,
                                                  share_guests)

            # respond with ok
            self.factory.initialised = True
            response = struct.pack("!cI", b"i", 0)

        elif res_op == b"r":
            # receives: attacker ip (used to serve same VM to same attacker)
            # sends: status code, guest_id, guest_ip, guest's ssh and telnet port

            recv = struct.unpack("!H", data[1:3])
            ip_len = recv[0]

            recv = struct.unpack(f"!{ip_len}s", data[3:])
            attacker_ip = recv[0].decode()

            log.msg(
                eventid="cowrie.backend_pool.server",
                format="Requesting a VM for attacker @ %(attacker_ip)s",
                attacker_ip=attacker_ip,
            )

            try:
                (
                    guest_id,
                    guest_ip,
                    guest_snapshot,
                ) = self.factory.pool_service.request_vm(attacker_ip)
                log.msg(
                    eventid="cowrie.backend_pool.server",
                    format="Providing VM id %(guest_id)s",
                    guest_id=guest_id,
                )

                ssh_port = CowrieConfig.getint("backend_pool",
                                               "guest_ssh_port",
                                               fallback=22)
                telnet_port = CowrieConfig.getint("backend_pool",
                                                  "guest_telnet_port",
                                                  fallback=23)

                # after we receive ip and ports, expose ports in the pool's public interface
                # we use NAT if this pool is being run remotely, and if users choose so
                if not self.local_pool and self.use_nat or self.pool_only:
                    nat_ssh_port, nat_telnet_port = self.factory.nat.request_binding(
                        guest_id, guest_ip, ssh_port, telnet_port)

                    fmt = "!cIIH{}sHHH{}s".format(len(self.nat_public_ip),
                                                  len(guest_snapshot))
                    response = struct.pack(
                        fmt,
                        b"r",
                        0,
                        guest_id,
                        len(self.nat_public_ip),
                        self.nat_public_ip.encode(),
                        nat_ssh_port,
                        nat_telnet_port,
                        len(guest_snapshot),
                        guest_snapshot.encode(),
                    )
                else:
                    fmt = "!cIIH{}sHHH{}s".format(len(guest_ip),
                                                  len(guest_snapshot))
                    response = struct.pack(
                        fmt,
                        b"r",
                        0,
                        guest_id,
                        len(guest_ip),
                        guest_ip.encode(),
                        ssh_port,
                        telnet_port,
                        len(guest_snapshot),
                        guest_snapshot.encode(),
                    )
            except NoAvailableVMs:
                log.msg(
                    eventid="cowrie.backend_pool.server",
                    format="No VM available, returning error code",
                )
                response = struct.pack("!cI", b"r", 1)

        elif res_op == b"f":
            # receives: guest_id
            recv = struct.unpack("!I", data[1:])
            guest_id = recv[0]

            log.msg(
                eventid="cowrie.backend_pool.server",
                format="Freeing VM %(guest_id)s",
                guest_id=guest_id,
            )

            # free the NAT
            if not self.local_pool and self.use_nat or self.pool_only:
                self.factory.nat.free_binding(guest_id)

            # free the vm
            self.factory.pool_service.free_vm(guest_id)

        elif res_op == b"u":
            # receives: guest_id
            recv = struct.unpack("!I", data[1:])
            guest_id = recv[0]

            log.msg(
                eventid="cowrie.backend_pool.server",
                format="Re-using VM %(guest_id)s (not used by attacker)",
                guest_id=guest_id,
            )

            # free the NAT
            if not self.local_pool and self.use_nat or self.pool_only:
                self.factory.nat.free_binding(guest_id)

            # free this connection and allow VM to be re-used
            self.factory.pool_service.reuse_vm(guest_id)

        if response:
            self.transport.write(response)
Exemple #24
0
class CowrieSSHChannel(channel.SSHChannel):
    """
    This is an SSH channel with built-in logging
    """

    ttylogEnabled = True
    ttylogFile = ""
    bytesReceived = 0
    bytesReceivedLimit = 0
    bytesWritten = 0
    name = b"cowrie-ssh-channel"
    startTime: float = 0.0
    ttylogPath = CowrieConfig.get("honeypot", "log_path")
    downloadPath = CowrieConfig.get("honeypot", "download_path")
    ttylogEnabled = CowrieConfig.getboolean("honeypot",
                                            "ttylog",
                                            fallback=True)
    bytesReceivedLimit = CowrieConfig.getint("honeypot",
                                             "download_limit_size",
                                             fallback=0)

    def __repr__(self):
        """
        Return a pretty representation of this object.

        @return Pretty representation of this object as a string
        @rtype: L{str}
        """
        return f"Cowrie SSH Channel {self.name}"

    def __init__(self, *args, **kw):
        """
        Initialize logging
        """
        channel.SSHChannel.__init__(self, *args, **kw)

    def channelOpen(self, specificData):
        self.startTime = time.time()
        self.ttylogFile = "{}/tty/{}-{}-{}.log".format(
            self.ttylogPath,
            time.strftime("%Y%m%d-%H%M%S"),
            self.conn.transport.transportId,
            self.id,
        )
        log.msg(
            eventid="cowrie.log.open",
            ttylog=self.ttylogFile,
            format="Opening TTY Log: %(ttylog)s",
        )
        ttylog.ttylog_open(self.ttylogFile, time.time())
        channel.SSHChannel.channelOpen(self, specificData)

    def closed(self):
        log.msg(
            eventid="cowrie.log.closed",
            format="Closing TTY Log: %(ttylog)s after %(duration)f seconds",
            ttylog=self.ttylogFile,
            size=self.bytesReceived + self.bytesWritten,
            duration=time.time() - self.startTime,
        )
        ttylog.ttylog_close(self.ttylogFile, time.time())
        channel.SSHChannel.closed(self)

    def dataReceived(self, data):
        """
        Called when we receive data from the user

        @type data: L{bytes}
        @param data: Data sent to the server from the client
        """
        self.bytesReceived += len(data)
        if self.bytesReceivedLimit and self.bytesReceived > self.bytesReceivedLimit:
            log.msg(f"Data upload limit reached for channel {self.id}")
            self.eofReceived()
            return

        if self.ttylogEnabled:
            ttylog.ttylog_write(self.ttylogFile, len(data), ttylog.TYPE_INPUT,
                                time.time(), data)

        channel.SSHChannel.dataReceived(self, data)

    def write(self, data):
        """
        Called when we send data to the user

        @type data: L{bytes}
        @param data: Data sent to the client from the server
        """
        if self.ttylogEnabled:
            ttylog.ttylog_write(self.ttylogFile, len(data), ttylog.TYPE_OUTPUT,
                                time.time(), data)
            self.bytesWritten += len(data)

        channel.SSHChannel.write(self, data)
Exemple #25
0
class Command_tftp(HoneyPotCommand):
    port = 69
    hostname = None
    file_to_get = None
    limit_size = CowrieConfig.getint("honeypot",
                                     "download_limit_size",
                                     fallback=0)

    def makeTftpRetrieval(self):
        progresshook = Progress(self).progresshook

        self.artifactFile = Artifact(self.file_to_get)

        tclient = None
        url = ""

        try:
            tclient = tftpy.TftpClient(self.hostname, int(self.port))

            # tftpy can't handle unicode string as filename
            # so we have to convert unicode type to str type
            tclient.download(str(self.file_to_get), self.artifactFile,
                             progresshook)

            url = "tftp://{}/{}".format(self.hostname,
                                        self.file_to_get.strip("/"))

            self.file_to_get = self.fs.resolve_path(self.file_to_get,
                                                    self.protocol.cwd)

            if hasattr(tclient.context, "metrics"):
                self.fs.mkfile(self.file_to_get, 0, 0,
                               tclient.context.metrics.bytes, 33188)
            else:
                self.fs.mkfile(self.file_to_get, 0, 0, 0, 33188)

        except tftpy.TftpException:
            if tclient and tclient.context and not tclient.context.fileobj.closed:
                tclient.context.fileobj.close()

        if url:
            # log to cowrie.log
            log.msg(
                format=
                "Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s",
                url=url,
                outfile=self.artifactFile.shasumFilename,
                shasum=self.artifactFile.shasum,
            )

            self.protocol.logDispatch(
                eventid="cowrie.session.file_download",
                format=
                "Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s",
                url=url,
                outfile=self.artifactFile.shasumFilename,
                shasum=self.artifactFile.shasum,
                destfile=self.file_to_get,
            )

            # Update the honeyfs to point to downloaded file
            self.fs.update_realfile(self.fs.getfile(self.file_to_get),
                                    self.artifactFile.shasumFilename)
            self.fs.chown(self.file_to_get, self.protocol.user.uid,
                          self.protocol.user.gid)

    def start(self):
        parser = CustomParser(self)
        parser.prog = "tftp"
        parser.add_argument("hostname", nargs="?", default=None)
        parser.add_argument("-c", nargs=2)
        parser.add_argument("-l")
        parser.add_argument("-g")
        parser.add_argument("-p")
        parser.add_argument("-r")

        args = parser.parse_args(self.args)
        if args.c:
            if len(args.c) > 1:
                self.file_to_get = args.c[1]
                if args.hostname is None:
                    self.exit()
                    return
                self.hostname = args.hostname
        elif args.r:
            self.file_to_get = args.r
            self.hostname = args.g
        else:
            self.write(
                "usage: tftp [-h] [-c C C] [-l L] [-g G] [-p P] [-r R] [hostname]\n"
            )
            self.exit()
            return

        if self.hostname is None:
            self.exit()
            return

        if self.hostname.find(":") != -1:
            host, port = self.hostname.split(":")
            self.hostname = host
            self.port = int(port)

        self.makeTftpRetrieval()
        self.exit()
Exemple #26
0
class command_curl(HoneyPotCommand):
    """
    curl command
    """

    limit_size = CowrieConfig.getint("honeypot", "download_limit_size", fallback=0)
    download_path = CowrieConfig.get("honeypot", "download_path")

    def start(self):
        try:
            optlist, args = getopt.getopt(
                self.args, "sho:O", ["help", "manual", "silent"]
            )
        except getopt.GetoptError as err:
            # TODO: should be 'unknown' instead of 'not recognized'
            self.write(f"curl: {err}\n")
            self.write(
                "curl: try 'curl --help' or 'curl --manual' for more information\n"
            )
            self.exit()
            return

        for opt in optlist:
            if opt[0] == "-h" or opt[0] == "--help":
                self.write(CURL_HELP)
                self.exit()
                return
            elif opt[0] == "-s" or opt[0] == "--silent":
                self.silent = True

        if len(args):
            if args[0] is not None:
                url = str(args[0]).strip()
        else:
            self.write(
                "curl: try 'curl --help' or 'curl --manual' for more information\n"
            )
            self.exit()
            return

        if "://" not in url:
            url = "http://" + url
        urldata = compat.urllib_parse.urlparse(url)

        outfile = None
        for opt in optlist:
            if opt[0] == "-o":
                outfile = opt[1]
            if opt[0] == "-O":
                outfile = urldata.path.split("/")[-1]
                if (
                    outfile is None
                    or not len(outfile.strip())
                    or not urldata.path.count("/")
                ):
                    self.write("curl: Remote file name has no length!\n")
                    self.exit()
                    return

        if outfile:
            outfile = self.fs.resolve_path(outfile, self.protocol.cwd)
            path = os.path.dirname(outfile)
            if not path or not self.fs.exists(path) or not self.fs.isdir(path):
                self.write(
                    "curl: %s: Cannot open: No such file or directory\n" % outfile
                )
                self.exit()
                return

        url = url.encode("ascii")
        self.url = url

        self.artifactFile = Artifact(outfile)
        # HTTPDownloader will close() the file object so need to preserve the name

        self.deferred = self.download(url, outfile, self.artifactFile)
        if self.deferred:
            self.deferred.addCallback(self.success, outfile)
            self.deferred.addErrback(self.error, url)

    def download(self, url, fakeoutfile, outputfile, *args, **kwargs):
        scheme: bytes
        try:
            parsed = compat.urllib_parse.urlparse(url)
            scheme = parsed.scheme
            host: str = parsed.hostname.decode("utf8")
            port: int = parsed.port or (443 if scheme == "https" else 80)
            if scheme != b"http" and scheme != b"https":
                raise NotImplementedError
        except Exception:
            self.errorWrite(
                f'curl: (1) Protocol "{scheme.encode("utf8")}" not supported or disabled in libcurl\n'
            )
            self.exit()
            return None

        factory = HTTPProgressDownloader(
            self, fakeoutfile, url, outputfile, *args, **kwargs
        )
        out_addr = None
        if CowrieConfig.has_option("honeypot", "out_addr"):
            out_addr = (CowrieConfig.get("honeypot", "out_addr"), 0)

        if scheme == "https":
            context_factory = ssl.optionsForClientTLS(hostname=host)
            self.connection = reactor.connectSSL(
                host, port, factory, context_factory, bindAddress=out_addr
            )
        else:  # Can only be http
            self.connection = reactor.connectTCP(
                host, port, factory, bindAddress=out_addr
            )

        return factory.deferred

    def handle_CTRL_C(self):
        self.write("^C\n")
        self.connection.transport.loseConnection()

    def success(self, data, outfile):
        if not os.path.isfile(self.artifactFile.shasumFilename):
            log.msg("there's no file " + self.artifactFile.shasumFilename)
            self.exit()

        self.protocol.logDispatch(
            eventid="cowrie.session.file_download",
            format="Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s",
            url=self.url,
            outfile=self.artifactFile.shasumFilename,
            shasum=self.artifactFile.shasum,
        )

        # Update the honeyfs to point to downloaded file if output is a file
        if outfile:
            self.fs.update_realfile(
                self.fs.getfile(outfile), self.artifactFile.shasumFilename
            )
            self.fs.chown(outfile, self.protocol.user.uid, self.protocol.user.gid)
        else:
            with open(self.artifactFile.shasumFilename, "rb") as f:
                self.writeBytes(f.read())

        self.exit()

    def error(self, error, url):

        log.msg(error.printTraceback())
        if hasattr(error, "getErrorMessage"):  # Exceptions
            errormsg = error.getErrorMessage()
        log.msg(errormsg)
        self.write("\n")
        self.protocol.logDispatch(
            eventid="cowrie.session.file_download.failed",
            format="Attempt to download file(s) from URL (%(url)s) failed",
            url=self.url,
        )
        self.exit()
Exemple #27
0
    def makeService(self, options: Dict) -> service.Service:
        """
        Construct a TCPServer from a factory defined in Cowrie.
        """

        if options["help"] is True:
            print("""Usage: twistd [options] cowrie [-h]
Options:
  -h, --help             print this help message.

Makes a Cowrie SSH/Telnet honeypot.
""")
            sys.exit(1)

        if os.name == "posix" and os.getuid() == 0:
            print("ERROR: You must not run cowrie as root!")
            sys.exit(1)

        tz: str = CowrieConfig.get("honeypot", "timezone", fallback="UTC")
        # `system` means use the system time zone
        if tz != "system":
            os.environ["TZ"] = tz

        log.msg("Python Version {}".format(str(sys.version).replace("\n", "")))
        log.msg("Twisted Version {}.{}.{}".format(
            __twisted_version__.major,
            __twisted_version__.minor,
            __twisted_version__.micro,
        ))
        log.msg("Cowrie Version {}.{}.{}".format(
            __cowrie_version__.major,
            __cowrie_version__.minor,
            __cowrie_version__.micro,
        ))

        # check configurations
        if not self.enableTelnet and not self.enableSSH and not self.pool_only:
            print(
                "ERROR: You must at least enable SSH or Telnet, or run the backend pool"
            )
            sys.exit(1)

        # Load output modules
        self.output_plugins = []
        for x in CowrieConfig.sections():
            if not x.startswith("output_"):
                continue
            if CowrieConfig.getboolean(x, "enabled") is False:
                continue
            engine: str = x.split("_")[1]
            try:
                output = __import__(f"cowrie.output.{engine}", globals(),
                                    locals(), ["output"]).Output()
                log.addObserver(output.emit)
                self.output_plugins.append(output)
                log.msg(f"Loaded output engine: {engine}")
            except ImportError as e:
                log.err(
                    f"Failed to load output engine: {engine} due to ImportError: {e}"
                )
                log.msg(
                    f"Please install the dependencies for {engine} listed in requirements-output.txt"
                )
            except Exception:
                log.err()
                log.msg(f"Failed to load output engine: {engine}")

        self.topService = service.MultiService()
        application = service.Application("cowrie")
        self.topService.setServiceParent(application)

        # initialise VM pool handling - only if proxy AND pool set to enabled, and pool is to be deployed here
        # or also enabled if pool_only is true
        backend_type: str = CowrieConfig.get("honeypot",
                                             "backend",
                                             fallback="shell")
        proxy_backend: str = CowrieConfig.get("proxy",
                                              "backend",
                                              fallback="simple")

        if (backend_type == "proxy"
                and proxy_backend == "pool") or self.pool_only:
            # in this case we need to set some kind of pool connection

            local_pool: bool = (CowrieConfig.get("proxy",
                                                 "pool",
                                                 fallback="local") == "local")
            pool_host: str = CowrieConfig.get("proxy",
                                              "pool_host",
                                              fallback="127.0.0.1")
            pool_port: int = CowrieConfig.getint("proxy",
                                                 "pool_port",
                                                 fallback=6415)

            if local_pool or self.pool_only:
                # start a pool locally
                f = PoolServerFactory()
                f.tac = self

                listen_endpoints = get_endpoints_from_section(
                    CowrieConfig, "backend_pool", 6415)
                create_endpoint_services(reactor, self.topService,
                                         listen_endpoints, f)

                pool_host = "127.0.0.1"  # force use of local interface

            # either way (local or remote) we set up a client to the pool
            # unless this instance has no SSH and Telnet (pool only)
            if (self.enableTelnet or self.enableSSH) and not self.pool_only:
                self.pool_handler = PoolHandler(pool_host, pool_port,
                                                self)  # type: ignore

        else:
            # we initialise the services directly
            self.pool_ready()

        return self.topService
Exemple #28
0
class command_wget(HoneyPotCommand):
    """
    wget command
    """

    limit_size = CowrieConfig.getint("honeypot",
                                     "download_limit_size",
                                     fallback=0)
    downloadPath = CowrieConfig.get("honeypot", "download_path")

    def start(self):
        try:
            optlist, args = getopt.getopt(self.args, "cqO:P:", ["header="])
        except getopt.GetoptError:
            self.errorWrite("Unrecognized option\n")
            self.exit()
            return

        if len(args):
            url = args[0].strip()
        else:
            self.errorWrite("wget: missing URL\n")
            self.errorWrite("Usage: wget [OPTION]... [URL]...\n\n")
            self.errorWrite("Try `wget --help' for more options.\n")
            self.exit()
            return

        self.outfile = None
        self.quiet = False
        for opt in optlist:
            if opt[0] == "-O":
                self.outfile = opt[1]
            if opt[0] == "-q":
                self.quiet = True

        # for some reason getopt doesn't recognize "-O -"
        # use try..except for the case if passed command is malformed
        try:
            if not self.outfile:
                if "-O" in args:
                    self.outfile = args[args.index("-O") + 1]
        except Exception:
            pass

        if "://" not in url:
            url = "http://%s" % url

        urldata = compat.urllib_parse.urlparse(url)

        self.url = url.encode("utf8")

        if self.outfile is None:
            self.outfile = urldata.path.split("/")[-1]
            if not len(self.outfile.strip()) or not urldata.path.count("/"):
                self.outfile = "index.html"

        if self.outfile != "-":
            self.outfile = self.fs.resolve_path(self.outfile,
                                                self.protocol.cwd)
            path = os.path.dirname(self.outfile)
            if not path or not self.fs.exists(path) or not self.fs.isdir(path):
                self.errorWrite(
                    "wget: %s: Cannot open: No such file or directory\n" %
                    self.outfile)
                self.exit()
                return

        self.deferred = self.download(self.url, self.outfile)
        if self.deferred:
            self.deferred.addCallback(self.success)
            self.deferred.addErrback(self.error, self.url)
        else:
            self.exit()

    def download(self, url, fakeoutfile, *args, **kwargs):
        """
        url - URL to download
        fakeoutfile - file in guest's fs that attacker wants content to be downloaded to
        """
        try:
            parsed = compat.urllib_parse.urlparse(url)
            scheme = parsed.scheme
            host = parsed.hostname.decode("utf8")
            port = parsed.port or (443 if scheme == b"https" else 80)
            if scheme != b"http" and scheme != b"https":
                raise NotImplementedError
            if not host:
                return None
        except Exception:
            self.errorWrite(f"{url}: Unsupported scheme.\n")
            return None

        # File in host's fs that will hold content of the downloaded file
        # HTTPDownloader will close() the file object so need to preserve the name
        self.artifactFile = Artifact(self.outfile)

        if not self.quiet:
            self.errorWrite("--{}--  {}\n".format(
                time.strftime("%Y-%m-%d %H:%M:%S"), url.decode("utf8")))
            self.errorWrite("Connecting to %s:%d... connected.\n" %
                            (host, port))
            self.errorWrite("HTTP request sent, awaiting response... ")

        factory = HTTPProgressDownloader(self, fakeoutfile, url,
                                         self.artifactFile, *args, **kwargs)

        out_addr = None
        if CowrieConfig.has_option("honeypot", "out_addr"):
            out_addr = (CowrieConfig.get("honeypot", "out_addr"), 0)

        if scheme == b"https":
            context_factory = ssl.optionsForClientTLS(hostname=host)
            self.connection = reactor.connectSSL(host,
                                                 port,
                                                 factory,
                                                 context_factory,
                                                 bindAddress=out_addr)

        elif scheme == b"http":
            self.connection = reactor.connectTCP(host,
                                                 port,
                                                 factory,
                                                 bindAddress=out_addr)
        else:
            raise NotImplementedError

        return factory.deferred

    def handle_CTRL_C(self):
        self.errorWrite("^C\n")
        self.connection.transport.loseConnection()

    def success(self, data):
        if not os.path.isfile(self.artifactFile.shasumFilename):
            log.msg("there's no file " + self.artifactFile.shasumFilename)
            self.exit()

        # log to cowrie.log
        log.msg(
            format=
            "Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s",
            url=self.url,
            outfile=self.artifactFile.shasumFilename,
            shasum=self.artifactFile.shasum,
        )

        # log to output modules
        self.protocol.logDispatch(
            eventid="cowrie.session.file_download",
            format=
            "Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s",
            url=self.url,
            outfile=self.artifactFile.shasumFilename,
            shasum=self.artifactFile.shasum,
        )

        # Update honeyfs to point to downloaded file or write to screen
        if self.outfile != "-":
            self.fs.update_realfile(self.fs.getfile(self.outfile),
                                    self.artifactFile.shasumFilename)
            self.fs.chown(self.outfile, self.protocol.user.uid,
                          self.protocol.user.gid)
        else:
            with open(self.artifactFile.shasumFilename, "rb") as f:
                self.writeBytes(f.read())

        self.exit()

    def error(self, error, url):
        # we need to handle 301 redirects separately
        if hasattr(error, "webStatus") and error.webStatus.decode() == "301":
            self.errorWrite(
                f"{error.webStatus.decode()} {error.webMessage.decode()}\n")
            https_url = error.getErrorMessage().replace(
                "301 Moved Permanently to ", "")
            self.errorWrite(f"Location {https_url} [following]\n")

            # do the download again with the https URL
            self.deferred = self.download(https_url.encode("utf8"),
                                          self.outfile)
            if self.deferred:
                self.deferred.addCallback(self.success)
                self.deferred.addErrback(self.error, https_url)
            else:
                self.exit()
        else:
            if hasattr(error, "getErrorMessage"):  # exceptions
                errorMessage = error.getErrorMessage()
                self.errorWrite(errorMessage + "\n")
                # Real wget also adds this:
            if (hasattr(error, "webStatus") and error.webStatus
                    and hasattr(error, "webMessage")):  # exceptions
                self.errorWrite("{} ERROR {}: {}\n".format(
                    time.strftime("%Y-%m-%d %T"),
                    error.webStatus.decode(),
                    error.webMessage.decode("utf8"),
                ))
            else:
                self.errorWrite("{} ERROR 404: Not Found.\n".format(
                    time.strftime("%Y-%m-%d %T")))

            # prevent cowrie from crashing if the terminal have been already destroyed
            try:
                self.protocol.logDispatch(
                    eventid="cowrie.session.file_download.failed",
                    format=
                    "Attempt to download file(s) from URL (%(url)s) failed",
                    url=self.url,
                )
            except Exception:
                pass

            self.exit()
Exemple #29
0
class Command_curl(HoneyPotCommand):
    """
    curl command
    """

    limit_size: int = CowrieConfig.getint("honeypot", "download_limit_size", fallback=0)
    outfile: Optional[str] = None  # outfile is the file saved inside the honeypot
    artifact: Artifact  # artifact is the file saved for forensics in the real file system
    currentlength: int = 0  # partial size during download
    totallength: int = 0  # total length
    silent: bool = False
    url: bytes
    host: str

    def start(self) -> None:
        try:
            optlist, args = getopt.getopt(
                self.args, "sho:O", ["help", "manual", "silent"]
            )
        except getopt.GetoptError as err:
            # TODO: should be 'unknown' instead of 'not recognized'
            self.write(f"curl: {err}\n")
            self.write(
                "curl: try 'curl --help' or 'curl --manual' for more information\n"
            )
            self.exit()
            return

        for opt in optlist:
            if opt[0] == "-h" or opt[0] == "--help":
                self.write(CURL_HELP)
                self.exit()
                return
            elif opt[0] == "-s" or opt[0] == "--silent":
                self.silent = True

        if len(args):
            if args[0] is not None:
                url = str(args[0]).strip()
        else:
            self.write(
                "curl: try 'curl --help' or 'curl --manual' for more information\n"
            )
            self.exit()
            return

        if "://" not in url:
            url = "http://" + url
        urldata = compat.urllib_parse.urlparse(url)

        for opt in optlist:
            if opt[0] == "-o":
                self.outfile = opt[1]
            if opt[0] == "-O":
                self.outfile = urldata.path.split("/")[-1]
                if (
                    self.outfile is None
                    or not len(self.outfile.strip())
                    or not urldata.path.count("/")
                ):
                    self.write("curl: Remote file name has no length!\n")
                    self.exit()
                    return

        if self.outfile:
            self.outfile = self.fs.resolve_path(self.outfile, self.protocol.cwd)
            if self.outfile:
                path = os.path.dirname(self.outfile)
            if not path or not self.fs.exists(path) or not self.fs.isdir(path):
                self.write(
                    f"curl: {self.outfile}: Cannot open: No such file or directory\n"
                )
                self.exit()
                return

        self.url = url.encode("ascii")

        parsed = compat.urllib_parse.urlparse(url)
        if parsed.hostname:
            self.host = parsed.hostname
        if parsed.scheme:
            scheme = parsed.scheme
        # port: int = parsed.port or (443 if scheme == "https" else 80)
        if scheme != "http" and scheme != "https":
            self.errorWrite(
                f'curl: (1) Protocol "{scheme}" not supported or disabled in libcurl\n'
            )
            self.exit()
            return

        # TODO: need to do full name resolution in case someon passes DNS name pointing to local address
        try:
            if ipaddress.ip_address(self.host).is_private:
                self.errorWrite(f"curl: (6) Could not resolve host: {self.host}\n")
                self.exit()
                return None
        except ValueError:
            pass

        self.artifact = Artifact("curl-download")

        self.deferred = self.treqDownload(url)
        if self.deferred:
            self.deferred.addCallback(self.success)
            self.deferred.addErrback(self.error)

    def treqDownload(self, url):
        """
        Download `url`
        """
        headers = {"User-Agent": ["curl/7.38.0"]}

        # TODO: use designated outbound interface
        # out_addr = None
        # if CowrieConfig.has_option("honeypot", "out_addr"):
        #     out_addr = (CowrieConfig.get("honeypot", "out_addr"), 0)

        deferred = treq.get(url=url, allow_redirects=False, headers=headers, timeout=10)
        return deferred

    def handle_CTRL_C(self):
        self.write("^C\n")
        self.exit()

    def success(self, response):
        """
        successful treq get
        """
        self.totallength = response.length
        # TODO possible this is UNKNOWN_LENGTH
        if self.limit_size > 0 and self.totallength > self.limit_size:
            log.msg(
                f"Not saving URL ({self.url}) (size: {self.totallength}) exceeds file size limit ({self.limit_size})"
            )
            self.exit()
            return

        if self.outfile and not self.silent:
            self.write(
                "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n"
            )
            self.write(
                "                                 Dload  Upload   Total   Spent    Left  Speed\n"
            )

        deferred = treq.collect(response, self.collect)
        deferred.addCallback(self.collectioncomplete)
        return deferred

    def collect(self, data: bytes) -> None:
        """
        partial collect
        """
        self.currentlength += len(data)
        if self.limit_size > 0 and self.currentlength > self.limit_size:
            log.msg(
                f"Not saving URL ({self.url.decode()}) (size: {self.currentlength}) exceeds file size limit ({self.limit_size})"
            )
            self.exit()
            return

        self.artifact.write(data)

        if self.outfile and not self.silent:
            self.write(
                "\r100  {}  100  {}    0     0  {}      0 --:--:-- --:--:-- --:--:-- {}".format(
                    self.currentlength, self.currentlength, 63673, 65181
                )
            )

        if not self.outfile:
            self.writeBytes(data)

    def collectioncomplete(self, data: None) -> None:
        """
        this gets called once collection is complete
        """
        self.artifact.close()

        if self.outfile and not self.silent:
            self.write("\n")

        # Update the honeyfs to point to artifact file if output is to file
        if self.outfile:
            self.fs.mkfile(self.outfile, 0, 0, self.currentlength, 33188)
            self.fs.chown(self.outfile, self.protocol.user.uid, self.protocol.user.gid)
            self.fs.update_realfile(
                self.fs.getfile(self.outfile), self.artifact.shasumFilename
            )

        self.protocol.logDispatch(
            eventid="cowrie.session.file_download",
            format="Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(filename)s",
            url=self.url,
            filename=self.artifact.shasumFilename,
            shasum=self.artifact.shasum,
        )
        log.msg(
            eventid="cowrie.session.file_download",
            format="Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(filename)s",
            url=self.url,
            filename=self.artifact.shasumFilename,
            shasum=self.artifact.shasum,
        )
        self.exit()

    def error(self, response):
        """
        handle any exceptions
        """
        if response.check(error.DNSLookupError) is not None:
            self.write(f"curl: (6) Could not resolve host: {self.host}\n")
            self.exit()
            return

        # possible errors:
        # defer.CancelledError,
        # error.ConnectingCancelledError,

        log.msg(response.printTraceback())
        if hasattr(response, "getErrorMessage"):  # Exceptions
            errormsg = response.getErrorMessage()
        log.msg(errormsg)
        self.write("\n")
        self.protocol.logDispatch(
            eventid="cowrie.session.file_download.failed",
            format="Attempt to download file(s) from URL (%(url)s) failed",
            url=self.url,
        )
        self.exit()
Exemple #30
0
class LoggingServerProtocol(insults.ServerProtocol):
    """
    Wrapper for ServerProtocol that implements TTY logging
    """

    ttylogPath: str = CowrieConfig.get("honeypot", "ttylog_path")
    downloadPath: str = CowrieConfig.get("honeypot", "download_path")
    ttylogEnabled: bool = CowrieConfig.getboolean("honeypot",
                                                  "ttylog",
                                                  fallback=True)
    bytesReceivedLimit: int = CowrieConfig.getint("honeypot",
                                                  "download_limit_size",
                                                  fallback=0)

    def __init__(self, prot=None, *a, **kw):
        self.type: str
        self.ttylogSize: int = 0
        self.bytesReceived: int = 0
        self.redirFiles: Set[List[str]] = set()
        self.redirlogOpen: bool = False  # it will be set at core/protocol.py
        self.stdinlogOpen: bool = False
        self.ttylogOpen: bool = False
        self.terminalProtocol: Any
        self.transport: Any

        insults.ServerProtocol.__init__(self, prot, *a, **kw)

        if prot is protocol.HoneyPotExecProtocol:
            self.type = "e"  # Execcmd
        else:
            self.type = "i"  # Interactive

    def getSessionId(self):
        transportId = self.transport.session.conn.transport.transportId
        channelId = self.transport.session.id
        return (transportId, channelId)

    def connectionMade(self) -> None:
        transportId, channelId = self.getSessionId()
        self.startTime: float = time.time()

        if self.ttylogEnabled:
            self.ttylogFile = "{}/{}-{}-{}{}.log".format(
                self.ttylogPath,
                time.strftime("%Y%m%d-%H%M%S"),
                transportId,
                channelId,
                self.type,
            )
            ttylog.ttylog_open(self.ttylogFile, self.startTime)
            self.ttylogOpen = True
            self.ttylogSize = 0

        self.stdinlogFile = "{}/{}-{}-{}-stdin.log".format(
            self.downloadPath,
            time.strftime("%Y%m%d-%H%M%S"),
            transportId,
            channelId,
        )

        if self.type == "e":
            self.stdinlogOpen = True
            # log the command into ttylog
            if self.ttylogEnabled:
                (sess, cmd) = self.protocolArgs
                ttylog.ttylog_write(self.ttylogFile, len(cmd),
                                    ttylog.TYPE_INTERACT, time.time(), cmd)
        else:
            self.stdinlogOpen = False

        insults.ServerProtocol.connectionMade(self)

        if self.type == "e":
            self.terminalProtocol.execcmd.encode("utf8")

    def write(self, data: bytes) -> None:
        if self.ttylogEnabled and self.ttylogOpen:
            ttylog.ttylog_write(self.ttylogFile, len(data), ttylog.TYPE_OUTPUT,
                                time.time(), data)
            self.ttylogSize += len(data)

        insults.ServerProtocol.write(self, data)

    def dataReceived(self, data: bytes) -> None:
        """
        Input received from user
        """
        self.bytesReceived += len(data)
        if self.bytesReceivedLimit and self.bytesReceived > self.bytesReceivedLimit:
            log.msg(format="Data upload limit reached")
            self.eofReceived()
            return

        if self.stdinlogOpen:
            with open(self.stdinlogFile, "ab") as f:
                f.write(data)
        elif self.ttylogEnabled and self.ttylogOpen:
            ttylog.ttylog_write(self.ttylogFile, len(data), ttylog.TYPE_INPUT,
                                time.time(), data)

        # prevent crash if something like this was passed:
        # echo cmd ; exit; \n\n
        if self.terminalProtocol:
            insults.ServerProtocol.dataReceived(self, data)

    def eofReceived(self) -> None:
        """
        Receive channel close and pass on to terminal
        """
        if self.terminalProtocol:
            self.terminalProtocol.eofReceived()

    def loseConnection(self) -> None:
        """
        Override super to remove the terminal reset on logout
        """
        self.transport.loseConnection()

    def connectionLost(self, reason):
        """
        FIXME: this method is called 4 times on logout....
        it's called once from Avatar.closed() if disconnected
        """
        if self.stdinlogOpen:
            try:
                with open(self.stdinlogFile, "rb") as f:
                    shasum = hashlib.sha256(f.read()).hexdigest()
                    shasumfile = os.path.join(self.downloadPath, shasum)
                    if os.path.exists(shasumfile):
                        os.remove(self.stdinlogFile)
                        duplicate = True
                    else:
                        os.rename(self.stdinlogFile, shasumfile)
                        duplicate = False

                log.msg(
                    eventid="cowrie.session.file_download",
                    format=
                    "Saved stdin contents with SHA-256 %(shasum)s to %(outfile)s",
                    duplicate=duplicate,
                    outfile=shasumfile,
                    shasum=shasum,
                    destfile="",
                )
            except OSError:
                pass
            finally:
                self.stdinlogOpen = False

        if self.redirFiles:
            for rp in self.redirFiles:

                rf = rp[0]

                if rp[1]:
                    url = rp[1]
                else:
                    url = rf[rf.find("redir_") + len("redir_"):]

                try:
                    if not os.path.exists(rf):
                        continue

                    if os.path.getsize(rf) == 0:
                        os.remove(rf)
                        continue

                    with open(rf, "rb") as f:
                        shasum = hashlib.sha256(f.read()).hexdigest()
                        shasumfile = os.path.join(self.downloadPath, shasum)
                        if os.path.exists(shasumfile):
                            os.remove(rf)
                            duplicate = True
                        else:
                            os.rename(rf, shasumfile)
                            duplicate = False
                    log.msg(
                        eventid="cowrie.session.file_download",
                        format=
                        "Saved redir contents with SHA-256 %(shasum)s to %(outfile)s",
                        duplicate=duplicate,
                        outfile=shasumfile,
                        shasum=shasum,
                        destfile=url,
                    )
                except OSError:
                    pass
            self.redirFiles.clear()

        if self.ttylogEnabled and self.ttylogOpen:
            ttylog.ttylog_close(self.ttylogFile, time.time())
            self.ttylogOpen = False
            shasum = ttylog.ttylog_inputhash(self.ttylogFile)
            shasumfile = os.path.join(self.ttylogPath, shasum)

            if os.path.exists(shasumfile):
                duplicate = True
                os.remove(self.ttylogFile)
            else:
                duplicate = False
                os.rename(self.ttylogFile, shasumfile)
                umask = os.umask(0)
                os.umask(umask)
                os.chmod(shasumfile, 0o666 & ~umask)

            log.msg(
                eventid="cowrie.log.closed",
                format="Closing TTY Log: %(ttylog)s after %(duration)d seconds",
                ttylog=shasumfile,
                size=self.ttylogSize,
                shasum=shasum,
                duplicate=duplicate,
                duration=time.time() - self.startTime,
            )

        insults.ServerProtocol.connectionLost(self, reason)