RiseFromTheDead (rev) – CTF Write-up

Challenge Overview

One of our greatest scientists died recently, but we need them to cure the zombie virus. Can you raise them from the dead and get the help we need?

  • Category: Reversing
  • Files: rise, core
  • Goal: Recover the original flag from a shuffled copy and the memory left behind in a core dump

Download the challenge

Initial Reconnaissance

Running file shows that rise is a 64-bit PIE executable and core is the dump produced by running ./rise flag:

file core rise

The file command identifies rise as an x86-64 PIE and core as its core dump

The executable is not stripped, so the interesting functions retain useful names.

Static Analysis

The main function opens the file passed on the command line and maps 0x1000 bytes into memory with mmap. It then builds a shuffle list, rearranges the mapped data and deliberately raises SIGSEGV with kill(0, 11).

IDA decompilation of the main function mapping the file and calling the shuffle routines

The linked-list nodes can be represented as:

struct node {
    struct node *next;
    uint8_t pos;
    char chr;
};

Building the shuffle list

init_shuffle_list() reads one byte at a time from /dev/urandom. Every unique byte up to 0xaf becomes the position of a new node; the corresponding flag character is stored beside it.

IDA decompilation of init_shuffle_list reading positions from dev urandom

The result is a linked list containing the original characters in order, together with the random positions where they will be written.

Shuffling the mapped flag

shuf() walks that list and performs the important assignment:

output[node->pos] = node->chr;
node->chr = 0;

IDA decompilation of shuf writing each character to its randomized position

The mapped file therefore contains the shuffled flag. The list still preserves the order and positions needed to reverse the operation, but its character fields have been cleared.

Recovering State From the Core Dump

A core dump is a snapshot of a process at the moment it crashes. Here, that means both pieces of the puzzle survive:

  • the file-backed mapping contains the shuffled flag;
  • the anonymous heap mapping contains the linked list.

The NT_FILE note confirms that the dump includes mappings for the executable and /mnt/flag.

Core dump file mappings including the mapped flag file

Instead of copying indices out of GDB by hand, pwntools can parse the dump directly. The solver locates the anonymous heap and flag mappings, finds the first cleared list node, then follows each next pointer and indexes into the shuffled data.

from pwn import Corefile
import struct

core = Corefile("./core")

heap = next(mapping for mapping in core.mappings if mapping.name == "")
flag_mapping = next(
    mapping for mapping in core.mappings if "flag" in mapping.name
)
shuffled_flag = core.read(
    flag_mapping.start, flag_mapping.size
).replace(b"\x00", b"")

list_head = None
for address in range(heap.start, heap.stop, 16):
    data = core.read(address, 16)
    next_node, position, character = struct.unpack("PBB", data[:10])
    if next_node in heap and position != 0 and character == 0:
        list_head = address
        break

flag = ""
while list_head:
    data = core.read(list_head, 9)
    next_node, position = struct.unpack("PB", data)
    flag += chr(shuffled_flag[position])
    list_head = next_node

print(flag)

Result

HTB{by_the_powers_of_https://man7.org/linux/man-pages/man5/core.5.html_i_resurrect_this_process_from_the_dead-reveal_your_secrets_to_me!228da2f265e1f3c3c8f4b777600611e822649a}

Key Takeaways

  • A core dump can preserve file-backed mappings, heap allocations and pointer relationships from the crashed process.
  • Clearing a value before crashing does not remove the surrounding metadata needed to reconstruct it.
  • Once the node layout is known, walking the in-memory linked list is more reliable than manually extracting offsets in GDB.