// this is the better print, because i'm cool like that ;) voidslow_type(char* msg) { int i = 0; while (1) { if (!msg[i]) return; putchar(msg[i]); usleep(5000); i += 1; } }
voidview_message() { int fd = open("./flag.txt", O_RDONLY); char* flag = calloc(0x50, sizeof(char)); read(fd , flag, 0x50); close(fd); slow_type("\n\e[1;93mAfter several intense attempts, you successfully breach the phone's defenses.\nUnlocking its secrets, you uncover a massive revelation that holds the power to reshape everything.\nThe once-elusive truth is now in your hands, but little do you know, the plot deepens, and the journey through the clandestine hideout takes an unexpected turn, becoming even more complicated.\n\e[0m"); printf("\n%s\n", flag); exit(0); }
for (int i = 0; i < 5; i++) { memset(attempt, 0, 0x30); printf("\e[1;91m%d TRIES LEFT.\n\e[0m", 5-i); printf("PIN: "); scanf("%s", attempt); if (!strcmp(attempt, pin)) { view_message(); } } slow_type("\n\e[1;33mAfter five unsuccessful attempts, the phone begins to emit an alarming heat, escalating to a point of no return. In a sudden burst of intensity, it explodes, sealing your fate.\e[0m\n\n"); }
voidbanner() {
slow_type("\e[1;33mAs you breached the final door to TACYERG's hideout, anticipation surged.\nYet, the room defied expectations – disorder reigned, furniture overturned, documents scattered, and the vault empty.\n'Yet another dead end,' you muttered under your breath.\nAs you sighed and prepared to leave, a glint caught your eye: a cellphone tucked away under unkempt sheets in a corner.\nRecognizing it as potentially the last piece of evidence you have yet to find, you picked it up with a growing sense of anticipation.\n\n\e[0m");
While it is quite a lot of code, most of it is redundant and a bit annoying (slow_type) and the only relevant function that exists is login().
From this code, it’s simple. The contents of the pin is being read and copied over to a variable. We are then allowed to try to guess the pin and if we guess correctly, view_message() is called and the flag is printed out.
The vulnerability is pretty obvious in the scanf() function called in login.
1 2 3
char attempt[0x30]; ...//ignore the code in between scanf("%s", attempt);//does not check how much input we give it so we could give it like 1000 bytes of input and crash the program (segfault)
scanf() doesn’t check how much input we give it so we could give input more than the size of attempt (0x30) and cause a buffer overflow. We can then utilise a ret2win attack to call the view_message() function via the buffer overflow. If you want to know how ret2win and buffer overflows work, it’s explained here: https://rwandi-ctf.github.io/greyCTF2024/babygoods/
Anyway, just like the babygoods challenge, all we have to do is find the offset to rip register using rbp’s offset+8 and call view_message()
Using the exploitation method that we used in babygoods challenge, we attempt to provide a 100 byte cyclic pattern and find the offset of rbp which gave us 64. We then +8 to it to give us the offset to rip register since rbp and rip would be right next to each other in the stack and this gives us 72.
We can see that view_message() located at 0x40138e. If we are doing this challlenge locally, we could just reuse the babygoods exploit script and pwn this binary.
But we were meant to connect to the remote server and exploit the binary and when i tried running the script to connect to remote server, it failed. I kept changing the payload and testing it for 30 minutes before i realised that stack alignment was a thing.
If you want to learn stack alignment is, you can check out this https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/stack-alignment which explains more about it but essentially, if your payload is delivered in a way where your stack is not 16-byte aligned (we are dealing with x64 system), it won’t work and you’d be hard struck on this challenge just like i was.
To fix this issue, you have to add a return (ret) gadget (gadgets are a whole new topic so i won’t be talking about it but it’s like mini crumbs of assembly that exists within the binary) right before the view_message()’s address.
If we were to run ROPgadget on the binary, we would find the ret gadget is located at 0x000000000040101a.
1 2 3 4
┌──()-[~/greyctf/pwn/motor/distribution] └─$ ROPgadget --binary ./chall ...//random gadgets that we do not care about 0x000000000040101a : ret
Now, we just have to modify our payload just a bit to include and run it against the remote server. Below is the final exploit script.
┌──()-[~/greyctf/pwn/motor/distribution] └─$ python3 exploit.py [+] Opening connection to challs.nusgreyhats.org on port 30211: Done [+] Receiving all data: Done (609B) [*] Closed connection to challs.nusgreyhats.org port 30211 b"\n\x1b[1;33mAfter five unsuccessful attempts, the phone begins to emit an alarming heat, escalating to a point of no return. In a sudden burst of intensity, it explodes, sealing your fate.\x1b[0m\n\n\n\x1b[1;93mAfter several intense attempts, you successfully breach the phone's defenses.\nUnlocking its secrets, you uncover a massive revelation that holds the power to reshape everything.\nThe once-elusive truth is now in your hands, but little do you know, the plot deepens, and the journey through the clandestine hideout takes an unexpected turn, becoming even more complicated.\n\x1b[0m\ngrey{g00d_w4rmup_for_p4rt_2_hehe}\n\n"
If we were to run this script, we would get our flag to be