adding validated services? patching forcad_local.py
This commit is contained in:
166
ctfcup24-school-ad/checkers/flysim/checker.py
Executable file
166
ctfcup24-school-ad/checkers/flysim/checker.py
Executable file
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env python3
|
||||
import socket
|
||||
import sys
|
||||
import random
|
||||
import string
|
||||
import requests
|
||||
import time
|
||||
import json
|
||||
|
||||
import drone_client
|
||||
import random_flight_plan
|
||||
from checklib import *
|
||||
|
||||
|
||||
def print(*args, **kwargs):
|
||||
pass # disable print
|
||||
|
||||
|
||||
def generate_random_datastring(length):
|
||||
characters = string.ascii_letters + string.digits
|
||||
random_string = "".join(random.choice(characters) for _ in range(length))
|
||||
|
||||
return random_string
|
||||
|
||||
|
||||
def percent_50():
|
||||
return random.randint(1, 2) == 1
|
||||
|
||||
|
||||
class Checker(BaseChecker):
|
||||
vulns: int = 1
|
||||
timeout: int = 20
|
||||
uses_attack_data: bool = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Checker, self).__init__(*args, **kwargs)
|
||||
|
||||
def action(self, action, *args, **kwargs):
|
||||
try:
|
||||
super(Checker, self).action(action, *args, **kwargs)
|
||||
except requests.exceptions.ConnectionError:
|
||||
self.cquit(
|
||||
Status.DOWN,
|
||||
"Connection error",
|
||||
"Got requests.exceptions.ConnectionError",
|
||||
)
|
||||
|
||||
def check(self):
|
||||
client = drone_client.DroneClient(ip=self.host)
|
||||
label = generate_random_datastring(random.randint(10, 30))
|
||||
client.create_drone(
|
||||
label=label, secret_data=generate_random_datastring(random.randint(10, 30))
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
client.connect_to_drone()
|
||||
vel = [random.randint(-3, 3), random.randint(-3, 3)]
|
||||
pos = [random.randint(-20, 20), random.randint(-20, 20)]
|
||||
client.update_velocity(vel)
|
||||
vel_msg = client.wait_for_vel_msg(timeout=30)
|
||||
self.assert_eq(
|
||||
vel, vel_msg["new_velocity"], "Incorrect velocity handling", Status.CORRUPT
|
||||
)
|
||||
|
||||
client.update_position(pos)
|
||||
pos_msg = client.wait_for_pos_msg(timeout=30)
|
||||
self.assert_eq(
|
||||
pos, pos_msg["new_position"], "Incorrect position handling", Status.CORRUPT
|
||||
)
|
||||
|
||||
drone_obj = client.wait_for_data_msg(timeout=30)
|
||||
for _ in range(5):
|
||||
if vel == drone_obj["velocity"]:
|
||||
break
|
||||
drone_obj = client.wait_for_data_msg(timeout=30)
|
||||
|
||||
self.assert_eq(
|
||||
vel, drone_obj["velocity"], "Incorrect velocity handling", Status.CORRUPT
|
||||
)
|
||||
|
||||
drone_obj_via_httpapi = client.get_drones_with_details(
|
||||
json.dumps([{"$match": {"label": label}}])
|
||||
)[0]
|
||||
self.assert_eq(
|
||||
drone_obj_via_httpapi["label"],
|
||||
label,
|
||||
"Incorrect HTTP API response",
|
||||
Status.CORRUPT,
|
||||
)
|
||||
self.assert_eq(
|
||||
drone_obj_via_httpapi["velocity"],
|
||||
vel,
|
||||
"Incorrect HTTP API response",
|
||||
Status.CORRUPT,
|
||||
)
|
||||
self.assert_eq(
|
||||
drone_obj_via_httpapi["id"],
|
||||
drone_obj["id"],
|
||||
"Incorrect HTTP API response",
|
||||
Status.CORRUPT,
|
||||
)
|
||||
|
||||
self.cquit(Status.OK)
|
||||
|
||||
def put(self, flag_id: str, flag: str, vuln: str):
|
||||
fp = "\n".join(random_flight_plan.generate_plan(4, 7))
|
||||
client = drone_client.DroneClient(ip=self.host)
|
||||
client.create_drone(
|
||||
label=generate_random_datastring(random.randint(10, 30)),
|
||||
secret_data=flag,
|
||||
flight_plan=fp,
|
||||
)
|
||||
self.cquit(
|
||||
Status.OK,
|
||||
f"{client.drone_id}",
|
||||
json.dumps(
|
||||
{
|
||||
"drone_id": client.drone_id,
|
||||
"control_key": client.control_key,
|
||||
"flight_plan": fp,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
def get(self, flag_id: str, flag: str, vuln: str):
|
||||
# print('get FLAG by!', flag_id, '!!<-id')
|
||||
drone = json.loads(flag_id)
|
||||
|
||||
if vuln == "1":
|
||||
client = drone_client.DroneClient(ip=self.host)
|
||||
client.drone_id = drone["drone_id"]
|
||||
client.control_key = drone["control_key"]
|
||||
client.connect_to_drone()
|
||||
|
||||
drone_obj = client.wait_for_data_msg(timeout=30)
|
||||
expected_fl = random_flight_plan.get_expected_flight_log(
|
||||
drone["flight_plan"], drone_obj["cur_time"], drone_obj
|
||||
)
|
||||
real_fl = drone_obj["flight_log"]
|
||||
self.assert_eq(
|
||||
sorted(expected_fl.split("\n")),
|
||||
sorted(real_fl.split("\n")),
|
||||
"Incorrect flight log",
|
||||
Status.CORRUPT,
|
||||
)
|
||||
self.assert_eq(
|
||||
flag, drone_obj["secret_data"], "Incorrect flag", Status.CORRUPT
|
||||
)
|
||||
|
||||
self.cquit(Status.OK)
|
||||
|
||||
|
||||
# if __name__ == '__main__':
|
||||
# c = Checker("127.0.0.1")
|
||||
# try:
|
||||
# c.action("put", "", "TestFLAG234Fofkdjfn", "1")
|
||||
# c.action("get",r'{"drone_id": "6756e4e9fb6b9f5011e88fa8", "control_key": "c3ff78e2", "flight_plan": "7 BOOSTY [drone] 1\n7 BOOSTX [drone] -1\n7 BOOSTX [drone] -2\n13 BOOSTY [drone] 2\n5 BOOSTY [drone] -1\n10 BOOSTY [drone] 2\n10 FIRE [drone]"}', "TestFLAG234Fofkdjfn", "1")
|
||||
# c.action("check")
|
||||
# except c.get_check_finished_exception():
|
||||
# cquit(Status(c.status), c.public, c.private)
|
||||
if __name__ == "__main__":
|
||||
c = Checker(sys.argv[2])
|
||||
try:
|
||||
c.action(sys.argv[1], *sys.argv[3:])
|
||||
except c.get_check_finished_exception():
|
||||
cquit(Status(c.status), c.public, c.private)
|
||||
132
ctfcup24-school-ad/checkers/flysim/drone_client.py
Normal file
132
ctfcup24-school-ad/checkers/flysim/drone_client.py
Normal file
@@ -0,0 +1,132 @@
|
||||
import socketio
|
||||
import requests
|
||||
import time
|
||||
import json
|
||||
from threading import Event
|
||||
|
||||
|
||||
def print(*args, **kwargs):
|
||||
pass # disable print
|
||||
|
||||
|
||||
class DroneClient:
|
||||
def __init__(self, ip="127.0.0.1"):
|
||||
sio = socketio.Client()
|
||||
self.base_url = f"http://{ip}:9000/"
|
||||
self.sio = sio
|
||||
|
||||
self.drone_id = None
|
||||
self.control_key = None
|
||||
self.data_received_event = Event()
|
||||
self.last_received_data = None
|
||||
|
||||
self.velocity_updated_event = Event()
|
||||
self.last_received_veldata = None
|
||||
|
||||
self.position_updated_event = Event()
|
||||
self.last_received_posdata = None
|
||||
|
||||
@sio.on("connect")
|
||||
def on_connect():
|
||||
print("Connected to server")
|
||||
|
||||
@sio.on("disconnect")
|
||||
def on_disconnect():
|
||||
print("Disconnected from server")
|
||||
|
||||
@sio.on("drone_connected")
|
||||
def on_drone_connected(data):
|
||||
print(f"Drone connection status: {data['data']}")
|
||||
|
||||
@sio.on("data_updated")
|
||||
def on_data_updated(data):
|
||||
if isinstance(data, dict):
|
||||
self.last_received_data = data
|
||||
self.data_received_event.set()
|
||||
|
||||
@sio.on("velocity_updated")
|
||||
def on_velocity_updated(data):
|
||||
if isinstance(data, dict):
|
||||
self.last_received_veldata = data
|
||||
self.velocity_updated_event.set()
|
||||
|
||||
@sio.on("position_updated")
|
||||
def on_position_updated(data):
|
||||
if isinstance(data, dict):
|
||||
self.last_received_posdata = data
|
||||
self.position_updated_event.set()
|
||||
|
||||
@sio.on("error")
|
||||
def on_error(data):
|
||||
print(f"Error: {data['data']}")
|
||||
|
||||
def wait_for_data_msg(self, timeout=None):
|
||||
self.data_received_event.clear()
|
||||
if self.data_received_event.wait(timeout):
|
||||
return self.last_received_data
|
||||
return None
|
||||
|
||||
def wait_for_vel_msg(self, timeout=None):
|
||||
self.velocity_updated_event.clear()
|
||||
if self.velocity_updated_event.wait(timeout):
|
||||
return self.last_received_veldata
|
||||
return None
|
||||
|
||||
def wait_for_pos_msg(self, timeout=None):
|
||||
self.position_updated_event.clear()
|
||||
if self.position_updated_event.wait(timeout):
|
||||
return self.last_received_posdata
|
||||
return None
|
||||
|
||||
def create_drone(self, label=None, secret_data=None, flight_plan=None):
|
||||
response = requests.post(
|
||||
f"{self.base_url}/create_drone",
|
||||
timeout=8000,
|
||||
json={}
|
||||
| ({"label": label} if label else {})
|
||||
| ({"secret_data": secret_data} if secret_data else {})
|
||||
| ({"flight_plan": flight_plan} if flight_plan else {}),
|
||||
)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
self.drone_id = data["id"]
|
||||
self.control_key = data["control_key"]
|
||||
print(
|
||||
f"Created drone with ID: {self.drone_id}, control key: {self.control_key}"
|
||||
)
|
||||
return True
|
||||
else:
|
||||
print(f"Failed to create drone: {response.text}")
|
||||
return False
|
||||
|
||||
def get_all_drones(self):
|
||||
response = requests.get(f"{self.base_url}/get_drones")
|
||||
print("All drones:", response.json())
|
||||
|
||||
def get_drones_with_details(self, with_what_details):
|
||||
response = requests.get(f"{self.base_url}/get_drones?with={with_what_details}")
|
||||
return response.json()
|
||||
|
||||
def connect_to_drone(self):
|
||||
try:
|
||||
self.sio.connect(self.base_url, socketio_path="/socket.io")
|
||||
self.sio.emit(
|
||||
"join_drone",
|
||||
{"drone_id": self.drone_id, "control_key": self.control_key},
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Connection error: {e}")
|
||||
return False
|
||||
|
||||
def update_position(self, position):
|
||||
self.sio.emit("set_position", {"drone_id": self.drone_id, "position": position})
|
||||
|
||||
def update_velocity(self, velocity):
|
||||
self.sio.emit("set_velocity", {"drone_id": self.drone_id, "velocity": velocity})
|
||||
|
||||
def disconnect(self):
|
||||
try:
|
||||
self.sio.disconnect()
|
||||
except Exception as e:
|
||||
print(f"Disconnection error: {e}")
|
||||
88
ctfcup24-school-ad/checkers/flysim/random_flight_plan.py
Normal file
88
ctfcup24-school-ad/checkers/flysim/random_flight_plan.py
Normal file
@@ -0,0 +1,88 @@
|
||||
import random
|
||||
|
||||
|
||||
def print(*args, **kwargs):
|
||||
pass # disable print
|
||||
|
||||
|
||||
def BOOSTX(drone, value):
|
||||
drone["velocity"][0] += value
|
||||
return f"Drone {drone['id']} boosted at X axis by {value}!"
|
||||
|
||||
|
||||
def BOOSTY(drone, value):
|
||||
drone["velocity"][1] += value
|
||||
return f"Drone {drone['id']} boosted at Y axis by {value}!"
|
||||
|
||||
|
||||
def FIRE(drone):
|
||||
return f"Drone {drone['id']} fired!"
|
||||
|
||||
|
||||
def SETFREQ(drone, frequency):
|
||||
return f"Drone {drone['id']} freq set to {frequency}!"
|
||||
|
||||
|
||||
def get_expected_flight_log(flight_plan, curr_time, drone):
|
||||
new_log = ""
|
||||
|
||||
commands = flight_plan.split("\n")
|
||||
for command in commands:
|
||||
if not command.strip():
|
||||
continue
|
||||
|
||||
parts = command.split()
|
||||
if len(parts) == 0:
|
||||
continue
|
||||
|
||||
expected_time = int(parts[0])
|
||||
if not (curr_time > expected_time or abs(curr_time - expected_time) < 0.2):
|
||||
continue
|
||||
|
||||
command_name = parts[1]
|
||||
args = parts[2:]
|
||||
func = globals().get(command_name)
|
||||
if func and callable(func):
|
||||
try:
|
||||
typed_args = []
|
||||
if len(args) > 0 and args[0] == "[drone]":
|
||||
typed_args.append(drone)
|
||||
args = args[1:]
|
||||
|
||||
typed_args.extend(
|
||||
[
|
||||
int(arg) if arg.replace("-", "", 1).isdigit() else arg
|
||||
for arg in args
|
||||
]
|
||||
)
|
||||
new_log += str(func(*typed_args)) + "\n"
|
||||
except Exception as e:
|
||||
print(f"Error calling {command_name}: {e}", flush=True)
|
||||
else:
|
||||
print(f"Unknown command: {command_name}", flush=True)
|
||||
return new_log
|
||||
|
||||
|
||||
def generate_plan(min_count, max_count):
|
||||
num_commands = random.randint(min_count, max_count)
|
||||
commands = []
|
||||
|
||||
for _ in range(num_commands):
|
||||
command_type = random.choice(["BOOSTX", "BOOSTY", "FIRE", "SETFREQ"])
|
||||
|
||||
if command_type == "BOOSTX":
|
||||
commands.append(
|
||||
f"{random.randint(0, 8)} BOOSTX [drone] {random.randint(-2, 2)}"
|
||||
)
|
||||
elif command_type == "BOOSTY":
|
||||
commands.append(
|
||||
f"{random.randint(0, 8)} BOOSTY [drone] {random.randint(-2, 2)}"
|
||||
)
|
||||
elif command_type == "FIRE":
|
||||
commands.append(f"{random.randint(0, 8)} FIRE [drone]")
|
||||
else:
|
||||
commands.append(
|
||||
f"{random.randint(0, 8)} SETFREQ [drone] {random.randint(10, 100)}"
|
||||
)
|
||||
|
||||
return commands
|
||||
4
ctfcup24-school-ad/checkers/flysim/requirements.txt
Normal file
4
ctfcup24-school-ad/checkers/flysim/requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
requests==2.32.3
|
||||
python-socketio==5.11.4
|
||||
checklib==0.7.0
|
||||
websocket-client==1.8.0
|
||||
Reference in New Issue
Block a user