by fs
You ever heard of the book “To kill a mockingbird”? What about to kill a canary?
This challenge was meant to be a TLS canary overwite challenge and pop shell.
Looking at the source code of this challenge, we see an obvious buffer overflow since the program doesnt restrict the size of input that we want to give in.
1 | void feedback(){ |
However, this program has canary and you can’t just overwrite the saved canary as the program checks if the canary has been modified and if it is, it’ll say stack smashing detected and crash. But for a very intentional and weird reason, this program also uses threading.
1 | int main(){ |
We see the program implements threading and also gives us free libc printf leak.
The saved canary is stored in TLS (thread-local storage: you can read about it here https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html)
Hence, this, combined with the fact I can give whatever size of input (like 3000), I can easily buffer overflow all the way to TLS and overwrite the canary there such that the canary that the thread refers to would be modified in TLS (fs:0x28) and we could craft our own canary to easily pop shell. Now, we can find the offset to TLS easily using gdb which I think was around 2176 and overwrite the canary at offset 2216 since the canary is stored in fs:0x28. We can construct the payload as the following “A” * OFFSET TO RIP (around like 120) and this also means we are overwritng the canary and saved rbp value. We can then place our pop shell payload (which can either be a pop_rdi in libc + /bin/sh or an SROP payload since pop_rax and syscall gadgets are given). We can then continue ovewriting all the way to TLS and overwrite the saved canary in TLS at offset 2216 with “A”*8 (and this would match the canary near rbp) hence the program would not throw the stack smashing detected error.
However, if you were to simply do this, you’re gonna get an error which is _pthread_enable_asynccancel() like the following
The reason to this is because the pthread_struct within glibc has a self header at fs:0x10 and basically the addres in the self-header gets de-referenced and is stored in rax register and the problem with this is that this address needs to be writeable or else glibc will complain and throw that error i mentioned. You can learn more about this with this link (https://www.theflash2k.me/blog/writeups/aofctf/pwn/birdy101) or the stuff I posted in #pwn of blahajctf discord So, given that we can calculate the libc base address since we’re given a printf leak, we can just calculate a writeable address within libc (can just search in libc using vmmap) and use it to overwrite the address at fs:0x10 (which is at offset 2192).
With all of these information, we can structure the following payload (im using srop but can also use onegadget/classic ret2libc technique).
1 | from pwn import * |
Running this, we get the flag blahaj{canary_master_101}
.
Many people got stuck at the self header in pthread_struct coz they didnt know they had to overwrite it with a writeable address so hope this chall taught yall a bit more about canary and pthread exploitation