pwnfreemedium

ReplaceMe

hackthebox

Task: sed-like string replacement utility with read() not adding null terminator and contiguous BSS buffers. Solution: 3-pass ret2libc exploiting strlen inflation across buffer boundary, partial return address overwrite for PIE leak, GOT read for libc leak.

$ ls tags/ techniques/
multi_pass_ret2libcpartial_ret_addr_overwritepie_leak_via_fputslibc_leak_via_gotbss_contiguous_strlen_inflationlocal_variable_overwrite_controlstack_alignment_tracking

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

ReplaceMe — HackTheBox

Description

"This useful interactive SED-like utility was shared with me to use, can you make sure it is safe?"

64-bit ELF PIE binary, dynamically linked, not stripped. Implements an interactive string replacement utility in sed format (s/old/new/). Takes an input string and a replacement pattern, performs substitution, and outputs the result.

Remote target: 154.57.164.68:32476.

Files: replaceme (binary), libc.so.6 (Ubuntu GLIBC 2.31-0ubuntu9.14), Dockerfile (Ubuntu 20.04, socat), flag.txt.

Protections

ProtectionStatus
PIEEnabled
NXEnabled
CanaryNo
RELROFull (GOT not writable, but readable)

Analysis

Key Functions

main()

Sequentially reads two inputs into BSS:

  • input (BSS 0x4040, 128 bytes) — string to process
  • replacement (BSS 0x40c0, 128 bytes) — replacement pattern in s/old/new/ format

Then calls do_replacement().

ask_input(prompt, buffer, size)

Uses read(0, buffer, size)does not add null terminator. This is critical: if all 128 bytes of input are filled, strlen() will continue reading into replacement.

do_replacement() — vulnerable function

Parses s/old/new/, finds old in input, builds result: prefix + new + suffix into stack buffer result[128] via memcpy without bounds checking.

rbp-0xc0: result[128]        ← buffer start
rbp-0x40: padding
rbp-0x3c: after_len           ← suffix length (int)
rbp-0x38: wp                  ← write pointer
rbp-0x30: match               ← match pointer
rbp-0x24: new_len
rbp-0x20: end_slash
rbp-0x14: old_len
rbp-0x10: mid_slash
rbp-0x08: old_start
rbp+0x00: saved RBP
rbp+0x08: return address      ← result + 0xc8 = result[200]

BSS Layout

input (0x4040) and replacement (0x40c0) are located contiguously in BSS. When input is filled with all 128 bytes without null terminator, strlen() continues counting bytes from replacement, inflating after_len.

Vulnerability

...

$ grep --similar

Similar writeups