From 3241f1478f81376df4cc9673fa039cc1c0dd4d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Tue, 3 Sep 2024 23:18:54 +0200 Subject: making submission safe again ): separating rce to another process --- iv/orodja/napad/index.html | 4 +++ iv/orodja/napad/submission.py | 78 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 iv/orodja/napad/index.html diff --git a/iv/orodja/napad/index.html b/iv/orodja/napad/index.html new file mode 100644 index 0000000..6c5d495 --- /dev/null +++ b/iv/orodja/napad/index.html @@ -0,0 +1,4 @@ + + +submission.py +

submission.py

diff --git a/iv/orodja/napad/submission.py b/iv/orodja/napad/submission.py index fce0a27..4a44947 100755 --- a/iv/orodja/napad/submission.py +++ b/iv/orodja/napad/submission.py @@ -5,6 +5,7 @@ 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") flag_regex = re.compile(os.getenv("FLAG_REGEX_MATCH", "^[A-Z0-9]{31}=$").encode(), re.ASCII | re.DOTALL | re.VERBOSE) @@ -45,7 +46,11 @@ async def submitter (): 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: incoming = await reader.readuntil(b'\n') except asyncio.exceptions.IncompleteReadError as e: @@ -55,6 +60,79 @@ 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') -- cgit v1.2.3