From 2563c7fe08a4ecd8fd59ad0378fea8b3d0fa2d07 Mon Sep 17 00:00:00 2001 From: Himadri Bhattacharjee <107522312+lavafroth@users.noreply.github.com> Date: Mon, 1 Dec 2025 15:45:42 +0530 Subject: [PATCH] feat: move from ducky script to python --- src/code.py | 4 +-- src/ducky.py | 94 +++++++++++++--------------------------------------- src/logs.py | 6 ++-- 3 files changed, 28 insertions(+), 76 deletions(-) diff --git a/src/code.py b/src/code.py index fe7962b..96fe49b 100644 --- a/src/code.py +++ b/src/code.py @@ -23,7 +23,7 @@ async def setup_server(): """ wifi.radio.start_ap(ssid=os.getenv("SSID"), password=os.getenv("PASSWORD")) pool = socketpool.SocketPool(wifi.radio) - server = Server(pool) + server = Server(pool, debug=True) @server.route("/") def base(request: Request): @@ -41,7 +41,7 @@ async def setup_server(): def api(request: Request): return handle(request) - server.serve_forever(str(wifi.radio.ipv4_address_ap)) + server.serve_forever("0.0.0.0", 80) async def main(): diff --git a/src/ducky.py b/src/ducky.py index 65c31b2..a109b93 100644 --- a/src/ducky.py +++ b/src/ducky.py @@ -1,6 +1,7 @@ """ -Logic to interpret and execute user defined ducky script payloads. +Execute user defined python scripts in a sandbox """ + import time import usb_hid @@ -23,95 +24,48 @@ kbd = Keyboard(usb_hid.devices) layout = KeyboardLayout(kbd) -def delay(millis): +def delay(millis=1000.0): """ Sleep, do absolutely nothing. """ time.sleep(float(millis) / 1000) -def prefix_checker(line: str): - """ - Returns a function that checks if line begins with - any of the prefixes supplied to it. - - Syntax sugar so that we can later use it in conditional - statements like if something := checker("foo", "bar") - """ - - def checker(*prefixes): - for prefix in prefixes: - if line.startswith(prefix): - return line[len(prefix) + 1:] - return None - - return checker - - -def press_keys(line: str): +def press(*keys): """ Press all the keys and then release them. - Really useful for keyboard shortcuts like Meta+R. """ - # loop on each key filtering empty values - for key in filter(None, line.upper().split(" ")): + + # If a single key is invalid, fail fast + for key in keys: + if key not in Keycode.__dict__: + warn(f"unknown key: <{key}>") + return + + for key in keys: if command_keycode := Keycode.__dict__.get(key): # If this is a valid key, send its keycode kbd.press(command_keycode) - continue - # If it's not a known key name, log it for diagnosis - warn(f"unknown key: <{key}>") kbd.release_all() -def repeat(contents: str, times: int): - """ - If the contents supplied is not empty or None, - repeat those ducky script lines `times` times. - """ - if not contents: - return - for _ in range(times): - run_script(contents) +def led(value: bool): + LED.value = value -def run_script(contents): +def run_script(contents: str): """ Interpret the ducky script and execute it line by line """ - default_delay = 0 - previous_line = None - for line in filter(None, contents.splitlines()): - line = line.rstrip() - after = prefix_checker(line) - - if times := after("REPEAT"): - repeat(previous_line, int(times)) - - elif after("REM"): - continue - elif (millis := after("DELAY")) is not None: - delay(millis or default_delay) - elif message := after("PRINT"): - info(message) - elif path := after("IMPORT"): - run_script_file(path) - elif millis := after("DEFAULT_DELAY", "DEFAULTDELAY"): - default_delay = int(millis) - elif after("LED") is not None: - LED.value ^= True - elif string := after("STRING"): - layout.write(string) - else: - press_keys(line) - - previous_line = line - delay(default_delay) + exec( + contents, + dict(print=info, write=layout.write, led=led, delay=delay, press=press), + ) def run_script_file(path: str): """ - Try reading and running a ducky script from the supplied path. + Try reading and running a python script from the supplied path. """ try: with open(path, "r", encoding="utf-8") as handle: @@ -119,14 +73,12 @@ def run_script_file(path: str): except OSError as error: warn(f"unable to open file {path}: {error}") - async def run_boot_script(): """ - If a script with the name 'boot.dd' exists, - run it without user interaction on boot. + Try reading and running a python script from the supplied path. """ try: - with open("payloads/boot.dd", "r", encoding="utf-8") as handle: + with open('boot_payload.py', "r", encoding="utf-8") as handle: run_script(handle.read()) except OSError: - info("boot script does not exist, skipping its execution") + info("not boot script set") diff --git a/src/logs.py b/src/logs.py index 9f3b2d7..b6df81c 100644 --- a/src/logs.py +++ b/src/logs.py @@ -16,15 +16,15 @@ def consume() -> str: return dump -def info(message: str): +def info(message): """ Add a log entry with the message prepended with the info marker """ - logs.append("info: " + message) + logs.append("info: " + str(message)) def warn(message: str): """ Add a log entry with the message prepended with the warning marker """ - logs.append("warning: " + message) + logs.append("warn: " + str(message))