← Back to Writeups
CUSTOMMediumpwn

bad_mood

XESXOR9/2/20263 min read
#pwn#custom#medium#logic_flaw_exploitation#source_code_analysis#linux

bad_mood

Platform: grodno_new_year_2026 | Category: pwn | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-11 | Status: Solved Techniques: logic_flaw_exploitation, source_code_analysis

Summary

Task: Heap-based note manager binary with an off-by-one bug and an admin check function that compares a hash against itself. Solution: Ignored the red herring heap vulnerability and simply called the check_admin function (menu option 5), which always passes because admin_hash is computed from the same admin_key at startup.

bad_mood - grodno_new_year_2026

Description (Russian)

У Василия сегодня плохое настроение. Он освобождает старые мысли, путает новые и иногда забывает, кто он. Но в глубине памяти его имя всё ещё где-то есть.

Description (English)

Vasily is in a bad mood today. He's letting go of old thoughts, tangling up new ones, and sometimes forgetting who he is. But somewhere in the depths of his memory, his name still lingers.

Binary Analysis

Files provided:

  • bad_mood (ELF 64-bit PIE binary)
  • libc.so.6 (glibc 2.41 Debian)
  • ld-linux-x86-64.so.2
  • libgcc_s.so.1 ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Binary features:

  • Heap-based note manager with menu:
    1. Alloc - allocate note (index 0-7, size 0=small 0x30 / 1=large 0x100)
    2. Free - free note (sets pointer to NULL)
    3. Edit - edit note (has off-by-one bug: reads size+1 bytes)
    4. Read - read note content
    5. Check admin - compares hash(admin_key) with admin_hash

Key BSS variables:

  • notes[8] at 0x4060
  • sizes[8] at 0x40a0
  • admin_key at 0x40e0 (set to "VASILIY__SECRET" at startup)
  • admin_hash at 0x40f0 (set to hash(admin_key) at startup) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Vulnerability found: off-by-one in edit_note function

Analysis

The challenge description strongly hints at heap exploitation:

  • "освобождает старые мысли" (frees old thoughts) = free()
  • "путает новые" (tangles up new ones) = heap corruption
  • "забывает, кто он" (forgets who he is) = memory corruption

However, the actual solution is much simpler!

The check_admin Function

c
void check_admin() {
    uint64_t computed_hash = fnv1a_hash(admin_key);
    if (computed_hash == admin_hash) {
        char* flag = getenv("FLAG_VAL");
        if (flag) {
            printf("Flag: %s\n", flag);
        }
    } else {
        printf("Access denied!\n");
    }
}

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The main() Initialization

c
int main() {
    strcpy(admin_key, "VASILIY__SECRET");
    admin_hash = fnv1a_hash(admin_key);
    // ... menu loop
}

The Logic Flaw

At program startup:

  1. admin_key is set to "VASILIY__SECRET"
  2. admin_hash is set to hash(admin_key)

When we call option 5 (check_admin):

  1. It computes hash(admin_key) again
  2. Compares with stored admin_hash
  3. Since nothing modifies either value before we interact, they always match!

The off-by-one vulnerability in the edit function was either:

  • A red herring to waste time
  • Intended for a harder version of the challenge
  • Left over from development ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Just call menu option 5 immediately:

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

p = remote('ctf.mf.grsu.by', 9073)
p.sendlineafter(b'> ', b'5')  # Just call check_admin
response = p.recvline()
print(f"Flag: {response.decode().strip()}")
p.close()

Lessons Learned

  1. Always check the simple path first - Before diving into heap exploitation, verify if the intended goal can be achieved trivially
  2. Read the code flow carefully - The vulnerability was real but irrelevant to getting the flag
  3. Challenge descriptions can be misleading - The heap-related hints were red herrings
  4. Logic bugs > Memory corruption - Sometimes the easiest path is a logic flaw, not a memory safety issue ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Alternative Approach (Not Needed)

If the challenge required modifying admin_key or admin_hash, the off-by-one could be used for:

  • Tcache poisoning to get arbitrary write
  • Overwriting BSS variables via heap overflow
  • Leaking heap addresses for further exploitation

But none of this was necessary for this challenge. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