* fix MySQL error handling
* fix tar command
* type hinting for proxy
This commit is contained in:
Michel Oosterhof 2021-05-14 15:43:28 +08:00 committed by GitHub
parent 988733210a
commit ec39aad0a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 172 additions and 135 deletions

View File

@ -176,7 +176,7 @@ Installing Backend Pool dependencies (OPTIONAL)
***********************************************
If you want to use the proxy functionality combined with the automatic
backend pool, you need to install some dependencies, namely qemu, libvirt,
backend pool, you need to install some dependencies, namely QEMU, libvirt,
and their Python interface. In Debian/Ubuntu::
$ sudo apt-get install qemu qemu-system-arm qemu-system-x86 libvirt-dev libvirt-daemon libvirt-daemon-system libvirt-clients nmap
@ -185,7 +185,7 @@ Then install the Python API to run the backend pool::
(cowrie-env) $ pip install libvirt-python==6.4.0
To allow Qemu to use disk images and snapshots, set it to run with the user and group of the user running the pool
To allow QEMU to use disk images and snapshots, set it to run with the user and group of the user running the pool
(usually called 'cowrie' too::
$ sudo vim /etc/libvirt/qemu.conf

View File

@ -22,7 +22,7 @@ lint:
.PHONY: clean
clean:
rm -rf _trial_temp build dist
rm -rf _trial_temp build dist src/_trial_temp src/Cowrie.egg-info
make -C docs clean
.PHONY: pre-commit

View File

@ -43,7 +43,7 @@ Features
* Or proxy SSH and telnet to another system
* Run as a pure telnet and ssh proxy with monitoring
* Or let Cowrie manage a pool of Qemu emualted servers to provide the systems to login to
* Or let Cowrie manage a pool of QEMU emulated servers to provide the systems to login to
For both settings:

View File

@ -135,8 +135,8 @@ A set of guest (VM) parameters can be defined as we explain below:
* **guest_image_path**: the base image upon which all VMs are created from
* **guest_hypervisor**: the hypervisor used; if you have an older machine or the emulated
architecture is different from the host one, then use software-based "qemu"; however,
if you are able to, use "kvm", it's **much** faster.
architecture is different from the host one, then use software-based "QEMU"; however,
if you are able to, use "KVM", it's **much** faster.
* **guest_memory**: memory assigned to the guest; choose a value considering the number
of guests you'll have running in total (``pool_max_vms``)

Binary file not shown.

View File

@ -1,43 +1,43 @@
qemu/libvirt Python examples to handle a guest
QEMU/libvirt Python examples to handle a guest
# Developer Guide
We'll start by looking at the classes that compose the Backend Pool, from "outermost" to the inner, specific classes.
## pool_server.py
The main interface of the backend pool is exposed as a TCP server in _pool\_server.py_. The protocol is a very simple
The main interface of the backend pool is exposed as a TCP server in _pool\_server.py_. The protocol is a very simple
wire protocol, always composed of an op-code, a status code (for responses), and any needed data thereafter.
## pool_service.py
The server interfaces exposes a producer-consumer infinite loop that runs on _pool\_service.py_.
The **producer** is an infinite loop started by the server, and runs every 5 seconds. It creates VMs up to the
configured limit, checks which VMs become available (by testing if they accept SSH and/or Telnet connections), and
The **producer** is an infinite loop started by the server, and runs every 5 seconds. It creates VMs up to the
configured limit, checks which VMs become available (by testing if they accept SSH and/or Telnet connections), and
destroys VMs that are no longer needed.
**Consumer** methods are called by server request, and basically involve requesting and freeing VMs. All operations on
shared data in the producer-consumer are guarded by a lock, since there may be concurrent requests. The lock protects
the _guests_ list, which contains references for each VM backend (in our case libvirt/qemu instances).
**Consumer** methods are called by server request, and basically involve requesting and freeing VMs. All operations on
shared data in the producer-consumer are guarded by a lock, since there may be concurrent requests. The lock protects
the _guests_ list, which contains references for each VM backend (in our case libvirt/QEMU instances).
Since we won't be dealing with a very large number of VMs (never more than 100, we find that a single simple lock is
Since we won't be dealing with a very large number of VMs (never more than 100, we find that a single simple lock is
enough.
The Pool Service expects to find a "backend service" with a given interface:
* A method to initialise the backend interface and environment (start_backend), stop it and destroy the current
* A method to initialise the backend interface and environment (start_backend), stop it and destroy the current
environment (stop_backend), and shutdown it permanently for the current execution (shutdown_backend).
* A method to create a new guest (create_guest)
* A method to destroy a guest (destroy_guest)
Currently the service supports a libvirt/qemu backend. However, by splitting the logic from generic guest handling /
interaction with main Cowrie, from the logic to create guests in a low-level perspective, we hope to ease development
Currently the service supports a libvirt/QEMU backend. However, by splitting the logic from generic guest handling /
interaction with main Cowrie, from the logic to create guests in a low-level perspective, we hope to ease development
of different kinds of backends in the future.
## libvirt classes
The main class for libvirt is _backend\_service.py_, and implements the interface discussed above. Guest, network and
The main class for libvirt is _backend\_service.py_, and implements the interface discussed above. Guest, network and
snapshot handlers deal with those specific components of libvirt's handling.
Initialising libvirt involves connecting to the running system daemon, creating a network filter to restrict guest's
Initialising libvirt involves connecting to the running system daemon, creating a network filter to restrict guest's
Internet access, and creating a "cowrie" network in libvirt.
Guest creation is started by creating a snapshot from the base qcow2 image defined in the configs, and instantiating
a guest from the XML provided. The Guest Handler replaces templates ("{guest_name}") with user configs for the wanted
Guest creation is started by creating a snapshot from the base qcow2 image defined in the configs, and instantiating
a guest from the XML provided. The Guest Handler replaces templates ("{guest_name}") with user configs for the wanted
guest. If the XML provided does not contain templates, then no replacement takes place, naturally.

View File

@ -1,5 +1,6 @@
# Copyright (c) 2019 Guilherme Borges <guilhermerosasborges@gmail.com>
# See the COPYRIGHT file for more information
import os
import random
import sys
@ -12,6 +13,8 @@ import backend_pool.libvirt.network_handler
import backend_pool.util
from cowrie.core.config import CowrieConfig
LIBVIRT_URI = "qemu:///system"
class LibvirtError(Exception):
pass
@ -23,11 +26,12 @@ class LibvirtBackendService:
import libvirt
# open connection to libvirt
self.conn = libvirt.open("qemu:///system")
self.conn = libvirt.open(LIBVIRT_URI)
if self.conn is None:
log.msg(
eventid="cowrie.backend_pool.qemu",
format="Failed to open connection to qemu:///system",
format="Failed to open connection to %(uri)s",
uri=LIBVIRT_URI,
)
raise LibvirtError()
@ -35,19 +39,19 @@ class LibvirtBackendService:
self.network = None
# signals backend is ready to be operated
self.ready = False
self.ready: bool = False
# table to associate IPs and MACs
seed = random.randint(0, sys.maxsize)
seed: int = random.randint(0, sys.maxsize)
self.network_table = backend_pool.util.generate_network_table(seed)
log.msg(
eventid="cowrie.backend_pool.qemu", format="Connection to Qemu established"
eventid="cowrie.backend_pool.qemu", format="Connection to QEMU established"
)
def start_backend(self):
"""
Initialises Qemu/libvirt environment needed to run guests. Namely starts networks and network filters.
Initialises QEMU/libvirt environment needed to run guests. Namely starts networks and network filters.
"""
# create a network filter
self.filter = backend_pool.libvirt.network_handler.create_filter(self.conn)
@ -62,7 +66,7 @@ class LibvirtBackendService:
def stop_backend(self):
log.msg(
eventid="cowrie.backend_pool.qemu", format="Doing Qemu clean shutdown..."
eventid="cowrie.backend_pool.qemu", format="Doing QEMU clean shutdown..."
)
self.ready = False
@ -74,7 +78,7 @@ class LibvirtBackendService:
log.msg(
eventid="cowrie.backend_pool.qemu",
format="Connection to Qemu closed successfully",
format="Connection to QEMU closed successfully",
)
def get_mac_ip(self, ip_tester):

View File

@ -19,18 +19,20 @@ def create_guest(connection, mac_address, guest_unique_id):
import libvirt
# get guest configurations
configuration_file = os.path.join(
configuration_file: str = os.path.join(
CowrieConfig.get(
"backend_pool", "config_files_path", fallback="share/pool_configs"
),
CowrieConfig.get("backend_pool", "guest_config", fallback="default_guest.xml"),
)
version_tag = CowrieConfig.get("backend_pool", "guest_tag", fallback="guest")
base_image = CowrieConfig.get("backend_pool", "guest_image_path")
hypervisor = CowrieConfig.get("backend_pool", "guest_hypervisor", fallback="qemu")
memory = CowrieConfig.getint("backend_pool", "guest_memory", fallback=128)
qemu_machine = CowrieConfig.get(
version_tag: str = CowrieConfig.get("backend_pool", "guest_tag", fallback="guest")
base_image: str = CowrieConfig.get("backend_pool", "guest_image_path")
hypervisor: str = CowrieConfig.get(
"backend_pool", "guest_hypervisor", fallback="qemu"
)
memory: int = CowrieConfig.getint("backend_pool", "guest_memory", fallback=128)
qemu_machine: str = CowrieConfig.get(
"backend_pool", "guest_qemu_machine", fallback="pc-q35-3.1"
)
@ -44,19 +46,21 @@ def create_guest(connection, mac_address, guest_unique_id):
os._exit(1)
# only in some cases, like wrt
kernel_image = CowrieConfig.get("backend_pool", "guest_kernel_image", fallback="")
kernel_image: str = CowrieConfig.get(
"backend_pool", "guest_kernel_image", fallback=""
)
# get a directory to save snapshots, even if temporary
try:
# guest configuration, to be read by qemu, needs an absolute path
snapshot_path = backend_pool.util.to_absolute_path(
snapshot_path: str = backend_pool.util.to_absolute_path(
CowrieConfig.get("backend_pool", "snapshot_path")
)
except NoOptionError:
snapshot_path = os.getcwd()
# create a disk snapshot to be used by the guest
disk_img = os.path.join(
disk_img: str = os.path.join(
snapshot_path, f"snapshot-{version_tag}-{guest_unique_id}.qcow2"
)

View File

@ -12,7 +12,7 @@ def create_filter(connection):
# lazy import to avoid exception if not using the backend_pool and libvirt not installed (#1185)
import libvirt
filter_file = os.path.join(
filter_file: str = os.path.join(
CowrieConfig.get(
"backend_pool", "config_files_path", fallback="share/pool_configs"
),
@ -39,7 +39,7 @@ def create_network(connection, network_table):
import libvirt
# TODO support more interfaces and therefore more IP space to allow > 253 guests
network_file = os.path.join(
network_file: str = os.path.join(
CowrieConfig.get(
"backend_pool", "config_files_path", fallback="share/pool_configs"
),
@ -50,8 +50,8 @@ def create_network(connection, network_table):
network_xml = backend_pool.util.read_file(network_file)
template_host = "<host mac='{mac_address}' name='{name}' ip='{ip_address}'/>\n"
hosts = ""
template_host: str = "<host mac='{mac_address}' name='{name}' ip='{ip_address}'/>\n"
hosts: str = ""
# generate a host entry for every possible guest in this network (253 entries)
it = iter(network_table)

View File

@ -51,9 +51,9 @@ class ServerProtocol(protocol.Protocol):
class ServerFactory(protocol.Factory):
def __init__(self, dst_ip, dst_port):
self.dst_ip = dst_ip
self.dst_port = dst_port
def __init__(self, dst_ip: str, dst_port: int) -> None:
self.dst_ip: str = dst_ip
self.dst_port: int = dst_port
def buildProtocol(self, addr):
return ServerProtocol(self.dst_ip, self.dst_port)

View File

@ -14,11 +14,15 @@ from cowrie.core.config import CowrieConfig
class PoolServer(Protocol):
def __init__(self, factory):
self.factory = factory
self.local_pool = CowrieConfig.get("proxy", "pool", fallback="local") == "local"
self.pool_only = CowrieConfig.getboolean(
self.local_pool: bool = (
CowrieConfig.get("proxy", "pool", fallback="local") == "local"
)
self.pool_only: bool = CowrieConfig.getboolean(
"backend_pool", "pool_only", fallback=False
)
self.use_nat = CowrieConfig.getboolean("backend_pool", "use_nat", fallback=True)
self.use_nat: bool = CowrieConfig.getboolean(
"backend_pool", "use_nat", fallback=True
)
if self.use_nat:
self.nat_public_ip = CowrieConfig.get("backend_pool", "nat_public_ip")

View File

@ -22,7 +22,7 @@ class PoolService:
VM States:
created -> available -> using -> used -> unavailable -> destroyed
created: initialised but not fully booted by Qemu
created: initialised but not fully booted by QEMU
available: can be requested
using: a client is connected, can be served for other clients from same ip
used: client disconnectec, but can still be served for its ip
@ -38,31 +38,35 @@ class PoolService:
self.nat_service = nat_service
self.guests = []
self.guest_id = 0
self.guest_id: int = 0
self.guest_lock = Lock()
# time in seconds between each loop iteration
self.loop_sleep_time = 5
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 = 2
self.vm_unused_timeout = 600
self.share_guests = True
self.max_vm: int = 2
self.vm_unused_timeout: int = 600
self.share_guests: bool = True
# file configs
self.ssh_port = CowrieConfig.getint(
self.ssh_port: int = CowrieConfig.getint(
"backend_pool", "guest_ssh_port", fallback=-1
)
self.telnet_port = CowrieConfig.getint(
self.telnet_port: int = CowrieConfig.getint(
"backend_pool", "guest_telnet_port", fallback=-1
)
self.local_pool = CowrieConfig.get("proxy", "pool", fallback="local") == "local"
self.pool_only = CowrieConfig.getboolean(
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 = CowrieConfig.getboolean("backend_pool", "use_nat", fallback=True)
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:
@ -72,7 +76,7 @@ class PoolService:
)
os._exit(1)
self.any_vm_up = False # TODO fix for no VM available
self.any_vm_up: bool = False # TODO fix for no VM available
def start_pool(self):
# cleanup older qemu objects
@ -124,7 +128,7 @@ class PoolService:
try:
self.qemu.stop_backend()
except libvirt.libvirtError:
print("Not connected to Qemu")
print("Not connected to QEMU")
def shutdown_pool(self):
# lazy import to avoid exception if not using the backend_pool and libvirt not installed (#1185)
@ -135,7 +139,7 @@ class PoolService:
try:
self.qemu.shutdown_backend()
except libvirt.libvirtError:
print("Not connected to Qemu")
print("Not connected to QEMU")
def restart_pool(self):
log.msg(
@ -188,7 +192,7 @@ class PoolService:
return has_ssh or has_telnet
# Producers
def __producer_mark_timed_out(self, guest_timeout):
def __producer_mark_timed_out(self, guest_timeout: int) -> None:
"""
Checks timed-out VMs and acquires lock to safely mark for deletion
"""

View File

@ -26,7 +26,7 @@ class CommandChannel(channel.SSHChannel):
def channelOpen(self, data):
self.conn.sendRequest(self, "exec", common.NS(self.command), wantReply=True)
def dataReceived(self, data):
def dataReceived(self, data: bytes) -> None:
self.data += data
def extReceived(self, dataType, data):

View File

@ -13,20 +13,21 @@ class TelnetConnectionError(Exception):
class TelnetClient(StatefulTelnetProtocol):
def __init__(self):
# output from server
self.response = b""
self.response: bytes = b""
# callLater instance to wait until we have stop getting output for some time
self.done_callback = None
def connectionMade(self):
self.command: bytes
def connectionMade(self):
"""
Set rawMode since we do not receive the login and password prompt in line mode.
We return to default line mode when we detect the prompt in the received data stream.
"""
self.setRawMode()
def rawDataReceived(self, bytes):
def rawDataReceived(self, data):
"""
The login and password prompt on some systems are not received in lineMode.
Therefore we do the authentication in raw mode and switch back to line mode
@ -38,17 +39,17 @@ class TelnetClient(StatefulTelnetProtocol):
else:
self.re_prompt = re.compile(self.factory.prompt.encode())
if re.search(br"([Ll]ogin:\s+$)", bytes):
if re.search(br"([Ll]ogin:\s+$)", data):
self.sendLine(self.factory.username.encode())
elif re.search(br"([Pp]assword:\s+$)", bytes):
elif re.search(br"([Pp]assword:\s+$)", data):
self.sendLine(self.factory.password.encode())
elif self.re_prompt.search(bytes):
elif self.re_prompt.search(data):
self.setLineMode()
# auth is done, send command to server
self.send_command(self.transport.factory.command)
def lineReceived(self, line):
def lineReceived(self, line: bytes) -> None:
# ignore data sent by server before command is sent
# ignore command echo from server
if not self.command or line == self.command:
@ -62,11 +63,11 @@ class TelnetClient(StatefulTelnetProtocol):
# start countdown to command done (when reached, consider the output was completely received and close)
if not self.done_callback:
self.done_callback = reactor.callLater(0.5, self.close)
self.done_callback = reactor.callLater(0.5, self.close) # type: ignore
else:
self.done_callback.reset(0.5)
def send_command(self, command):
def send_command(self, command: str) -> None:
"""
Sends a command via Telnet using line mode
"""
@ -113,13 +114,12 @@ class TelnetClientCommand:
def __init__(self, callback, prompt, command):
# callback to be called when execution is done
self.callback = callback
self.prompt = prompt
self.command = command
def connect(self, host, port, username, password):
# deferred to signal command and its output is done
done_deferred = defer.Deferred()
done_deferred: defer.Deferred = defer.Deferred()
# start connection to the Telnet server
factory = TelnetFactory(

View File

@ -18,8 +18,9 @@ class Command_tar(HoneyPotCommand):
l, d = path.split("/"), []
while len(l):
d.append(l.pop(0))
if not self.fs.exists("/".join(d)):
self.fs.mkdir("/".join(d), 0, 0, 4096, f.mode, f.mtime)
p = "/".join(d)
if p and not self.fs.exists(p):
self.fs.mkdir(p, 0, 0, 4096, f.mode, f.mtime)
def call(self):
if len(self.args) < 2:

View File

@ -68,12 +68,20 @@ def convert(input):
"""
This converts a nested dictionary with bytes in it to string
"""
if isinstance(input, str):
return input
if isinstance(input, dict):
return {convert(key): convert(value) for key, value in list(input.items())}
if isinstance(input, dict):
return {convert(key): convert(value) for key, value in list(input.items())}
elif isinstance(input, list):
return [convert(element) for element in input]
elif isinstance(input, bytes):
return input.decode("utf-8")
try:
string = input.decode("utf-8")
except UnicodeDecodeError:
string = repr(input)
return string
else:
return input

View File

@ -65,7 +65,7 @@ class Output(cowrie.core.output.Output):
use_unicode=True,
)
except (MySQLdb.Error, MySQLdb._exceptions.Error) as e:
log.msg("output_mysql: Error %d: %s" % (e.args[0], e.args[1]))
log.msg(f"output_mysql: Error {e.args[0]}: {e.args[1]}")
def stop(self):
self.db.commit()
@ -76,18 +76,18 @@ class Output(cowrie.core.output.Output):
1146, "Table '...' doesn't exist"
1406, "Data too long for column '...' at row ..."
"""
if error.value[0] in (1146, 1406):
log.msg(f"output_mysql: MySQL Error: {error.value}")
if error.value.args[0] in (1146, 1406):
log.msg(f"output_mysql: MySQL Error: {error.value.args!r}")
log.msg("MySQL schema maybe misconfigured, doublecheck database!")
else:
log.err(f"output_mysql: MySQL Error: {error.value}")
log.msg(f"output_mysql: MySQL Error: {error.value.args!r}")
def simpleQuery(self, sql, args):
"""
Just run a deferred sql query, only care about errors
"""
if self.debug:
log.msg(f"output_mysql: MySQL query: {sql} {repr(args)}")
log.msg(f"output_mysql: MySQL query: {sql} {args!r}")
d = self.db.runQuery(sql, args)
d.addErrback(self.sqlerror)
@ -95,14 +95,14 @@ class Output(cowrie.core.output.Output):
def write(self, entry):
if entry["eventid"] == "cowrie.session.connect":
r = yield self.db.runQuery(
"SELECT `id`" "FROM `sensors`" "WHERE `ip` = %s", (self.sensor,)
f"SELECT `id`\" \"FROM `sensors`\" \"WHERE `ip` = {self.sensor}"
)
if r:
sensorid = r[0][0]
else:
yield self.db.runQuery(
"INSERT INTO `sensors` (`ip`) " "VALUES (%s)", (self.sensor,)
f"INSERT INTO `sensors` (`ip`) \" \"VALUES ({self.sensor})"
)
r = yield self.db.runQuery("SELECT LAST_INSERT_ID()")

View File

@ -11,7 +11,7 @@ from cowrie.core.config import CowrieConfig
class PoolClient(Protocol):
"""
Represents the connection between a protocol instance (SSH or Telnet) and a Qemu pool
Represents the connection between a protocol instance (SSH or Telnet) and a QEMU pool
"""
def __init__(self, factory):

View File

@ -29,7 +29,9 @@ def cowrieOpenConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avata
)
# Forward redirect
redirectEnabled: bool = CowrieConfig.getboolean("ssh", "forward_redirect", fallback=False)
redirectEnabled: bool = CowrieConfig.getboolean(
"ssh", "forward_redirect", fallback=False
)
if redirectEnabled:
redirects = {}
items = CowrieConfig.items("ssh")
@ -56,7 +58,9 @@ def cowrieOpenConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avata
)
# TCP tunnel
tunnelEnabled: bool = CowrieConfig.getboolean("ssh", "forward_tunnel", fallback=False)
tunnelEnabled: bool = CowrieConfig.getboolean(
"ssh", "forward_tunnel", fallback=False
)
if tunnelEnabled:
tunnels = {}
items = CowrieConfig.items("ssh")

View File

@ -1,6 +1,8 @@
# Copyright (c) 2019 Guilherme Borges <guilhermerosasborges@gmail.com>
# All rights reserved.
from typing import Any
from twisted.conch.ssh import transport
from twisted.internet import defer, protocol
from twisted.protocols.policies import TimeoutMixin
@ -173,5 +175,8 @@ class BackendSSHTransport(transport.SSHClientTransport, TimeoutMixin):
class BackendSSHFactory(protocol.ClientFactory):
server: Any
def buildProtocol(self, addr):
return BackendSSHTransport(self)

View File

@ -28,10 +28,10 @@
class BaseProtocol:
data = b""
packetSize = 0
name = ""
uuid = ""
data: bytes = b""
packetSize: int = 0
name: str = ""
uuid: str = ""
ttylog_file = None
def __init__(self, uuid=None, name=None, ssh=None):
@ -52,32 +52,32 @@ class BaseProtocol:
def channel_closed(self):
pass
def extract_int(self, length):
def extract_int(self, length: int) -> int:
value = int.from_bytes(self.data[:length], byteorder="big")
self.packetSize = self.packetSize - length
self.data = self.data[length:]
return value
def put_int(self, number):
def put_int(self, number: int) -> bytes:
return number.to_bytes(4, byteorder="big")
def extract_string(self):
def extract_string(self) -> bytes:
length = self.extract_int(4)
value = self.data[:length]
self.packetSize -= length
self.data = self.data[length:]
return value
def extract_bool(self):
def extract_bool(self) -> bool:
value = self.extract_int(1)
return bool(value)
def extract_data(self):
def extract_data(self) -> bytes:
length = self.extract_int(4)
self.packetSize = length
value = self.data
self.packetSize -= len(value)
self.data = ""
self.data = b""
return value
def __deepcopy__(self, memo):

View File

@ -52,12 +52,12 @@ class ExecTerm(base_protocol.BaseProtocol):
self.transportId = ssh.server.transportId
self.channelId = channelId
self.startTime = time.time()
self.ttylogPath = CowrieConfig.get("honeypot", "ttylog_path")
self.ttylogEnabled = CowrieConfig.getboolean(
self.startTime: float = time.time()
self.ttylogPath: str = CowrieConfig.get("honeypot", "ttylog_path")
self.ttylogEnabled: bool = CowrieConfig.getboolean(
"honeypot", "ttylog", fallback=True
)
self.ttylogSize = 0
self.ttylogSize: bool = 0
if self.ttylogEnabled:
self.ttylogFile = "{}/{}-{}-{}e.log".format(

View File

@ -26,20 +26,22 @@
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
from typing import Optional
from twisted.python import log
from cowrie.ssh_proxy.protocols import base_protocol
class SFTP(base_protocol.BaseProtocol):
prevID = ""
ID = ""
handle = ""
path = ""
command = ""
payloadSize = 0
payloadOffset = 0
theFile = ""
prevID: str = ""
ID: str = ""
handle: str = ""
path: str = ""
command: str = ""
payloadSize: int = 0
payloadOffset: int = 0
theFile: str = ""
packetLayout = {
1: "SSH_FXP_INIT",
@ -83,11 +85,11 @@ class SFTP(base_protocol.BaseProtocol):
self.clientPacket = base_protocol.BaseProtocol()
self.serverPacket = base_protocol.BaseProtocol()
self.parent = None
self.parent: Optional[str] = None
self.parentPacket = None
self.offset = 0
self.offset: int = 0
def parse_packet(self, parent, payload):
def parse_packet(self, parent: str, payload: bytes) -> None:
self.parent = parent
if parent == "[SERVER]":
@ -99,7 +101,7 @@ class SFTP(base_protocol.BaseProtocol):
self.parentPacket.packetSize = int(payload[:4].hex(), 16) - len(payload[4:])
payload = payload[4:]
self.parentPacket.data = payload
payload = ""
payload = b""
else:
if len(payload) > self.parentPacket.packetSize:
@ -111,7 +113,7 @@ class SFTP(base_protocol.BaseProtocol):
else:
self.parentPacket.packetSize -= len(payload)
self.parentPacket.data = self.parentPacket.data + payload
payload = ""
payload = b""
if self.parentPacket.packetSize == 0:
self.handle_packet(parent)
@ -120,11 +122,12 @@ class SFTP(base_protocol.BaseProtocol):
self.parse_packet(parent, payload)
def handle_packet(self, parent):
self.packetSize = self.parentPacket.packetSize
self.data = self.parentPacket.data
self.packetSize: int = self.parentPacket.packetSize
self.data: bytes = self.parentPacket.data
self.command: bytes
sftp_num = self.extract_int(1)
packet = self.packetLayout[sftp_num]
sftp_num: int = self.extract_int(1)
packet: str = self.packetLayout[sftp_num]
self.prevID = self.ID
self.ID = self.extract_int(4)
@ -252,8 +255,8 @@ class SFTP(base_protocol.BaseProtocol):
)
def extract_attrs(self):
cmd = ""
flags = f"{self.extract_int(4):08b}"
cmd: bytes = ""
flags: str = f"{self.extract_int(4):08b}"
if flags[5] == "1":
perms = f"{self.extract_int(4):09b}"

View File

@ -40,20 +40,20 @@ class Term(base_protocol.BaseProtocol):
def __init__(self, uuid, chan_name, ssh, channelId):
super().__init__(uuid, chan_name, ssh)
self.command = b""
self.pointer = 0
self.tabPress = False
self.upArrow = False
self.command: bytes = b""
self.pointer: int = 0
self.tabPress: bool = False
self.upArrow: bool = False
self.transportId = ssh.server.transportId
self.channelId = channelId
self.transportId: int = ssh.server.transportId
self.channelId: int = channelId
self.startTime = time.time()
self.ttylogPath = CowrieConfig.get("honeypot", "ttylog_path")
self.ttylogEnabled = CowrieConfig.getboolean(
self.startTime: float = time.time()
self.ttylogPath: str = CowrieConfig.get("honeypot", "ttylog_path")
self.ttylogEnabled: bool = CowrieConfig.getboolean(
"honeypot", "ttylog", fallback=True
)
self.ttylogSize = 0
self.ttylogSize: int = 0
if self.ttylogEnabled:
self.ttylogFile = "{}/{}-{}-{}i.log".format(
@ -61,7 +61,7 @@ class Term(base_protocol.BaseProtocol):
)
ttylog.ttylog_open(self.ttylogFile, self.startTime)
def channel_closed(self):
def channel_closed(self) -> None:
if self.ttylogEnabled:
ttylog.ttylog_close(self.ttylogFile, time.time())
shasum = ttylog.ttylog_inputhash(self.ttylogFile)
@ -87,8 +87,8 @@ class Term(base_protocol.BaseProtocol):
duration=time.time() - self.startTime,
)
def parse_packet(self, parent, payload):
self.data = payload
def parse_packet(self, parent: str, payload: bytes) -> None:
self.data: bytes = payload
if parent == "[SERVER]":
while len(self.data) > 0:
@ -196,7 +196,7 @@ class Term(base_protocol.BaseProtocol):
elif self.data[:3] == b"\x1b\x5b\x43":
self.pointer += 1
self.data = self.data[3:]
elif self.data[:2] == b"\x1b\x5b" and self.data[3] == b"\x50":
elif self.data[:2] == b"\x1b\x5b" and self.data[3:3] == b"\x50":
self.data = self.data[4:]
# Needed?!
elif self.data[:1] != b"\x07" and self.data[:1] != b"\x0d":