adding validated services? patching forcad_local.py
This commit is contained in:
77
ctfcup24-school-ad/checkers/filtranator/checker.py
Executable file
77
ctfcup24-school-ad/checkers/filtranator/checker.py
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import requests
|
||||
import json
|
||||
from checklib import *
|
||||
from filtranator import *
|
||||
|
||||
|
||||
def strcmp(str1,str2):
|
||||
if (len(str1) != len(str2)):
|
||||
return False
|
||||
for i in range(len(str1)):
|
||||
if(str1[i] != str2[i]):
|
||||
if((str1[i] in os and str2[i] in os) or (str1[i] in ixs and str2[i] in ixs) or (str1[i] in qs and str2[i] in qs)):
|
||||
continue
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
class Checker(BaseChecker):
|
||||
vulns: int = 1
|
||||
timeout: int = 10
|
||||
uses_attack_data: bool = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Checker, self).__init__(*args, **kwargs)
|
||||
self.mch = CheckMachine(self)
|
||||
|
||||
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 connection error")
|
||||
|
||||
def check(self):
|
||||
session = get_initialized_session()
|
||||
username, password = rnd_username(), rnd_password()
|
||||
|
||||
self.mch.register(session, username, password, Status.MUMBLE)
|
||||
self.mch.login(session, username, password, Status.MUMBLE)
|
||||
img_value = rnd_string(20).upper()
|
||||
self.mch.put_image(session,img_value,Status.MUMBLE)
|
||||
value = self.mch.get_image(session)
|
||||
self.assert_(img_value.replace('0','O').replace('1','I').replace('l','I'), value.replace('0','O').replace('1','I').replace('l','I'), Status.MUMBLE)
|
||||
self.mch.logout(session,Status.MUMBLE)
|
||||
self.cquit(Status.OK)
|
||||
|
||||
def put(self, flag_id: str, flag: str, vuln: str):
|
||||
session = get_initialized_session()
|
||||
username, password = rnd_username(), rnd_password()
|
||||
|
||||
self.mch.register(session, username, password, Status.MUMBLE)
|
||||
self.mch.login(session, username, password, Status.MUMBLE)
|
||||
self.mch.put_image(session, flag, Status.MUMBLE)
|
||||
self.mch.logout(session, Status.MUMBLE)
|
||||
self.cquit( Status.OK,public=json.dumps({"username":username}),
|
||||
private=f"{username}:{password}",
|
||||
)
|
||||
|
||||
def get(self, flag_id: str, flag: str, vuln: str):
|
||||
s = get_initialized_session()
|
||||
username, password = flag_id.split(":")
|
||||
self.mch.login(s, username, password, Status.CORRUPT)
|
||||
value = self.mch.get_image(s)
|
||||
self.mch.logout(s, Status.CORRUPT)
|
||||
self.assert_(value.upper().replace('0','O').replace('1','I').replace('l','I'), flag.upper().replace('0','O').replace('1','I').replace('l','I'), Status.CORRUPT)
|
||||
self.cquit(Status.OK)
|
||||
|
||||
|
||||
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)
|
||||
91
ctfcup24-school-ad/checkers/filtranator/filtranator.py
Normal file
91
ctfcup24-school-ad/checkers/filtranator/filtranator.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import requests
|
||||
import pytesseract
|
||||
import io
|
||||
import sys
|
||||
from PIL import Image, ImageFont, ImageDraw, ImageColor
|
||||
from checklib import *
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
|
||||
PORT = 6969
|
||||
|
||||
|
||||
def text_to_image(
|
||||
text: str,
|
||||
font_filepath: str,
|
||||
font_size: int,
|
||||
color: (int, int, int), # color is in RGB
|
||||
font_align="center",
|
||||
):
|
||||
#font = ImageFont.load_default(18)
|
||||
font = ImageFont.load_default(18)
|
||||
#box = font.getsize_multiline(text)
|
||||
img = Image.new("RGBA", (400,400),color='black')
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw_point = (0, 0)
|
||||
draw.multiline_text(draw_point, text, font=font, fill=color, align=font_align)
|
||||
return img
|
||||
|
||||
|
||||
class CheckMachine:
|
||||
@property
|
||||
def url(self):
|
||||
return f"http://{self.c.host}:{self.port}"
|
||||
|
||||
def __init__(self, checker: BaseChecker):
|
||||
self.c = checker
|
||||
self.port = PORT
|
||||
|
||||
def register(self, session: requests.Session, username: str, password: str,status: Status.MUMBLE):
|
||||
url = f"{self.url}/register"
|
||||
resp = session.post(url, data={"username": username, "password": password})
|
||||
self.c.assert_(resp.text, "<p1>Sucessfully registered</p1>")
|
||||
resp = session.post(
|
||||
url, data={"username": username, "password": password}
|
||||
)
|
||||
self.c.assert_(resp.text, "<p1>User alredy exist</p1>",status)
|
||||
|
||||
def logout(self, session: requests.Session,status: Status.MUMBLE):
|
||||
url = f"{self.url}/logout"
|
||||
resp = session.get(url)
|
||||
self.c.assert_(resp.text,'<p1>Sucessfully logout</p1>',status)
|
||||
|
||||
def login(
|
||||
self, session: requests.Session, username: str, password: str, status: Status
|
||||
):
|
||||
url = f"{self.url}/login"
|
||||
resp = session.post(url, data={"username": username, "password": password})
|
||||
self.c.assert_(resp.text, "<p1>Sucessfully logged in</p1>",status)
|
||||
resp = session.post(url, data={"username": username, "password": password})
|
||||
self.c.assert_(resp.text, "<p1>Already logged in</p1>",status)
|
||||
|
||||
def put_image(self, session: requests.Session, text: str,status: Status.MUMBLE):
|
||||
url = f"{self.url}/apply_filter"
|
||||
kek = text_to_image(
|
||||
text,
|
||||
"./MonaSansCondensed-Black.otf",
|
||||
40,
|
||||
(120, 120, 120),
|
||||
)
|
||||
metadata = PngInfo()
|
||||
metadata.add_text("flag", text)
|
||||
bytex = io.BytesIO()
|
||||
kek.save(bytex,format='PNG',pnginfo = metadata)
|
||||
imgkek = bytex.getvalue()
|
||||
files = {"image": ("img", imgkek, "multipart/form-data", {"Expires": "0"})}
|
||||
resp = session.post(
|
||||
url,
|
||||
data={"filter": "none", "filename": "flag"},
|
||||
files=files,
|
||||
)
|
||||
self.c.assert_(resp.text, "<p1>Image saved</p1>",status)
|
||||
|
||||
def get_image(self, session: requests.Session) -> str:
|
||||
url = f"{self.url}/images"
|
||||
resp = session.get(url)
|
||||
img_bytes = io.BytesIO(resp.content)
|
||||
if img_bytes.getbuffer().nbytes == 0:
|
||||
return ''
|
||||
img = Image.open(img_bytes)
|
||||
print(img.text.keys(),file=sys.stderr)
|
||||
text = img.text['flag']#pytesseract.image_to_string(img)
|
||||
return text
|
||||
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
|
||||
6
ctfcup24-school-ad/checkers/requirements.txt
Normal file
6
ctfcup24-school-ad/checkers/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
requests==2.32.3
|
||||
python-socketio==5.11.4
|
||||
websocket-client==1.8.0
|
||||
checklib==0.7.0
|
||||
pillow == 11
|
||||
pytesseract
|
||||
Reference in New Issue
Block a user