From 5a21bf8a591721b2e55927902309daf73248be84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Wed, 4 Sep 2024 01:10:18 +0200 Subject: updated napad --- iv/orodja/napad/config | 96 --------------------------- iv/orodja/napad/exploit.sh | 1 + iv/orodja/napad/genconfig.sh | 114 ++++++++++++++++++++++++++++++++ iv/orodja/napad/index.html | 4 -- iv/orodja/napad/nadzor.py | 36 ++++++++++ iv/orodja/napad/submission.py | 95 +------------------------- iv/orodja/napad/templates/frontend.html | 58 ++++++++++++++++ 7 files changed, 212 insertions(+), 192 deletions(-) delete mode 100644 iv/orodja/napad/config create mode 100755 iv/orodja/napad/genconfig.sh delete mode 100644 iv/orodja/napad/index.html create mode 100755 iv/orodja/napad/nadzor.py create mode 100644 iv/orodja/napad/templates/frontend.html diff --git a/iv/orodja/napad/config b/iv/orodja/napad/config deleted file mode 100644 index dc165ce..0000000 --- a/iv/orodja/napad/config +++ /dev/null @@ -1,96 +0,0 @@ -# Common config for exploit.sh and submission.py. -# It is to be sourced. It only sets environment variables. - -# ========================== -# ========= COMMON ========= - -export SUBMISSION_PORT=21502 - -# ========================== -# ======= EXPLOIT.SH ======= - -# This regex is used to grep -Eo flags from stdout of exploits before submitting them -export FLAG_REGEX_SEARCH="[A-Za-z0-9]{31}=" - -# Where can exploit.sh find submission.py. Port is a common setting. -export SUBMISSION_HOST=localhost -### export SUBMISSION_HOST=k.4a.si - -# Must be precise, not less than round duration. Used to calculate round id. -export ROUND_DURATION=120 - -# When does the game start (in UTC). Used to calculate current round id. -export GAME_START=2024-09-01T07:00:00 - -# Team numbers to attack -export GAME_TEAMS="2 69" -###export GAME_TEAMS={0..10} - -# Flag IDs URL -game_flag_ids_url() -{ - echo http://splet.4a.si/dir/flagids.txt - ### echo "http://10.10.0.1:8081/flagIds?service=$1&team=$2&round=$3" -} -export -f game_flag_ids_url - -# Target IP from ID -game_target_ip() -{ - echo 10.69.69.$1 - ### echo 10.60.$1.1 -} -export -f game_target_ip - -# NOP TEAM ID -export GAME_NOP_TEAM=0 - -# For how many non-current rounds are flags valid at a time? -# It doesn't make sense for this to be less than 0. -# Setting to 0 means only the current round is valid. -export GAME_VALID_ROUNDS=5 - -# Function exploit.sh should call on errors. -# Args: service team pwd usr@pc message -# 1 2 3 4 5 -exploit_error_handler() -{ - notify-send --version > /dev/null && notify-send "exploit.sh ERROR" "$5" --urgency critical -} -export -f exploit_error_handler - -# Max exploit execution time -export EXPLOIT_TIMEOUT=5 - -# ========================== -# ====== SUBMISSION.PY ===== - -# This regex is used to verify flags before storing them -# It can be .*, no problem, just make sure you're then not sending invalid flags -# to submission TCP -- you shouldn't anyways, as submission expects flags neatly -# line by line, it will not clean up random bullshit. -# Don't just send exploit stdout to submission, use exploit.sh! -export FLAG_REGEX_MATCH="^[A-Z0-9]{31}=$" - -# Where to store flags -- sqlite3 db -export SUBMISSION_DB=flags.db - -# How much flags to send in one request. -# With 2560, if it takes 37 bytes per flag, 2560*37=94720 -# Ostane nam torej še dobrih 5280 za headerje, -# če je request limited na 100 kB -export SUBMISSION_MAX_FLAGS=2560 - -# PUT request, ECSC 2024 AD style -export SUBMISSION_URL=http://z.4a.si/dir/submit.php -### export SUBMISSION_URL=http://10.10.0.1:8080/flags - -# How many seconds to delay after a successful submission. -# With 15, we send at most 4 requests per minute out of 15 allowed. -export SUBMISSION_DELAY=15 - -# This is sent in X-Team-Token in requests to SUBMISSION_URL -export SUBMISSION_TEAM_TOKEN=e5152d70a4d18093cae8844f4e959cf1 - -# Where to bind to. Use SUBMISSION_PORT in common settings for port. -export SUBMISSION_BIND=:: diff --git a/iv/orodja/napad/exploit.sh b/iv/orodja/napad/exploit.sh index b482c06..9e2cafb 100755 --- a/iv/orodja/napad/exploit.sh +++ b/iv/orodja/napad/exploit.sh @@ -25,6 +25,7 @@ Set the following environment variables to alter behaviour: waiting for the next round. Only valid for loop subcommand. EXPLOIT_VERBOSE=1: print _every_ line executed by $0 (set -x) EXPLOIT_NOTPARALLEL=1: disable parallel even if parallel is available +$EXPLOIT_ADDITIONAL_HELP_TEXT EOF exit 1 fi diff --git a/iv/orodja/napad/genconfig.sh b/iv/orodja/napad/genconfig.sh new file mode 100755 index 0000000..825da18 --- /dev/null +++ b/iv/orodja/napad/genconfig.sh @@ -0,0 +1,114 @@ +#!/bin/bash +set -xeuo pipefail +statusresp=`curl --fail-with-body --no-progress-meter https://ad.ecsc2024.it/api/status` +starttime=`jq --raw-output .start <<<"$statusresp"` +roundtime=`jq --raw-output .roundTime <<<"$statusresp"` +team_names=`jq --raw-output .teams.[].shortname <<<"$statusresp" | tr $'\n' ' '` +team_numbers=`jq --raw-output .teams.[].id <<<"$statusresp" | tr $'\n' ' '` +services=`jq --raw-output .services.[].shortname <<<"$statusresp" | tr $'\n' ' '` +cat < /dev/null && notify-send "exploit.sh ERROR" "$5" --urgency critical +} +export -f exploit_error_handler + +# Max exploit execution time +export EXPLOIT_TIMEOUT=5 + +# ========================== +# ====== SUBMISSION.PY ===== + +# This regex is used to verify flags before storing them +# It can be .*, no problem, just make sure you're then not sending invalid flags +# to submission TCP -- you shouldn't anyways, as submission expects flags neatly +# line by line, it will not clean up random bullshit. +# Don't just send exploit stdout to submission, use exploit.sh! +export FLAG_REGEX_MATCH="^[A-Z0-9]{31}=$" + +# Where to store flags -- sqlite3 db +export SUBMISSION_DB=flags.db + +# How much flags to send in one request. +# With 2560, if it takes 37 bytes per flag, 2560*37=94720 +# Ostane nam torej še dobrih 5280 za headerje, +# če je request limited na 100 kB +export SUBMISSION_MAX_FLAGS=2560 + +# PUT request, ECSC 2024 AD style +export SUBMISSION_URL=http://z.4a.si/dir/submit.php +### export SUBMISSION_URL=http://10.10.0.1:8080/flags + +# How many seconds to delay after a successful submission. +# With 15, we send at most 4 requests per minute out of 15 allowed. +export SUBMISSION_DELAY=15 + +# This is sent in X-Team-Token in requests to SUBMISSION_URL +export SUBMISSION_TEAM_TOKEN=e5152d70a4d18093cae8844f4e959cf1 + +# Where to bind to. Use SUBMISSION_PORT in common settings for port. +export SUBMISSION_BIND=:: + +# ========================== +# ======== NADZOR.PY ======= +EOF diff --git a/iv/orodja/napad/index.html b/iv/orodja/napad/index.html deleted file mode 100644 index 6c5d495..0000000 --- a/iv/orodja/napad/index.html +++ /dev/null @@ -1,4 +0,0 @@ - - -submission.py -

