b01lers CTF Writeup

この大会は2022/4/23 9:00(JST)~2022/4/25 9:00(JST)に開催されました。
今回もチームで参戦。結果は1020点で653チーム中45位でした。
自分で解けた問題をWriteupとして書いておきます。

sanity_check (misc)

Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。

bctf{b01ler_up_4nd_h4mm3r_d0wn_h4ck3r}

crackme (rev)

Ghidraでデコンパイルする。

bool main(void)

{
  int iVar1;
  char local_48 [60];
  undefined4 local_c;
  
  local_c = 0;
  printf("Product Key> ");
  fgets(local_48,0x2f,stdin);
  iVar1 = check(local_48);
  if (iVar1 == 0) {
    printf("Key incorrect, not activating.\n");
  }
  else {
    printf("Key correct, activating.\n");
  }
  return iVar1 == 0;
}

undefined4 check(char *param_1)

{
  undefined4 local_c;
  
  if (*param_1 == 'b') {
    if (param_1[1] == 'c') {
      if (param_1[2] == 't') {
        if (param_1[3] == 'f') {
          if (param_1[4] == '{') {
            if (param_1[5] == '1') {
              if (param_1[6] == '3') {
                if (param_1[7] == '3') {
                  if (param_1[8] == '&') {
                    if (param_1[9] == '_') {
                      if (param_1[10] == 'l') {
                        if (param_1[0xb] == 'e') {
                          if (param_1[0xc] == 't') {
                            if (param_1[0xd] == 'm') {
                              if (param_1[0xe] == 'e') {
                                if (param_1[0xf] == 'i') {
                                  if (param_1[0x10] == 'n') {
                                    if (param_1[0x11] == '_') {
                                      if (param_1[0x12] == '1') {
                                        if (param_1[0x13] == '2') {
                                          if (param_1[0x14] == '3') {
                                            if (param_1[0x15] == '}') {
                                              local_c = 1;
                                            }
                                            else {
                                              local_c = 0;
                                            }
                                          }
                                          else {
                                            local_c = 0;
                                          }
                                        }
                                        else {
                                          local_c = 0;
                                        }
                                      }
                                      else {
                                        local_c = 0;
                                      }
                                    }
                                    else {
                                      local_c = 0;
                                    }
                                  }
                                  else {
                                    local_c = 0;
                                  }
                                }
                                else {
                                  local_c = 0;
                                }
                              }
                              else {
                                local_c = 0;
                              }
                            }
                            else {
                              local_c = 0;
                            }
                          }
                          else {
                            local_c = 0;
                          }
                        }
                        else {
                          local_c = 0;
                        }
                      }
                      else {
                        local_c = 0;
                      }
                    }
                    else {
                      local_c = 0;
                    }
                  }
                  else {
                    local_c = 0;
                  }
                }
                else {
                  local_c = 0;
                }
              }
              else {
                local_c = 0;
              }
            }
            else {
              local_c = 0;
            }
          }
          else {
            local_c = 0;
          }
        }
        else {
          local_c = 0;
        }
      }
      else {
        local_c = 0;
      }
    }
    else {
      local_c = 0;
    }
  }
  else {
    local_c = 0;
  }
  return local_c;
}

1文字ずつチェックしているのがわかるので、それを連結する。

bctf{133&_letmein_123}

crackme_2 (rev)

Ghidraでデコンパイルする。

bool main(void)

