From af9786a2444bb3fd856db655585b2fed97bf3846 Mon Sep 17 00:00:00 2001 From: JSCU-CNI <121175071+JSCU-CNI@users.noreply.github.com> Date: Mon, 21 Aug 2023 13:51:17 +0200 Subject: [PATCH] Add support for ufw to iptables plugin (#366) --- .../target/plugins/os/unix/linux/iptables.py | 20 +++++++++-- tests/test_plugins_os_unix_linux_iptables.py | 36 ++++++++++++++++++- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/dissect/target/plugins/os/unix/linux/iptables.py b/dissect/target/plugins/os/unix/linux/iptables.py index a88f37a61..584f9164c 100644 --- a/dissect/target/plugins/os/unix/linux/iptables.py +++ b/dissect/target/plugins/os/unix/linux/iptables.py @@ -35,7 +35,7 @@ class IptablesSavePlugin(Plugin): - """Parser for iptables-save (and ip6tables-save) rules. + """Parser for iptables-save, ip6tables-save and ufw rules. As iptables rules are not stored on disk by default, users that want persistent rules need to store them somewhere and @@ -46,6 +46,7 @@ class IptablesSavePlugin(Plugin): References: - https://git.netfilter.org/iptables/ + - https://manpages.ubuntu.com/manpages/jammy/en/man8/ufw-framework.8.html """ COMMON_SAVE_PATHS = ( @@ -59,6 +60,19 @@ class IptablesSavePlugin(Plugin): "/etc/iptables/rules.v6", "/etc/iptablesRule.v6", "/etc/sysconfig/ip6tables", + # UFW + "/etc/ufw/before.rules", + "/etc/ufw/user.rules", + "/etc/ufw/after.rules", + "/etc/ufw/before6.rules", + "/etc/ufw/user6.rules", + "/etc/ufw/after6.rules", + "/usr/share/ufw/user.rules", + "/usr/share/ufw/user6.rules", + "/usr/share/ufw/iptables/user.rules", + "/usr/share/ufw/iptables/user6.rules", + "/var/lib/ufw/user.rules", + "/var/lib/ufw/user6.rules", ) LOG_TIME_FORMAT = "%a %b %d %H:%M:%S %Y" @@ -79,12 +93,12 @@ def _get_rule_files(self) -> Iterator[Path]: with rule_path.open("r") as h_rule: first_line = h_rule.readline() - if PATTERN_IPTABLES_SAVE_GENERATED.match(first_line): + if PATTERN_IPTABLES_SAVE_GENERATED.match(first_line) or "*filter" in first_line: yield rule_path @export(record=IptablesSaveRecord) def iptables(self) -> Iterator[IptablesSaveRecord]: - """Return iptables rules saved using iptables-save.""" + """Return iptables and ufw rules saved using iptables-save.""" tzinfo = self.target.datetime.tzinfo for rule_path in self._rule_files: diff --git a/tests/test_plugins_os_unix_linux_iptables.py b/tests/test_plugins_os_unix_linux_iptables.py index b2b8e09c6..65de532b8 100644 --- a/tests/test_plugins_os_unix_linux_iptables.py +++ b/tests/test_plugins_os_unix_linux_iptables.py @@ -1,7 +1,7 @@ import textwrap from io import BytesIO -from flow.record.fieldtypes import datetime +from flow.record.fieldtypes import datetime, path from dissect.target import Target from dissect.target.filesystem import VirtualFilesystem @@ -114,3 +114,37 @@ def test_iptables_plugin(target_unix_users: Target, fs_unix: VirtualFilesystem): assert results[19].rule == "-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER" assert results[19].packet_count == 78 assert results[19].byte_count == 2323 + + +def test_iptables_plugin_ufw(target_unix_users, fs_unix): + ufw_rules_path = "/etc/ufw/user.rules" + + ufw_rules = """*filter + :ufw-user-input - [0:0] + :ufw-user-output - [0:0] + :ufw-user-forward - [0:0] + :ufw-user-limit - [0:0] + :ufw-user-limit-accept - [0:0] + ### RULES ### + -A ufw-user-limit -m limit --limit 3/minute -j LOG --log-prefix "[UFW LIMIT BLOCK] " + -A ufw-user-limit -j REJECT + -A ufw-user-limit-accept -j ACCEPT + COMMIT + """ + + fs_unix.map_file_fh( + ufw_rules_path, + BytesIO(textwrap.dedent(ufw_rules).encode()), + ) + + target_unix_users.add_plugin(IptablesSavePlugin) + results = list(target_unix_users.iptables()) + + # 5 policies, 3 rules + assert len(results) == 5 + 3 + + assert results[-1].source == path.from_posix(ufw_rules_path) + assert results[-1].rule == "-A ufw-user-limit-accept -j ACCEPT" + assert results[-1].table == "filter" + assert results[-1].chain == "ufw-user-limit-accept" + assert results[-1].type == "rule"