Skip to content

ACSC Simplesample Writeup

Published: Aryt3 Änderungen vorschlagen

Simplesample

Description

We have gained access to the genome browser of a large pharmaceutical company. 
I'm sure they're hiding something in there.

Provided Files

- simplesample.zip

Writeup

Starting off, I inspected the provided code.

Main Application Code:

import os
import sqlite3
from datetime import timedelta

from flask import Flask, render_template, request
from decorators import compressed, rate_limited


WEB_PORT = int(os.getenv("WEB_PORT", "5000"))
FLAG = os.getenv("FLAG")
RATE_LIMIT = timedelta(seconds=int(os.getenv("RATE_LIMIT_SECONDS", 5)))  # don't want abuse of our api


DATABASE = 'genomes.db'
def get_db_connection():
    conn = sqlite3.connect(f"file:{DATABASE}?mode=ro")
    conn.row_factory = sqlite3.Row  # so we can access columns by name
    return conn


def add_flag():
    conn = sqlite3.connect(f"file:{DATABASE}?mode=rw")
    conn.execute("INSERT INTO genomes (name, description, sequence, restricted) VALUES (?, ?, ?, ?)", ("ACSC", "Unknown mutation, under evaluation", FLAG, 1))
    conn.commit()
    conn.close()


app = Flask(__name__)


@app.route('/', methods=['GET'])
@rate_limited(RATE_LIMIT)
@compressed
def index():
    query = request.args.get('query')
    had_query = bool(query)
    if not query:  # display default query
        query = "GGAGGTGGGTTCGAGCCCAACTTCATGCTCTTCGAGAAGTGCGAGGTGAACGGTGCGGGG"

    try:
        conn = get_db_connection()
        results = conn.execute(f"SELECT * FROM genomes WHERE sequence LIKE '%{query}%'").fetchall()
        conn.close()
    except sqlite3.OperationalError:
        results = []

    if any(result['restricted'] == 1 for result in results):
        results = [{"id": -1, "sequence": "Restricted results"}]
    elif len(results) > 4:
        results = [{"id": -1, "sequence": "Too many results"}]

    if not had_query:  # no need to show default query
        query = None

    return render_template('index.html', results=results, query=query)


if __name__ == '__main__':
    add_flag()
    app.run("0.0.0.0", port=WEB_PORT, debug=False)

The flag was inserted into the genomes table which we can filter.
Additionally a restricted parameter was added so that we can’t just simply grab the flag.
Our query filters the genomes based on the sequence column where the flag is also stored in.
Knowing this we could theoretically bruteforce the flag character by character exploiting the '%' sql-syntax and knowing the prefix dach2025{.
If the flag would be returned we would get the error Restricted results otherwise we would get no results.

Inside the Index API-endpoint an f-string is being used for sql-query which indicates an SQLi.
To extract the flag we can simply use a UNION-SQLi by grabbing the flag and setting the restricted parameter to false.
Using the query aaaaa' UNION SELECT NULL, NULL, NULL, sequence, 2 FROM genomes where sequence LIKE 'dach2025{%'; -- sets restricted column to false returning the actual flag and solving the challenge.

dach2025{infosec_in_biosec_is_not_that_easy_it_seems_08yi8cigt7ofc0hx}

Letzter Beitrag
ACSC Rewire Writeup
Nächster Beitrag
Vorstellung von AlpSec