{
  int iVar1;
  long in_FS_OFFSET;
  char local_48 [56];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("Product key> ");
  fgets(local_48,0x2f,stdin);
  iVar1 = check(local_48);
  if (iVar1 == 0) {
    puts("Key incorrect, not activating.");
  }
  else {
    puts("Key correct, activating.");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return iVar1 == 0;
}

char check(char *param_1)

{
  char cVar1;
  
  if ((((*param_1 == 'b') && (param_1[1] == 'c')) && (param_1[2] == 't')) &&
     ((param_1[3] == 'f' && (param_1[4] == '{')))) {
    if (param_1[5] == '4') {
      if (param_1[6] == 'l') {
        if (param_1[7] == 'g') {
          if (param_1[8] == '3') {
            if (param_1[9] == 'b') {
              if ((byte)(param_1[10] ^ param_1[9]) == 0x10) {
                if (param_1[0xb] + -1 == (int)param_1[8]) {
                  if (param_1[0xc] == '!') {
                    cVar1 = param_1[0xd];
                    if (cVar1 != '}') {
                      cVar1 = '\0';
                    }
                  }
                  else {
                    cVar1 = '\0';
                  }
                }
                else {
                  cVar1 = '\0';
                }
              }
              else {
                cVar1 = '\0';
              }
            }
            else {
              cVar1 = '\0';
            }
          }
          else {
            cVar1 = '\0';
          }
        }
        else {
          cVar1 = '\0';
        }
      }
      else {
        cVar1 = '\0';
      }
    }
    else {
      cVar1 = '\0';
    }
  }
  else {
    cVar1 = '\0';
  }
  return cVar1;
}

条件を満たすよう、簡単な計算で入力文字を算出できる。

#!/usr/bin/env python3
flag = list(b'bctf{4lg3b') + [b''] * 4
flag[10] = flag[9] ^ 0x10
flag[0xb] = flag[8] + 1
flag[0xc] = ord('!')
flag[0xd] = ord('}')
flag = ''.join([chr(c) for c in flag])
print(flag)
bctf{4lg3br4!}

Hardcore (crypto)

サーバの処理概要は以下の通り。

・diff: 難易度選択(1 or 2)
・diff == 1の場合
 ・FLAG = FLAG1
 ・Level(1)
・diff == 2の場合
 ・FLAG = FLAG2
 ・Level(0.9)

■Level(probability)
・encrypted_secret: FLAGのsha256
・encrypted_secret表示
・以下繰り返し
 ・array = parse_input()
  ・bitstring: 2進数文字列入力
  ・array: bitstringをnumpy配列に変換→返却
 ・predictor(array, probability = probability)の結果を表示
  ・x_r: FLAGの2進数表記と異なるビットはTrueとなっている配列
  ・np.random.seed(x_r)
  ・chance = np.random.rand()
  ・prediction = 0
  ・chanceがprobability以下の場合
   ・prediction = generate_hardcore(digest_to_array(FLAG), r)
  ・chanceがprobabilityより大きい場合
   ・prediction = 1 - generate_hardcore(digest_to_array(FLAG), r)
  ・predictionを返却

難易度1の方を解く必要がある。generate_hardcore関数はFLAGのbit配列と、指定したbit配列の内積を算出し、その和の2で割った余りを返す。1ビットずつ0の場合と1の場合を指定し、返却値が変わらなければ0、変化したら1であるとわかる。このことから1ビットずつ割り出すスクリプトにし、フラグを求める。

#!/usr/bin/env python3
import socket

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('ctf.b01lers.com', 9003))

data = recvuntil(s, b':')
print(data + '1')
s.sendall(b'1\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)

b_flag = ''
for i in range(256):
    b_tmp = b_flag + '0' + '0' * (255 - i)
    print(b_tmp)
    s.sendall(b_tmp.encode() + b'\n')
    hc0 = recvuntil(s, b'\n').rstrip()
    print(hc0)

    b_tmp = b_flag + '1' + '0' * (255 - i)
    print(b_tmp)
    s.sendall(b_tmp.encode() + b'\n')
    hc1 = recvuntil(s, b'\n').rstrip()
    print(hc1)

    if hc0 == hc1:
        b_flag += '0'
    else:
        b_flag += '1'

flag = ''
for i in range(0, len(b_flag), 8):
    flag += chr(int(b_flag[i:i+8], 2))
print(flag)

実行結果は以下の通り。

Select a difficulty (1/2):1
We're looking to find the secret behind this SHA1 hash <d578448067f47a44e1d97974492a07ca4b3f230ae70bb0f9129bb8d62d197703>. Luckily for you, this socket takes a bitstring and predicts the dot product of the secret with that bit string (mod 2) with 100% accuracy and sends you back the answer.

0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0
        :
        :

0110001001100011011101000110011001111011011001000110111101011111011110010110111101110101010111110110110001101001011010110110010101011111011010000110000101110010011001000110001101101111011100100110010101011111011000110110100001100001011011000111001101111100
0
0110001001100011011101000110011001111011011001000110111101011111011110010110111101110101010111110110110001101001011010110110010101011111011010000110000101110010011001000110001101101111011100100110010101011111011000110110100001100001011011000111001101111101
1
bctf{do_you_like_hardcore_chals}
bctf{do_you_like_hardcore_chals}