First pwn challenge, the most solved challenge in this category surely, given the binary file and its source code so i don't need to fire up my ghidra.
vuln.c :
As you can see, there's an obvious buffer overflow with gets, which we can overwrite return address and control rip, and there's win function that will give use flag, that's why this kind of challenge technique called ret2win same as the challenge name which mean we return to win function.
To exploit buffer overflow vulnerability we have to fill up stack frame until we got into the return address.
just to give you some of the basic visualisation (i just randomly grab some image on google by typing stack frame), stack frame is something in between rsp and rbp register, return address is something that saved function address. So when a function done execute its instruction it go back to the function that called him, for example if main called gets it will saved address of main somewhere in stack and that's what we call return address, and by overwriting it we can control the flow of the program.This was the most basic challenge in pwn so no need to explain any further.
Final Solver :
It's the same challenge as the earlier challenge, this time we have to spawn shell not just calling win function, there's a technique called ret2libc but libc wasn't given so we have to think a bit more creative this time.
For someone has done ret2libc before you may know that we have to leak libc first and bla bla set rdi call system, but there aren't any puts or write function, something that will printed out the leak to stdout so we got no leak.
Before going any further it's good practice to check the mitigations first
so there's no canary, PIE was off, and we can overwrite GOT Table. The reason i don't do this earlier was because i'm pretty sure canary and pie was off and no need to check for RELRO, this time to be able to overwrite GOT for my exploit script to works is a must.
Let me explain my idea, first i think i have to resolve the system so i want to call win function first but turns out that's not really useful so i just comment it out, what i mean by resolve is that when libc function in got table got executed the address will be a libc address not more a plt address, but this was nothing useful as i said earlier, because we don't need to leak something and if we have to leak we can leak other function that has been resolved for e.g puts of course this is for another challenge.
So, we'll jump to main when the instruction is lea rax, [rbp-0x40]
, oh let me give you the full main disassemble to understand better.
basically we jump to lea rax, [rbp-0x40]
so we can control rdi, because i don't see any pop rdi
gadget in ropper
, but we have to also set the rbp before ret to main again the buffer size is 64 and there's rbp after that that rbp we have to set to GOT gets + 0x40 (elf.got.gets + 0x40
) and that will set our rdi to GOT gets, and for this gets we'll input system plt, and basically overwrite return address again and call main once again, but there's more in payload let me break it down.
i set /bin/sh
to the buffer, and because this was on the bss section i can calculate the address (just do basic math with the address of GOT gets), one thing that must be underlined is the add rsp+8
gadget, when we don't use it in our system call it will be segfaulting because trying to write to non-writeable address (not in bss anymore), so i need to add more rsp so it can works after i tested it out there's some amount of rsp that we have to add if we add too little we'll get segfault because of the earlier reason or the system call argument doesn't get pass correctly system(<some random address>)
, my teammates said that was because there were to many args when we call posix_spawn
so we need to reduce the stack frame by adding more rsp, in my local machine 219 add rsp+8 gadget was enough but in remote server i tested it was 390-400 ish add rsp gadgets.
Final Solver :
Me personally think this challenge is the easiest among the pwn category, because we don't really need a script to solve it, but yeah we have to know the trick, this challenge vuln was format string (actually one of my favourite pwn challenge because easy and fun).
Disassembly :
Or if you prefer a decompilation :
Okay so the program malloc 2 chunk, first chunk will be the buffer for flag.txt, the second one is for our input buffer, and there's length restriction in our input so our payload must be small, well doesn't have to make a long one for this challenge.
How our stack looks like after the printf :
as u can see there's our heap address in stack, and there's a stack address that point to our heap.
So, the idea is overwrite the stack address that point to heap with just one byte, this one so the stack address will point to our heap that contain flag.
and then we can leak the flag with %s
, this address since it will be overwritten to flag heap
We have to use non-positional format to execute our first idea (overwrite stack to point to flag heap) and we can use the $
to access the stack that have been overwritten.
The payload is :
we stack up %c
until we hit the 6th stack, the 7th format will be the payload to overwrite one byte %hhn
this will overwrite 7th stack one byte, 155 is a padding, we want to overwrite the heap to 0xa0 in the end(0xa0 is 160) but since we already have 5 %c
earlier we have to substract it with 5, and with that the overwrite will be saved, so when we do %6$s
this will actually going to treat the 6th stack as string and actually leak out our flag.
note : stack i provided earlier is stack after printf and stack i talked about is when we do %p
since some of the earlier stack is something that use in internal printf cmiiw though.
Final Solver :
I'm still trying to learn heap exploitation there's not many technique that i understand, but for this one i use fastbin dup and double free since it have uaf but doesn't have edit function, i won't explain each function of the program just my idea on how i approach this challenge.
Btw there's a seccomp used, and this is what they restrict us
basically open, read, write is what we have to do to get flag.
libc version (2.35) :
mitigations :
Because this is libc 2.35 there's safe linking procedure, just add demale and mangle function into exploit to resolve it
When i first saw this challenge what comes to my mind is somehow we have to do rop orw so we have to get libc address and stack address, if we successfully leak libc we can leak stack also from libc environ.
By getting a chunk in unsorted bin we can leak libc, to do it we have to fill up tcache first, and big enough chunk won't get to fastbin it goes to unsorted bin instead.
also we have to leak libc
so if we don't use demangle we won't get the same address as our heap, and sometimes this will get us in trouble.
after we got libc and heap, we'll do fastbin dup and double free so we can change the tcache structure so it's point to libc environ and libc environ has stack address in it.
i'll give some visualization for double free
that's what will happen to your heap in fastbin, we can't do that in tcache because of libc mitigations.
so our 7 (heap with index 7) will point to <chunk 1>
, we malloc to it again and input our libc environ, so it will be something like
that's happen in tcache since we have retrieve all the tcache before malloc 7, then we malloc again 3 times, the last malloc will overwrite libc environ so we can read 10 to leak because fgets terminate input with null.
to leak the stack i try to do something creative, i don't know if this will works in other machine but this also works in remote, well my solver really is not stable but doesn't care i solved it with this.
so um i honestly don't know, i thought when i add 1 more chunk the not valid stack will get allocated but not i got another heap address that will point to that's not valid stack, here the visualization
i read it and mangle it 2 times with libc heap and libc environ, we can't use demangle because that's not heap, let's go through the safe linking first.
So if we free chunk it will get encrypted it with mangle
that'some shitty explanation but i hope you understand, so we have to mangle it to get to address of stack that been mangled by libc environ address, then we mangle back to get actuall stack that libc environ originally has, please don't be confused on this, for further explanation if we have a heap address to get its original address we can do demangle because we don't really need a seccond value as the key xor, we can recover that because address of heap (if it's not differ to much) the first couple byte will be the same so demangle works. but if we have libc injected we have to use mangle becuase the xor key is heap and first couple byte isn't the same, hope that's explain.
we get our libc address and stack address, we will have to malloc to stack to do rop but where though?? well we have to malloc in return address but remember chunk have to be perfectly aligned (not 0x8), so rip-8 is good.
for my solver i'm really trying to malloc to return address but somehow it crash, what i mean by crash is we can't malloc there or something, i don't know it hard to debug why this happen, then i decided to calculate a return address of a function (doesn't remember) and get -0x190 from libc environ, but yes it's also crash and when i calculate again in many function it return -0x160 and yes this CRASHHH!! then i tried to add sleep(1)
before last payload subtract more -0x170 .. until -0x190 and it works?? what??, there's to many thing i don't understand i'm sorry for being silly but the return address is -0x190 from stack libc environ, accept it.
the rop payload is divided by 2 because the size isn't enough.
rop2 will be malloced at rip-8+len(rop1)
while the rop1 is the problem i earlier told (crash when malloc or something, idk), the rop payload it self is just a basic orw syscall payload.
and that's it, the program printed us flag heap heap hoorayy, this is the most pain chall that i solved since i'm heapy noob.
Final Solver :
May this writeups help you :+1:
All the solver available at : Github Page