CVE-2024-48911 (Python)

OpenCanary can cross a privilege boundary when the daemon is started with elevated privileges but its configuration is discovered from locations writable by an unprivileged user, such as the current working directory or the user's home directory. An attacker who can control those configuration files, or the environment used while they are parsed, may influence command execution paths during configuration loading, error handling, or modules that build privileged system commands from configuration values. This can allow local privilege escalation by causing attacker-controlled commands or command arguments to run with the daemon's privileges.

Weakness:
CWE-863: name: Incorrect Authorization
description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.

Repository: https://github.com/thinkst/opencanary

Vulnerable code:
file_path: opencanary/config.py
snippet:     def __init__(self, configfile=SETTINGS):
        self.__config = None
        self.__configfile = configfile

        files = [
            configfile,
            "%s/.%s" % (expanduser("~"), configfile),
            "/etc/opencanaryd/%s" % configfile,
        ]
        print(
            "** We hope you enjoy using OpenCanary. For more open source Canary goodness, head over to canarytokens.org. **"
        )
        for fname in files:
            try:
                with open(fname, "r") as f:
                    print("[-] Using config file: %s" % fname)
                    self.__config = json.load(f)
                    self.__config = expand_vars(self.__config)
                return
            except IOError as e:
                print("[-] Failed to open %s for reading (%s)" % (fname, e))
            except ValueError as e:
                print("[-] Failed to decode json from %s (%s)" % (fname, e))
                subprocess.call(
                    "cp -r %s /var/tmp/config-err-$(date +%%s)" % fname, shell=True
                )
            except Exception as e:
                print("[-] An error occurred loading %s (%s)" % (fname, e))
        if self.__config is None:
            print(
                'No config file found. Please create one with "opencanaryd --copyconfig"'
            )
            sys.exit(1)

file_path: opencanary/modules/portscan.py
snippet: def detectNFTables():
    return b"nf_tables" in subprocess.check_output(["iptables", "--version"])

Fixed code:
file_path: opencanary/config.py
snippet:     def __init__(self, configfile=SETTINGS):
        self.__config = None
        self.__configfile = configfile

        files = [
            "/etc/opencanaryd/%s" % configfile,
            "%s/.%s" % (expanduser("~"), configfile),
            configfile,
        ]
        print(
            "** We hope you enjoy using OpenCanary. For more open source Canary goodness, head over to canarytokens.org. **"
        )
        for fname in files:
            try:
                with open(fname, "r") as f:
                    print("[-] Using config file: %s" % fname)
                    self.__config = json.load(f)
                    self.__config = expand_vars(self.__config)
                if fname is configfile:
                    print(
                        "[-] Warning, making use of the configuration file in the immediate directory is not recommended! Suggested locations: %s"
                        % ", ".join(files[:2])
                    )
                return
            except IOError as e:
                print("[-] Failed to open %s for reading (%s)" % (fname, e))
            except ValueError as e:
                print("[-] Failed to decode json from %s (%s)" % (fname, e))
                safe_exec("cp", ["-r", fname, "/var/tmp/config-err-$(date +%%s)"])
            except Exception as e:
                print("[-] An error occurred loading %s (%s)" % (fname, e))
        if self.__config is None:
            print(
                'No config file found. Please create one with "opencanaryd --copyconfig"'
            )
            sys.exit(1)

file_path: opencanary/modules/portscan.py
snippet: def detectNFTables():
    return b"nf_tables" in safe_exec("iptables", ["--version"])