feat: move from ducky script to python
This commit is contained in:
@@ -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():
|
||||
|
||||
94
src/ducky.py
94
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")
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user