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
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 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).
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.
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;
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.
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.