submission.py

diff --git a/iv/orodja/napad/nadzor.py b/iv/orodja/napad/nadzor.py new file mode 100755 index 0000000..515aa3e --- /dev/null +++ b/iv/orodja/napad/nadzor.py @@ -0,0 +1,36 @@ +#!/usr/bin/python3 +from flask import Flask, render_template, request +import os +import sqlite3 +import sys + + +app = Flask(__name__) + +@app.route("/", methods=["GET"]) +def frontend(): + return render_template("frontend.html") + +@app.route("/sql", methods=["POST"]) +def sql(): + with sqlite3.connect(os.getenv("SUBMISSION_DB", "flags.db")) as db: + db.setconfig(sqlite3.SQLITE_DBCONFIG_DEFENSIVE, True) + rows = [] + for row in db.execute(request.data.decode()): + columns = [] + for column in row: + if type(column) == bytes: + columns.append(column.decode("utf-8", errors="surrogateescape")) + else: + columns.append(column) + rows.append(columns) + return rows + +if __name__ == "__main__": + port = 21503 + host = "::" + if len(sys.argv) > 1: + port = int(sys.argv[1]) + if len(sys.argv) > 2: + host = sys.argv[2] + app.run(port=port, debug=True, host=host) diff --git a/iv/orodja/napad/submission.py b/iv/orodja/napad/submission.py index 4a44947..c345bdb 100755 --- a/iv/orodja/napad/submission.py +++ b/iv/orodja/napad/submission.py @@ -5,9 +5,8 @@ import re import sqlite3 import aiohttp import traceback -import json db = sqlite3.connect(os.getenv("SUBMISSION_DB", "flags.db")) -db.execute("CREATE TABLE IF NOT EXISTS flags (id INTEGER PRIMARY KEY, flag TEXT NOT NULL UNIQUE, team INTEGER, service BLOB, round INTEGER, context BLOB, sent INTEGER NOT NULL DEFAULT 0, date TEXT DEFAULT (strftime('%FT%R:%f', 'now')) NOT NULL, status TEXT, msg TEXT) STRICT") +db.execute("CREATE TABLE IF NOT EXISTS flags (id INTEGER PRIMARY KEY, flag TEXT NOT NULL UNIQUE, team INTEGER, service BLOB, round INTEGER, context BLOB, sent INTEGER NOT NULL DEFAULT 0, date TEXT DEFAULT (strftime('%FT%R:%f', 'now')) NOT NULL, status TEXT, msg TEXT, submitted TEXT) STRICT") # submitted is date flag_regex = re.compile(os.getenv("FLAG_REGEX_MATCH", "^[A-Z0-9]{31}=$").encode(), re.ASCII | re.DOTALL | re.VERBOSE) async def submitter (): while True: @@ -40,18 +39,14 @@ async def submitter (): break cursor = db.cursor() for obj in await response.json(): - cursor.execute("UPDATE flags SET sent=?, status=?, msg=? WHERE flag=?", [int(obj.get("status") != "RESUBMIT"), obj.get("status"), obj.get("msg"), obj.get("flag")]) + cursor.execute("UPDATE flags SET sent=?, status=?, msg=?, submitted=strftime('%FT%R:%f', 'now') WHERE flag=?", [int(obj.get("status") != "RESUBMIT"), obj.get("status"), obj.get("msg"), obj.get("flag")]) db.commit() except Exception as e: traceback.print_exc() await asyncio.sleep(int(os.getenv("SUBMISSION_DELAY", "15"))) async def handle_client (reader, writer): - linenumber = -1 - http_request = None - http_headers = dict() while True: - linenumber += 1 - try: + try: # SUBMISSION LINE FORMAT: "flag teamnumber roundnumber service any other context" incoming = await reader.readuntil(b'\n') except asyncio.exceptions.IncompleteReadError as e: if int(str(e).split(" ")[0]) == 0: @@ -60,93 +55,9 @@ async def handle_client (reader, writer): if len(incoming) == 0: break buffer = incoming.replace(b'\r', b'').replace(b'\n', b'') - if http_request: - if len(buffer) == 0: - if http_request[1] == b"/": - with open("index.html", "rb") as index: - if http_request[0] != b"GET": - writer.write(b'HTTP/1.0 405 Method Not Allowed\r\nContent-Type: text/plain\r\n\r\n405 Method Not Allowed. Try GET.\r\n') - break - writer.write(b'HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') - writer.write(index.read()) - break - elif http_request[1] == b"/python": - if http_request[0] != b"POST": - writer.write(b'HTTP/1.0 405 Method Not Allowed\r\nContent-Type: text/plain\r\n\r\n405 Method Not Allowed. Try POST.\r\n') - break - if b'content-length' not in http_headers.keys(): - writer.write(b'HTTP/1.0 411 Length Required\r\nContent-Type: text/plain\r\n\r\n411 Length Required.\r\n') - break - post_body = None - try: - post_body = await reader.read(int(http_headers.get(b'content-length').decode())) - except Exception as e: - writer.write(b'HTTP/1.0 400 Bad Request\r\nContent-Type: text/plain\r\n\r\nBad request. ' + str(e).encode() + b"\r\n") - raise e - break - try: - writer.write(b"HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n" + eval(post_body)) - break - except Exception as e: - writer.write(b"HTTP/1.0 500 Internal Server Error\r\nContent-Type: text/plain\r\n\r\n" + str(e).encode() + b"\r\n") - raise e - break - elif http_request[1] == b"/sql": - if http_request[0] != b'POST': - writer.write(b'HTTP/1.0 405 Method Not Allowed\r\nContent-Type: text/plain\r\n\r\n405 Method Not Allowed. Try POST.\r\n') - break - if b'content-length' not in http_headers.keys(): - writer.write(b'HTTP/1.0 411 Length Required\r\nContent-Type: text/plain\r\n\r\n411 Length Required.\r\n') - break - post_body = None - try: - post_body = (await reader.read(int(http_headers.get(b'content-length').decode()))).decode() - except Exception as e: - writer.write(b'HTTP/1.0 400 Bad Request\r\nContent-Type: text/plain\r\n\r\nBad request. ' + str(e).encode() + b"\r\n") - raise e - break - try: - rows = [] - for row in db.execute(post_body): - columns = [] - for column in row: - if type(column) == bytes: - columns.append(column.decode("utf-8", errors="surrogateescape")) - else: - columns.append(column) - rows.append(columns) - response = json.dumps(rows, ensure_ascii=False, indent=1) - except Exception as e: - writer.write(b'HTTP/1.0 500 Internal Server Error\r\nContent-Type: text/plain\r\n\r\n500 Internal Server Error. ' + str(e).encode() + b"\r\n") - raise e - break - else: - writer.write(b'HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n' + response.encode()) - break - else: - writer.write(b'HTTP/1.0 404 Not Found\r\nContent-Type: text/plain\r\n\r\n404 Not Found') - break - splitbuf = buffer.split(b': ') - headername = splitbuf.pop(0).lower() - http_headers[headername] = b': '.join(splitbuf) - continue - if linenumber == 0 and re.match(b'[A-Z]+ [/A-Za-z0-9?&=%+~]+ HTTP/[0-9.]+', buffer): - http_request = buffer.split(b' ') - continue - if buffer.startswith(b' '): - for row in db.execute(buffer[1:].decode()): - writer.write(str(row).encode() + b'\n') - continue - if buffer.startswith(b'@'): - writer.write(str(db.execute(buffer[1:].decode()).fetchall()).encode() + b'\n') - continue - if buffer.startswith(b'#'): - writer.write(str(len(db.execute(buffer[1:].decode()).fetchall())).encode() + b'\n') - continue if re.match(flag_regex, buffer.split(b' ')[0]) == None: writer.write(b'BAD_FLAG\n') continue - # SUBMISSION LINE FORMAT: "flag teamnumber roundnumber service any other context" flag = buffer.split(b' ')[0].decode() context = b' '.join(buffer.split(b' ')[1:]) try: diff --git a/iv/orodja/napad/templates/frontend.html b/iv/orodja/napad/templates/frontend.html new file mode 100644 index 0000000..8f0389d --- /dev/null +++ b/iv/orodja/napad/templates/frontend.html @@ -0,0 +1,58 @@ + + + +napad/nadzor.py +

napad/nadzor.py

+ + + + + + + +
+ime podatka + +vrednost +
čas zadnje ACCEPTED zastavice
neposlanih zastavic
+
+ + +
+ -- cgit v1.2.3