mirror of
https://github.com/telekom-security/tpotce.git
synced 2026-05-29 17:24:15 +00:00
Enhance Cowrie persona support by adding package manager handling and skip command filtering in protocol.py
This commit is contained in:
parent
aefe3c7dac
commit
e1d6d376dc
3 changed files with 351 additions and 3 deletions
|
|
@ -433,6 +433,7 @@ assert_custom_filesystem() {
|
||||||
docker exec -i "${TEST_CONTAINER_NAME}" python3 - <<'PY'
|
docker exec -i "${TEST_CONTAINER_NAME}" python3 - <<'PY'
|
||||||
import configparser
|
import configparser
|
||||||
import json
|
import json
|
||||||
|
import pickle
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
@ -441,7 +442,40 @@ personas_root = root / "personas"
|
||||||
metadata_path = personas_root / "personas.json"
|
metadata_path = personas_root / "personas.json"
|
||||||
selected_path = Path("/tmp/cowrie/persona")
|
selected_path = Path("/tmp/cowrie/persona")
|
||||||
runtime_config_path = Path("/tmp/cowrie/runtime/cowrie.cfg")
|
runtime_config_path = Path("/tmp/cowrie/runtime/cowrie.cfg")
|
||||||
|
protocol_path = root / "src" / "cowrie" / "shell" / "protocol.py"
|
||||||
offenders = []
|
offenders = []
|
||||||
|
expected_package_managers = {
|
||||||
|
"debian-bookworm-vuln": {"apt", "apt-get", "dpkg"},
|
||||||
|
"fedora-36-vuln": {"dnf", "rpm", "yum"},
|
||||||
|
"rhel-9-vuln": {"dnf", "rpm", "yum"},
|
||||||
|
"dlink-dir859": set(),
|
||||||
|
"tplink-wr841n": set(),
|
||||||
|
"zyxel-nas326": set(),
|
||||||
|
"openwrt-1806": {"opkg"},
|
||||||
|
"qnap-qts": {"qpkg_cli"},
|
||||||
|
"synology-dsm": {"synopkg"},
|
||||||
|
"ubiquiti-edgerouter-x": {"apt-get", "dpkg"},
|
||||||
|
}
|
||||||
|
package_manager_paths = (
|
||||||
|
"bin/apt",
|
||||||
|
"bin/apt-get",
|
||||||
|
"bin/dnf",
|
||||||
|
"bin/opkg",
|
||||||
|
"bin/rpm",
|
||||||
|
"bin/yum",
|
||||||
|
"sbin/opkg",
|
||||||
|
"sbin/qpkg_cli",
|
||||||
|
"usr/bin/apt",
|
||||||
|
"usr/bin/apt-get",
|
||||||
|
"usr/bin/dnf",
|
||||||
|
"usr/bin/dpkg",
|
||||||
|
"usr/bin/opkg",
|
||||||
|
"usr/bin/rpm",
|
||||||
|
"usr/bin/yum",
|
||||||
|
"usr/sbin/opkg",
|
||||||
|
"usr/sbin/qpkg_cli",
|
||||||
|
"usr/syno/bin/synopkg",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def read_bytes(path):
|
def read_bytes(path):
|
||||||
|
|
@ -474,12 +508,29 @@ def check_forbidden(path):
|
||||||
offenders.append(str(path))
|
offenders.append(str(path))
|
||||||
|
|
||||||
|
|
||||||
|
def node_children(node):
|
||||||
|
return node[7] if len(node) > 7 and isinstance(node[7], list) else []
|
||||||
|
|
||||||
|
|
||||||
|
def find_node(root_node, relative_path):
|
||||||
|
current = root_node
|
||||||
|
for part in Path(relative_path).parts:
|
||||||
|
if part in ("", "/"):
|
||||||
|
continue
|
||||||
|
current = next((child for child in node_children(current) if child[0] == part), None)
|
||||||
|
if current is None:
|
||||||
|
return None
|
||||||
|
return current
|
||||||
|
|
||||||
|
|
||||||
if not metadata_path.is_file():
|
if not metadata_path.is_file():
|
||||||
fail(f"Missing Cowrie persona metadata: {metadata_path}")
|
fail(f"Missing Cowrie persona metadata: {metadata_path}")
|
||||||
if not selected_path.is_file():
|
if not selected_path.is_file():
|
||||||
fail(f"Missing selected Cowrie persona file: {selected_path}")
|
fail(f"Missing selected Cowrie persona file: {selected_path}")
|
||||||
if not runtime_config_path.is_file():
|
if not runtime_config_path.is_file():
|
||||||
fail(f"Missing runtime Cowrie config: {runtime_config_path}")
|
fail(f"Missing runtime Cowrie config: {runtime_config_path}")
|
||||||
|
if "skip_python_commands" not in protocol_path.read_text(encoding="utf-8"):
|
||||||
|
fail("Cowrie protocol.py does not contain persona command filtering patch")
|
||||||
|
|
||||||
personas = json.loads(metadata_path.read_text(encoding="utf-8"))
|
personas = json.loads(metadata_path.read_text(encoding="utf-8"))
|
||||||
if len(personas) != 10:
|
if len(personas) != 10:
|
||||||
|
|
@ -508,6 +559,8 @@ if config.get("honeypot", "txtcmds_path", fallback="") != str(expected_txtcmds):
|
||||||
fail("Runtime config does not point to the selected txtcmds")
|
fail("Runtime config does not point to the selected txtcmds")
|
||||||
if config.get("ssh", "version", fallback="") != ids[selected]["ssh_banner"]:
|
if config.get("ssh", "version", fallback="") != ids[selected]["ssh_banner"]:
|
||||||
fail("Runtime config SSH banner does not match selected persona metadata")
|
fail("Runtime config SSH banner does not match selected persona metadata")
|
||||||
|
if "apt" not in config.get("shell", "skip_python_commands", fallback=""):
|
||||||
|
fail("Runtime config does not disable generic package-manager Python commands")
|
||||||
|
|
||||||
for persona_id, persona in ids.items():
|
for persona_id, persona in ids.items():
|
||||||
persona_dir = personas_root / persona_id
|
persona_dir = personas_root / persona_id
|
||||||
|
|
@ -531,6 +584,8 @@ for persona_id, persona in ids.items():
|
||||||
fail(f"{persona_id} fs.pickle is unexpectedly small: {pickle_path.stat().st_size} bytes")
|
fail(f"{persona_id} fs.pickle is unexpectedly small: {pickle_path.stat().st_size} bytes")
|
||||||
|
|
||||||
pickle_bytes = read_bytes(pickle_path)
|
pickle_bytes = read_bytes(pickle_path)
|
||||||
|
with pickle_path.open("rb") as handle:
|
||||||
|
pickle_tree = pickle.load(handle)
|
||||||
if persona["user"].encode("utf-8") not in pickle_bytes:
|
if persona["user"].encode("utf-8") not in pickle_bytes:
|
||||||
fail(f"{persona_id} fs.pickle does not contain persona user {persona['user']}")
|
fail(f"{persona_id} fs.pickle does not contain persona user {persona['user']}")
|
||||||
if persona["hostname"].encode("utf-8") not in pickle_bytes:
|
if persona["hostname"].encode("utf-8") not in pickle_bytes:
|
||||||
|
|
@ -540,6 +595,12 @@ for persona_id, persona in ids.items():
|
||||||
cmdoutput = json.loads(cmdoutput_path.read_text(encoding="utf-8"))
|
cmdoutput = json.loads(cmdoutput_path.read_text(encoding="utf-8"))
|
||||||
if not cmdoutput.get("command", {}).get("ps"):
|
if not cmdoutput.get("command", {}).get("ps"):
|
||||||
fail(f"{persona_id} cmdoutput.json has no ps process list")
|
fail(f"{persona_id} cmdoutput.json has no ps process list")
|
||||||
|
config_text = config_path.read_text(encoding="utf-8")
|
||||||
|
if "skip_python_commands =" not in config_text:
|
||||||
|
fail(f"{persona_id} config does not define skip_python_commands")
|
||||||
|
package_managers = set(persona.get("package_managers", []))
|
||||||
|
if package_managers != expected_package_managers[persona_id]:
|
||||||
|
fail(f"{persona_id} package managers do not match persona: {sorted(package_managers)}")
|
||||||
for command_path in (
|
for command_path in (
|
||||||
"bin/df",
|
"bin/df",
|
||||||
"bin/dmesg",
|
"bin/dmesg",
|
||||||
|
|
@ -551,6 +612,15 @@ for persona_id, persona in ids.items():
|
||||||
):
|
):
|
||||||
if not (txtcmds_path / command_path).is_file():
|
if not (txtcmds_path / command_path).is_file():
|
||||||
fail(f"{persona_id} txtcmds is missing {command_path}")
|
fail(f"{persona_id} txtcmds is missing {command_path}")
|
||||||
|
for command_path in package_manager_paths:
|
||||||
|
manager_name = Path(command_path).name
|
||||||
|
should_exist = manager_name in package_managers
|
||||||
|
exists_in_pickle = find_node(pickle_tree, command_path) is not None
|
||||||
|
exists_in_txtcmds = (txtcmds_path / command_path).is_file()
|
||||||
|
if should_exist and exists_in_txtcmds and not exists_in_pickle:
|
||||||
|
fail(f"{persona_id} package manager is missing from fs.pickle: {command_path}")
|
||||||
|
if not should_exist and (exists_in_pickle or exists_in_txtcmds):
|
||||||
|
fail(f"{persona_id} has mismatched package manager command: {command_path}")
|
||||||
|
|
||||||
passwd = read_bytes(honeyfs / "etc" / "passwd")
|
passwd = read_bytes(honeyfs / "etc" / "passwd")
|
||||||
hostname = read_bytes(honeyfs / "etc" / "hostname")
|
hostname = read_bytes(honeyfs / "etc" / "hostname")
|
||||||
|
|
|
||||||
231
docker/cowrie/dist/generate_cowrie_fs.py
vendored
231
docker/cowrie/dist/generate_cowrie_fs.py
vendored
|
|
@ -34,6 +34,70 @@ TXTCMD_PATHS = [
|
||||||
"usr/bin/top",
|
"usr/bin/top",
|
||||||
]
|
]
|
||||||
RANDOM = random.SystemRandom()
|
RANDOM = random.SystemRandom()
|
||||||
|
PACKAGE_MANAGER_PATHS = [
|
||||||
|
"bin/apt",
|
||||||
|
"bin/apt-get",
|
||||||
|
"bin/dnf",
|
||||||
|
"bin/opkg",
|
||||||
|
"bin/rpm",
|
||||||
|
"bin/yum",
|
||||||
|
"sbin/opkg",
|
||||||
|
"sbin/qpkg_cli",
|
||||||
|
"usr/bin/apt",
|
||||||
|
"usr/bin/apt-get",
|
||||||
|
"usr/bin/dnf",
|
||||||
|
"usr/bin/dpkg",
|
||||||
|
"usr/bin/opkg",
|
||||||
|
"usr/bin/rpm",
|
||||||
|
"usr/bin/yum",
|
||||||
|
"usr/sbin/opkg",
|
||||||
|
"usr/sbin/qpkg_cli",
|
||||||
|
"usr/syno/bin/synopkg",
|
||||||
|
]
|
||||||
|
HIGH_VARIANCE_COMMAND_PATHS = [
|
||||||
|
"bin/busybox",
|
||||||
|
"usr/bin/gcc",
|
||||||
|
"usr/bin/git",
|
||||||
|
"usr/bin/lspci",
|
||||||
|
"usr/bin/perl",
|
||||||
|
"usr/bin/python",
|
||||||
|
"usr/bin/python3",
|
||||||
|
"usr/bin/sudo",
|
||||||
|
"usr/sbin/service",
|
||||||
|
]
|
||||||
|
COMMON_SKIP_PYTHON_COMMANDS = [
|
||||||
|
"/bin/apt",
|
||||||
|
"/bin/apt-get",
|
||||||
|
"/bin/busybox",
|
||||||
|
"/bin/yum",
|
||||||
|
"/usr/bin/apt",
|
||||||
|
"/usr/bin/apt-get",
|
||||||
|
"/usr/bin/busybox",
|
||||||
|
"/usr/bin/gcc",
|
||||||
|
"/usr/bin/lspci",
|
||||||
|
"/usr/bin/sudo",
|
||||||
|
"/usr/bin/yum",
|
||||||
|
"/usr/sbin/service",
|
||||||
|
"apt",
|
||||||
|
"apt-get",
|
||||||
|
"busybox",
|
||||||
|
"gcc",
|
||||||
|
"lspci",
|
||||||
|
"sudo",
|
||||||
|
"yum",
|
||||||
|
"service",
|
||||||
|
]
|
||||||
|
EMBEDDED_SKIP_PYTHON_COMMANDS = [
|
||||||
|
"adduser",
|
||||||
|
"finger",
|
||||||
|
"git",
|
||||||
|
"groups",
|
||||||
|
"perl",
|
||||||
|
"python",
|
||||||
|
"python3",
|
||||||
|
"scp",
|
||||||
|
"systemctl",
|
||||||
|
]
|
||||||
|
|
||||||
A_NAME = 0
|
A_NAME = 0
|
||||||
A_TYPE = 1
|
A_TYPE = 1
|
||||||
|
|
@ -261,6 +325,144 @@ def random_ps_start():
|
||||||
return day.strftime("%b%d")
|
return day.strftime("%b%d")
|
||||||
|
|
||||||
|
|
||||||
|
def package_manager_txtcmds(persona):
|
||||||
|
family = persona["family"]
|
||||||
|
if family == "debian":
|
||||||
|
apt_version = "apt 2.6.1 (amd64)\n"
|
||||||
|
apt_help = """apt 2.6.1 (amd64)
|
||||||
|
Usage: apt [options] command
|
||||||
|
|
||||||
|
apt is a commandline package manager and provides commands for
|
||||||
|
searching and managing as well as querying information about packages.
|
||||||
|
|
||||||
|
Most used commands:
|
||||||
|
list - list packages based on package names
|
||||||
|
search - search in package descriptions
|
||||||
|
show - show package details
|
||||||
|
update - update list of available packages
|
||||||
|
install - install packages
|
||||||
|
remove - remove packages
|
||||||
|
upgrade - upgrade the system by installing/upgrading packages
|
||||||
|
"""
|
||||||
|
dpkg_version = "Debian 'dpkg' package management program version 1.21.22 (amd64).\n"
|
||||||
|
return {
|
||||||
|
"usr/bin/apt": apt_help,
|
||||||
|
"usr/bin/apt-get": apt_version,
|
||||||
|
"usr/bin/dpkg": dpkg_version,
|
||||||
|
}
|
||||||
|
if family == "fedora":
|
||||||
|
dnf = """dnf 4.14.0
|
||||||
|
usage: dnf [options] COMMAND
|
||||||
|
|
||||||
|
List of Main Commands:
|
||||||
|
install install a package or packages on your system
|
||||||
|
remove remove a package or packages from your system
|
||||||
|
upgrade upgrade a package or packages on your system
|
||||||
|
search search package details for the given string
|
||||||
|
repolist display the configured software repositories
|
||||||
|
"""
|
||||||
|
rpm = "RPM version 4.17.0\n"
|
||||||
|
return {
|
||||||
|
"usr/bin/dnf": dnf,
|
||||||
|
"usr/bin/yum": dnf,
|
||||||
|
"usr/bin/rpm": rpm,
|
||||||
|
}
|
||||||
|
if family == "rhel":
|
||||||
|
dnf = """dnf 4.14.0
|
||||||
|
usage: dnf [options] COMMAND
|
||||||
|
|
||||||
|
List of Main Commands:
|
||||||
|
install install a package or packages on your system
|
||||||
|
remove remove a package or packages from your system
|
||||||
|
upgrade upgrade a package or packages on your system
|
||||||
|
repolist display the configured software repositories
|
||||||
|
module interact with modular content
|
||||||
|
"""
|
||||||
|
rpm = "RPM version 4.16.1.3\n"
|
||||||
|
return {
|
||||||
|
"usr/bin/dnf": dnf,
|
||||||
|
"usr/bin/yum": dnf,
|
||||||
|
"usr/bin/rpm": rpm,
|
||||||
|
}
|
||||||
|
if family == "openwrt":
|
||||||
|
opkg = """opkg must have one sub-command argument
|
||||||
|
usage: opkg [options...] sub-command [arguments...]
|
||||||
|
|
||||||
|
Package Manipulation:
|
||||||
|
update Update list of available packages
|
||||||
|
install <pkg> Install package(s)
|
||||||
|
remove <pkg> Remove package(s)
|
||||||
|
list-installed List installed packages
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"bin/opkg": opkg,
|
||||||
|
}
|
||||||
|
if family == "ubiquiti":
|
||||||
|
apt = """Reading package lists... Done
|
||||||
|
Building dependency tree... Done
|
||||||
|
E: Unable to locate package
|
||||||
|
"""
|
||||||
|
dpkg = "Debian 'dpkg' package management program version 1.19.8 (mips).\n"
|
||||||
|
return {
|
||||||
|
"usr/bin/apt-get": apt,
|
||||||
|
"usr/bin/dpkg": dpkg,
|
||||||
|
}
|
||||||
|
if family == "qnap":
|
||||||
|
qpkg = """Usage: qpkg_cli [options]
|
||||||
|
--list list installed packages
|
||||||
|
--status NAME show package status
|
||||||
|
--help show this help
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"sbin/qpkg_cli": qpkg,
|
||||||
|
}
|
||||||
|
if family == "synology":
|
||||||
|
synopkg = """Copyright (c) 2003-2023 Synology Inc. All rights reserved.
|
||||||
|
Usage: synopkg <command> [package]
|
||||||
|
list
|
||||||
|
status <package>
|
||||||
|
start <package>
|
||||||
|
stop <package>
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"usr/syno/bin/synopkg": synopkg,
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def command_inventory_txtcmds(persona):
|
||||||
|
family = persona["family"]
|
||||||
|
txtcmds = {}
|
||||||
|
|
||||||
|
if family in {"debian", "fedora", "rhel"}:
|
||||||
|
txtcmds.update(
|
||||||
|
{
|
||||||
|
"usr/bin/lspci": "00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC\n00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA\n00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE\n00:02.0 VGA compatible controller: Device 1234:1111\n",
|
||||||
|
"usr/bin/sudo": "sudo: a password is required\n",
|
||||||
|
"usr/sbin/service": "Usage: service < option > | --status-all | [ service_name [ command | --full-restart ] ]\n",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
elif family in {"qnap", "synology"}:
|
||||||
|
txtcmds["usr/bin/lspci"] = (
|
||||||
|
"00:00.0 Host bridge: Intel Corporation Atom Processor C3000 Host Bridge\n"
|
||||||
|
"00:14.0 USB controller: Intel Corporation Atom Processor C3000 USB 3.0 xHCI Controller\n"
|
||||||
|
"00:17.0 SATA controller: Intel Corporation Atom Processor C3000 SATA Controller\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
if family in {"iot-router", "iot-nas", "openwrt", "qnap", "synology", "ubiquiti"}:
|
||||||
|
if "bin/busybox" in persona["files"]:
|
||||||
|
txtcmds["bin/busybox"] = persona["files"]["bin/busybox"]
|
||||||
|
|
||||||
|
return txtcmds
|
||||||
|
|
||||||
|
|
||||||
|
def skip_python_commands_for_family(family):
|
||||||
|
commands = list(COMMON_SKIP_PYTHON_COMMANDS)
|
||||||
|
if family in {"iot-router", "iot-nas", "openwrt", "qnap", "synology", "ubiquiti"}:
|
||||||
|
commands.extend(EMBEDDED_SKIP_PYTHON_COMMANDS)
|
||||||
|
return sorted(set(commands))
|
||||||
|
|
||||||
|
|
||||||
def ps_entry(user, pid, command, cpu=0.0, mem=0.1, vsz=0, rss=0, stat="S", tty="?", start=None):
|
def ps_entry(user, pid, command, cpu=0.0, mem=0.1, vsz=0, rss=0, stat="S", tty="?", start=None):
|
||||||
return {
|
return {
|
||||||
"USER": user,
|
"USER": user,
|
||||||
|
|
@ -418,7 +620,7 @@ cpu time (seconds, -t) unlimited
|
||||||
max user processes (-u) 1024
|
max user processes (-u) 1024
|
||||||
virtual memory (kbytes, -v) unlimited
|
virtual memory (kbytes, -v) unlimited
|
||||||
"""
|
"""
|
||||||
return {
|
txtcmds = {
|
||||||
"bin/df": df,
|
"bin/df": df,
|
||||||
"bin/dmesg": dmesg,
|
"bin/dmesg": dmesg,
|
||||||
"bin/mount": mount,
|
"bin/mount": mount,
|
||||||
|
|
@ -427,6 +629,9 @@ virtual memory (kbytes, -v) unlimited
|
||||||
"usr/bin/nproc": f"{cpu_count}\n",
|
"usr/bin/nproc": f"{cpu_count}\n",
|
||||||
"usr/bin/top": top,
|
"usr/bin/top": top,
|
||||||
}
|
}
|
||||||
|
txtcmds.update(package_manager_txtcmds(persona))
|
||||||
|
txtcmds.update(command_inventory_txtcmds(persona))
|
||||||
|
return txtcmds
|
||||||
|
|
||||||
|
|
||||||
def profile(
|
def profile(
|
||||||
|
|
@ -488,6 +693,7 @@ def profile(
|
||||||
"vulnerability": vulnerability,
|
"vulnerability": vulnerability,
|
||||||
"shell": shell,
|
"shell": shell,
|
||||||
"process_start": random_ps_start(),
|
"process_start": random_ps_start(),
|
||||||
|
"skip_python_commands": skip_python_commands_for_family(family),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -988,6 +1194,8 @@ def write_text_file(path, text, mode=0o644):
|
||||||
def apply_persona_to_tree(tree, persona):
|
def apply_persona_to_tree(tree, persona):
|
||||||
for remove_path in persona["remove_paths"]:
|
for remove_path in persona["remove_paths"]:
|
||||||
remove_node(tree, remove_path)
|
remove_node(tree, remove_path)
|
||||||
|
for remove_path in PACKAGE_MANAGER_PATHS + HIGH_VARIANCE_COMMAND_PATHS:
|
||||||
|
remove_node(tree, remove_path)
|
||||||
|
|
||||||
for relative_path in COMMON_DIRS:
|
for relative_path in COMMON_DIRS:
|
||||||
mode = 0o755
|
mode = 0o755
|
||||||
|
|
@ -1013,7 +1221,7 @@ def apply_persona_to_tree(tree, persona):
|
||||||
mode = 0o755
|
mode = 0o755
|
||||||
ensure_file(tree, relative_path, text.encode("utf-8"), 0, 0, mode)
|
ensure_file(tree, relative_path, text.encode("utf-8"), 0, 0, mode)
|
||||||
|
|
||||||
for relative_path in TXTCMD_PATHS:
|
for relative_path in txtcmds_for_persona(persona):
|
||||||
ensure_file(tree, relative_path, b"", 0, 0, 0o755)
|
ensure_file(tree, relative_path, b"", 0, 0, 0o755)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1093,6 +1301,7 @@ kernel_build_string = {persona["kernel_build_string"]}
|
||||||
hardware_platform = {persona["hardware_platform"]}
|
hardware_platform = {persona["hardware_platform"]}
|
||||||
operating_system = {persona["operating_system"]}
|
operating_system = {persona["operating_system"]}
|
||||||
ssh_version = {persona["shell_ssh_version"]}
|
ssh_version = {persona["shell_ssh_version"]}
|
||||||
|
skip_python_commands = {",".join(persona["skip_python_commands"])}
|
||||||
|
|
||||||
[ssh]
|
[ssh]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
@ -1182,6 +1391,7 @@ def validate_persona(persona, persona_dir, runtime_persona_dir):
|
||||||
)
|
)
|
||||||
|
|
||||||
pickle_bytes = pickle_path.read_bytes()
|
pickle_bytes = pickle_path.read_bytes()
|
||||||
|
pickle_tree = load_pickle(pickle_path)
|
||||||
required = [
|
required = [
|
||||||
persona["user"].encode("utf-8"),
|
persona["user"].encode("utf-8"),
|
||||||
persona["hostname"].encode("utf-8"),
|
persona["hostname"].encode("utf-8"),
|
||||||
|
|
@ -1207,9 +1417,21 @@ def validate_persona(persona, persona_dir, runtime_persona_dir):
|
||||||
expected_start = persona["process_start"]
|
expected_start = persona["process_start"]
|
||||||
if any(process.get("START") != expected_start for process in process_list):
|
if any(process.get("START") != expected_start for process in process_list):
|
||||||
raise RuntimeError(f"{persona['id']} has inconsistent process start dates")
|
raise RuntimeError(f"{persona['id']} has inconsistent process start dates")
|
||||||
for relative_path in TXTCMD_PATHS:
|
expected_txtcmds = txtcmds_for_persona(persona)
|
||||||
|
for relative_path in expected_txtcmds:
|
||||||
if not (txtcmds / relative_path).is_file():
|
if not (txtcmds / relative_path).is_file():
|
||||||
raise RuntimeError(f"{persona['id']} has no txtcmds/{relative_path}")
|
raise RuntimeError(f"{persona['id']} has no txtcmds/{relative_path}")
|
||||||
|
for relative_path in PACKAGE_MANAGER_PATHS:
|
||||||
|
has_command = relative_path in expected_txtcmds
|
||||||
|
in_pickle = find_node(pickle_tree, relative_path) is not None
|
||||||
|
in_honeyfs = (honeyfs / relative_path).exists()
|
||||||
|
if has_command and not in_pickle:
|
||||||
|
raise RuntimeError(f"{persona['id']} package manager missing from pickle: {relative_path}")
|
||||||
|
if not has_command and (in_pickle or in_honeyfs):
|
||||||
|
raise RuntimeError(f"{persona['id']} has mismatched package manager path: {relative_path}")
|
||||||
|
config_text = config_path.read_text(encoding="utf-8")
|
||||||
|
if "skip_python_commands =" not in config_text:
|
||||||
|
raise RuntimeError(f"{persona['id']} config does not define skip_python_commands")
|
||||||
if not (honeyfs / "etc" / "hostname").is_file():
|
if not (honeyfs / "etc" / "hostname").is_file():
|
||||||
raise RuntimeError(f"{persona['id']} honeyfs has no /etc/hostname")
|
raise RuntimeError(f"{persona['id']} honeyfs has no /etc/hostname")
|
||||||
|
|
||||||
|
|
@ -1249,6 +1471,9 @@ def write_metadata(personas_root):
|
||||||
"user": persona["user"],
|
"user": persona["user"],
|
||||||
"arch": persona["arch"],
|
"arch": persona["arch"],
|
||||||
"ssh_banner": persona["ssh_banner"],
|
"ssh_banner": persona["ssh_banner"],
|
||||||
|
"package_managers": sorted(
|
||||||
|
Path(path).name for path in package_manager_txtcmds(persona)
|
||||||
|
),
|
||||||
"vulnerability": persona["vulnerability"],
|
"vulnerability": persona["vulnerability"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,50 @@ def replace_all(path, old, new):
|
||||||
def patch_protocol(cowrie_root):
|
def patch_protocol(cowrie_root):
|
||||||
path = cowrie_root / "src" / "cowrie" / "shell" / "protocol.py"
|
path = cowrie_root / "src" / "cowrie" / "shell" / "protocol.py"
|
||||||
replace_once(path, "import traceback\n", "import traceback\nfrom pathlib import Path\n")
|
replace_once(path, "import traceback\n", "import traceback\nfrom pathlib import Path\n")
|
||||||
|
replace_once(
|
||||||
|
path,
|
||||||
|
""" if cmd in self.commands:
|
||||||
|
return self.commands[cmd]
|
||||||
|
if cmd[0] in (".", "/"):
|
||||||
|
path = self.fs.resolve_path(cmd, self.cwd)
|
||||||
|
if not self.fs.exists(path):
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
for i in [f"{self.fs.resolve_path(x, self.cwd)}/{cmd}" for x in paths]:
|
||||||
|
if self.fs.exists(i):
|
||||||
|
path = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if path is None:
|
||||||
|
return None
|
||||||
|
""",
|
||||||
|
""" skip_python_commands = {
|
||||||
|
command.strip()
|
||||||
|
for command in CowrieConfig.get(
|
||||||
|
"shell", "skip_python_commands", fallback=""
|
||||||
|
).split(",")
|
||||||
|
if command.strip()
|
||||||
|
}
|
||||||
|
|
||||||
|
def skip_python_command(name):
|
||||||
|
return name in skip_python_commands or Path(name).name in skip_python_commands
|
||||||
|
|
||||||
|
if cmd in self.commands and not skip_python_command(cmd):
|
||||||
|
return self.commands[cmd]
|
||||||
|
if cmd[0] in (".", "/"):
|
||||||
|
path = self.fs.resolve_path(cmd, self.cwd)
|
||||||
|
if not self.fs.exists(path):
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
for i in [f"{self.fs.resolve_path(x, self.cwd)}/{cmd}" for x in paths]:
|
||||||
|
if self.fs.exists(i):
|
||||||
|
path = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if path is None:
|
||||||
|
return None
|
||||||
|
""",
|
||||||
|
)
|
||||||
replace_once(
|
replace_once(
|
||||||
path,
|
path,
|
||||||
""" try:
|
""" try:
|
||||||
|
|
@ -42,6 +86,15 @@ def patch_protocol(cowrie_root):
|
||||||
pass
|
pass
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
replace_once(
|
||||||
|
path,
|
||||||
|
""" if path in self.commands:
|
||||||
|
return self.commands[path]
|
||||||
|
""",
|
||||||
|
""" if path in self.commands and not skip_python_command(path):
|
||||||
|
return self.commands[path]
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def patch_ssh(cowrie_root):
|
def patch_ssh(cowrie_root):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue