PicoCTF - Easy as GDB

PicoCTF - Easy as GDB

Source code

:::spoiler IDA Main Function

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *str_len; // eax
  int v5; // [esp-8h] [ebp-20h]
  int v6; // [esp-4h] [ebp-1Ch]
  char *input_flag; // [esp+4h] [ebp-14h]
  size_t str_len_1; // [esp+8h] [ebp-10h]
  char *src; // [esp+Ch] [ebp-Ch]

  input_flag = calloc(0x200u, 1u);
  printf("input the flag: ");
  fgets(input_flag, 512, stdin);
  str_len = strnlen(aZNh, 512, v5, v6);
  src = enc_input(str_len, str_len);
  sub_7C2(src, 1, 1);
  if ( check_flag(src, str_len_1) == 1 )
    puts("Correct!");
  else
    puts("Incorrect.");
  return 0;
}

:::

:::spoiler IDA Main Encryption Part

char *__cdecl sub_82B(char *src, size_t enc_flag_len)
{
  unsigned int i; // [esp+0h] [ebp-18h]
  char *dest; // [esp+Ch] [ebp-Ch]
  size_t enc_flag_len_and; // [esp+24h] [ebp+Ch]

  enc_flag_len_and = (enc_flag_len & 0xFFFFFFFC) + 4;
  dest = malloc(enc_flag_len_and + 1);
  strncpy(dest, src, enc_flag_len_and);
  for ( i = 0xABCF00D; i < 0xDEADBEEF; i += 0x1FAB4D )
    main_enc_part(dest, enc_flag_len_and, i);
  return dest;
}

::: :::spoiler IDA Main Encryption Part 2

unsigned int __cdecl sub_6BD(int dest, unsigned int enc_flag_len_and, int idx)
{
  unsigned int result; // eax
  unsigned int i; // [esp+14h] [ebp-14h]
  char v5[4]; // [esp+18h] [ebp-10h]
  unsigned int v6; // [esp+1Ch] [ebp-Ch]

  v6 = __readgsdword(0x14u);
  v5[0] = HIBYTE(idx);                          // 0x0a
  v5[1] = BYTE2(idx);                           // 0xbc
  v5[2] = BYTE1(idx);                           // 0xf0
  v5[3] = idx;                                  // 0x0d
  for ( i = 0; i < enc_flag_len_and; ++i )
    *(dest + i) ^= v5[i & 3];
  result = __readgsdword(0x14u) ^ v6;
  if ( result )
    sub_B20();
  return result;
}

:::

Recon

這一題算簡單,但搞了好久,一方面是ida有些東西翻的很醜,一方面gdb看不出來main, encryption等等function symbol,所以一些動態的address的提示都沒有,會有點妨礙,但整體來說他做的事情就是他先把index的每一個bytes,都獨立出來,以0x0abcf00d來說, v5[0]=0x0a v5[1]=0xbc v5[2]=0xf0 v5[3]=0x0d 然後跟我們的input的每一個bytes都進行XOR,當第一round結束後new index = 0x0abcf00d + 0x1FAB4D,然後重複前面執行的部分,所以我們要做的事情就只是重複剛剛所有的過程,就可以拿到flag了。 至於要比較的ciphertext可以直接從gdb的動態看出來

Exploit

cipher = bytes.fromhex("2E6E40681D53657C175816436D5862366F436230016347333F6314636d7a00")

flag = []
for i in range(len(cipher)):
    flag.append(hex(cipher[i])[2:])


for i in range(0xABCF00D, 0xdea62e4b, 0x1FAB4D):
    tmp_idx = hex(i)[2:].encode()
    if len(tmp_idx) < 8:
        tmp_idx = b'0' + tmp_idx
    key = [int(tmp_idx[-8:-6], 16), int(tmp_idx[-6:-4], 16), int(tmp_idx[-4:-2], 16), int(tmp_idx[-2:], 16)]

    for j in range(len(cipher)):
        tmp = hex(int(flag[j], 16) ^ key[j % 4])[2:]
        flag[j] = tmp
        # cipher[j] = bytes.fromhex(hex(cipher[j] ^ key[j % 4])[2:])
print(bytes.fromhex("".join(flag)).decode('cp437'))