Cyber Apocalypse 2025 - pwn_blessing
This post is part of a series where I describe some Cyber Apocalypse 2025 CTF challenges I participated in.
The list of posts is as follows:
Enjoy!
pwn_blessing
In this challenge, we can readily spot in the disassembly that it has a read_flag
function. Where is it called?
.text:0000000000001723 mov rdx, [rbp+size]
.text:0000000000001727 mov rax, [rbp+buf]
.text:000000000000172B add rax, rdx
.text:000000000000172E sub rax, 1
.text:0000000000001732 mov qword ptr [rax], 0
.text:0000000000001739 mov rdx, [rbp+size] ; n
.text:000000000000173D mov rax, [rbp+buf]
.text:0000000000001741 mov rsi, rax ; buf
.text:0000000000001744 mov edi, 1 ; fd
.text:0000000000001749 call _write
.text:000000000000174E mov rax, [rbp+var_18]
.text:0000000000001752 mov rax, [rax]
.text:0000000000001755 test rax, rax
.text:0000000000001758 jnz short loc_1766
.text:000000000000175A mov eax, 0
.text:000000000000175F call read_flag <---
.text:0000000000001764 jmp short loc_1798
What the code does can be summarized as:
- Allocates a buffer of 0x30000 bytes and sets the first qword to 1.
- Prints the buffer pointer to stdout.
- Asks for a size and allocates a buffer of that size using the malloc function.
- Clears the first qword of this buffer.
- Reads from stdin into the buffer and outputs the same buffer contents.
- If the first qword of the 0x30000 buffer is zero, it calls
read_flag
.
At first glance, nothing looks buggy. However, some things seem suspicious. Why clear only the first qword? Initially, I thought it could be something like a single-byte malloc buffer overflow, messing with the malloc chunks. I assumed I needed to somehow get the allocated memory to go right behind the 0x30000 buffer, then find a way to clear the first qword. For example, if we could make it allocate the second buffer over the first one…
Given the mechanics of malloc, I could never get the resulting malloc pointer + size to end right before the 0x30000 buffer. There would always be a margin between that last byte and the 0x30000 buffer.
Lesson Learned: Would it be possible to make the second malloc buffer be right before the 0x30000 one? Probably not. I tried multiple sizes—0x30000, 0x301000, 0x2f0000—but none would make malloc return a chunk placed right before the 0x30000 buffer. In specific scenarios, we can get it close, but there is still a margin between the last byte and the first byte of the 0x30000 buffer.
To solve this challenge, we can provide that pointer as the size. For the pointer above, that would be 140737353642000. The malloc will fail with this size, resulting in a null malloc pointer, but at the 0x001723 instructions, it will do:
mov rdx, [rbp+size] ; 140737353642000 = 0x00007ffff7f87010
mov rax, [rbp+buf] ; ZERO
add rax, rdx ; ZERO + 0x00007ffff7f87010 = 0x00007ffff7f87010
sub rax, 1 ; 0x00007ffff7f87010 - 1 = 0x00007ffff7f8700f
mov qword ptr [rax], 0 ; [0x00007ffff7f8700f] = 0
We just need to provide a size in decimal that is the pointer provided plus 1.
The next post is about the pwn_crossbow challenge.