castorsCTF20 Writeup

この大会は2020/5/30 5:00(JST)~2020/6/1 5:00(JST)に開催されました。
今回もチームで参戦。結果は5725点で500チーム中37位でした。
自分で解けた問題をWriteupとして書いておきます。

Readme (Welcome)

ルールのページの賞金の記載の箇所に白字でフラグが書いてあった。

castorsCTF{0u7_0f_5173_0u7_0f_m1nd}

Password Crack 1 (Misc)

CrackStationで3c80b091de0981ec64e43262117d618aをクラックする。

irocktoo
castorsCTF{irocktoo}

Password Crack 2 (Misc)

CrackStationで867c9e11faa64d7a5257a56c415a42725e17aa6dをクラックする。

pi3141592653589
castorsCTF{pi3141592653589}

Password Crack 3 (Misc)

フラグフォーマットにして、rockyou.txtをブルートフォースしてsha256が一致するものを探す。

import hashlib

with open('dict/rockyou.txt', 'r') as f:
    words = [word.rstrip() for word in f.readlines()]

h = '7adebe1e15c37e23ab25c40a317b76547a75ad84bf57b378520fd59b66dd9e12'

for word in words:
    flag = 'castorsCTF{%s}' % word
    if hashlib.sha256(flag).hexdigest() == h:
        print flag
        break
castorsCTF{theformat!}

Gif (Misc)

アニメーションGIFに表示される数字を順に並べてみる。

15 13 7 19 15 6 21 14 14 25 12 15 12

アルファベットのインデックスとしてあてはめてみる。

         11111111111222222
12345678901234567890123456
abcdefghijklmnopqrstuvwxyz

→omgsofunnylol
castorsCTF{omgsofunnylol}

Arithmetics (Coding)

$ nc chals20.cybercastors.com 14429

------------------Welcome to Beginner Arithmetics!------------------
To get the flag you'll have to solve a series of arithmetic challenges.
The problems may be addition, substraction, multiplication or integer division.
Numbers can range from 1 to 9. Easy right?
You'll have very little time to answer so do your best!
Hit <enter> when ready.

What is 6 * 1 ?
6
Correct answer!
What is 9 + 5 ?
14
Too slow!

四則演算の計算結果を答えていくだけ。ただ、数値や演算が英語になるので変換が必要。

import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals20.cybercastors.com', 14429))

data = recvuntil(s, 'ready.\n').rstrip()
print data + '\n'
s.sendall('\n')

for i in range(100):
    print 'Round %d' % (i+1)
    data = recvuntil(s, '\n').rstrip()
    print data
    data = data.replace('zero', '0')
    data = data.replace('one', '1')
    data = data.replace('two', '2')
    data = data.replace('three', '3')
    data = data.replace('four', '4')
    data = data.replace('five', '5')
    data = data.replace('six', '6')
    data = data.replace('seven', '7')
    data = data.replace('eight', '8')
    data = data.replace('nine', '9')
    data = data.replace('plus', '+')
    data = data.replace('minus', '-')
    data = data.replace('multiplied-by', '*')
    data = data.replace('divided-by', '//')
    ans = str(eval(data[8:-2]))
    print ans
    s.sendall(ans + '\n')
    data = recvuntil(s, '\n').rstrip()
    print data

data = s.recv(8192)
print data

実行結果は以下の通り。

        :
Round 100
What is nine - 3 ?
6
Correct answer!
Wow! You're fast! Here's your flag: castorsCTF(n00b_pyth0n_4r17hm3t1c5}
castorsCTF(n00b_pyth0n_4r17hm3t1c5}

Base Runner (Coding)

$ nc chals20.cybercastors.com 14430

 ______  ______  ______  ______       ______  __  __  __   __  __   __  ______  ______    
/\  == \/\  __ \/\  ___\/\  ___\     /\  == \/\ \/\ \/\ "-.\ \/\ "-.\ \/\  ___\/\  == \   
\ \  __<\ \  __ \ \___  \ \  __\     \ \  __<\ \ \_\ \ \ \-.  \ \ \-.  \ \  __\\ \  __<   
 \ \_____\ \_\ \_\/\_____\ \_____\    \ \_\ \_\ \_____\ \_\\ \_\ \_\\ \_\ \_____\ \_\ \_\ 
  \/_____/\/_/\/_/\/_____/\/_____/     \/_/ /_/\/_____/\/_/ \/_/\/_/ \/_/\/_____/\/_/ /_/ 
                                                                                          

Listen up!
We're down 0 - 49 with 2 outs on the final inning but we got this!
Don't worry about getting hits, just run those bases as fast as you can.
They have The Flash fielding for their team!
Hit <enter> when ready.

00110110 00110101 00100000 00110111 00110001 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110110 00110010 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110110 00110110 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110001 00110100 00110001 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110110 00110111 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110111 00110001 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110111 00110001 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110110 00110011 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110110 00110000 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110001 00110100 00110101 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110110 00110101 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110110 00110010 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110001 00110100 00110101 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110110 00110001 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110110 00110011 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110001 00110100 00110011 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110001 00110100 00110001 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110110 00110110 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110110 00110010 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110001 00110100 00110110 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110110 00110110 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110110 00110010 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110110 00110111 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110110 00110010 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110110 00110000 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110110 00110000 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110110 00110110 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110110 00110001 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110001 00110100 00110100 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110001 00110100 00110100

次々とbaseを変更してデコードする。

2進数→8進数→16進数→base64
import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals20.cybercastors.com', 14430))

data = recvuntil(s, 'ready.\n').rstrip()
print data + '\n'
s.sendall('\n')

for i in range(50):
    print 'Round %d' % (i+1)
    data = recvuntil(s, '\n').rstrip()
    print data
    codes = data.split(' ')
    msg = ''
    for code in codes:
        msg += chr(int(code, 2))
    #print msg
    
    codes = msg.split(' ')
    msg = ''
    for code in codes:
        msg += chr(int(code, 8))
    #print msg
    
    codes = msg.split(' ')
    msg = ''
    for code in codes:
        msg += chr(int(code, 16))
    #print msg
    
    dec = msg.decode('base64')
    print dec
    s.sendall(dec)

    data = recvuntil(s, '\n').rstrip()
    print data

data = s.recv(8192)
print data

実行結果は以下の通り。

        :
Round 50
00110110 00110101 00100000 00110111 00110001 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110110 00110010 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110110 00110110 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110001 00110100 00110001 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110110 00110111 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110111 00110001 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110111 00110001 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110110 00110011 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110110 00110000 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110001 00110100 00110101 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110110 00110101 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110110 00110010 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110001 00110100 00110101 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110110 00110011 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110001 00110100 00110010 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110001 00110100 00110001 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110110 00110100 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110110 00110011 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110001 00110100 00110101 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110110 00110001 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110110 00110011 00100000 00110100 00110000 00100000 00110110 00110110 00100000 00110001 00110100 00110011 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110110 00110010 00100000 00110100 00110000 00100000 00110110 00110111 00100000 00110110 00110101 00100000 00110100 00110000 00100000 00110110 00110101 00100000 00110001 00110100 00110001 00100000 00110100 00110000 00100000 00110110 00110100 00100000 00110111 00110000 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110110 00110000 00100000 00110100 00110000 00100000 00110110 00110011 00100000 00110001 00110100 00110100
castorsCTF{mrKYwsurTnd}
Correct answer!
Wow! The Flash is impressed! Here's your flag: castorsCTF[m4j0r_l34gu3_py7h0n_b4s3_runn3r}
castorsCTF[m4j0r_l34gu3_py7h0n_b4s3_runn3r}

Flag Gods (Coding)

$ nc chals20.cybercastors.com 14431

 ______ __      ______  ______       ______  ______  _____   ______    
/\  ___/\ \    /\  __ \/\  ___\     /\  ___\/\  __ \/\  __-./\  ___\   
\ \  __\ \ \___\ \  __ \ \ \__ \    \ \ \__ \ \ \/\ \ \ \/\ \ \___  \  
 \ \_\  \ \_____\ \_\ \_\ \_____\    \ \_____\ \_____\ \____-\/\_____\ 
  \/_/   \/_____/\/_/\/_/\/_____/     \/_____/\/_____/\/____/ \/_____/ 
                                                                                                                                                             

We have a small problem...
The flag gods are trying to send us a message, but our transmitter isn't calibrated to
decode it! If you can find the hamming distance for the following messages we may be
able to calibrate the transmitter in time. Entering the wrong distance will lock the
machine. Good luck, we'll only have 90 seconds!
Hit <enter> when ready.

The machine is currently 20% calibrated.
Transmitted message: A clueless boy runs a boy merrily.
Received message: 0e9e9a9382be9796ad9ff29d0087cfaa0e11d7cd995fbd91a6df839a8cbf839304d9
Enter hamming distance: 12
The machine locked up. The correct distance was: 213

Hamming distanceを答える必要がある。この問題の場合、Transmitted messageの2進表記とReceived messageの2進表記の差のあるビットの数を答えればよい。

import socket
from Crypto.Util.number import *

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

def get_hamming_distance(t, r):
    bin_r = bin(int(r, 16))[2:].zfill(len(r)*4)
    bin_t = bin(bytes_to_long(t))[2:].zfill(len(r)*4)

    count = 0
    for i in range(len(bin_r)):
        if bin_r[i] != bin_t[i]:
            count += 1
    return count

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals20.cybercastors.com', 14431))

data = recvuntil(s, 'ready.\n').rstrip()
print data + '\n'
s.sendall('\n')

for i in range(80):
    print 'Round %d' % (i+1)
    data = recvuntil(s, 'calibrated.\n').rstrip()
    print data

    data = recvuntil(s, '\n').rstrip()
    print data
    t_msg = data.split(': ')[1]
    data = recvuntil(s, '\n').rstrip()
    print data
    r_msg = data.split(': ')[1]

    distance = str(get_hamming_distance(t_msg, r_msg))
    data = recvuntil(s, ': ')
    print data + distance
    s.sendall(distance + '\n')

    data = recvuntil(s, '\n').rstrip()
    print data

data = s.recv(8192)
print data

実行結果は以下の通り。

        :
Round 80
The machine is currently 99% calibrated.
Transmitted message: The clueless girl hits a rabbit foolishly.
Received message: 54686520636c77656c65737320636d726c206869747320612072616260697420664f6f6c6973686c792f
Enter hamming distance: 6
Correct answer!
Machine calibration successful! Here's the transmitted flag: castorsCTF{c0mmun1ng_w17h_7h3_f14g_g0d5}
castorsCTF{c0mmun1ng_w17h_7h3_f14g_g0d5}

Vault0 (Reversing)

checkしているhex文字列を結合してhexデコードすればよい。

636173746f72734354467b72317854795f6d316e757433735f67745f73317874795f6d316e757433737d
$ echo 636173746f72734354467b72317854795f6d316e757433735f67745f73317874795f6d316e757433737d | xxd -r -p
castorsCTF{r1xTy_m1nut3s_gt_s1xty_m1nut3s}
castorsCTF{r1xTy_m1nut3s_gt_s1xty_m1nut3s}

XoR (Reversing)

Ghidraでデコンパイルする。

undefined8 FUN_001008b1(void)

{
  int iVar1;
  long in_FS_OFFSET;
  char local_48 [56];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("Enter flag: ");
  fgets(local_48,0x2c,stdin);
  iVar1 = FUN_0010080a(local_48);
  if (iVar1 == 0) {
    puts("Correct!");
  }
  else {
    puts("Wrong flag!");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

ulong FUN_0010080a(char *pcParm1)

{
  int iVar1;
  size_t sVar2;
  int local_18;
  
  sVar2 = strlen(pcParm1);
  local_18 = 0;
  while (local_18 < (int)sVar2) {
    pcParm1[(long)local_18] = pcParm1[(long)local_18] ^ (char)local_18 + 10U;
    pcParm1[(long)local_18] = pcParm1[(long)local_18] + -2;
    local_18 = local_18 + 1;
  }
  iVar1 = strcmp(pcParm1,&DAT_00301020);
  return (ulong)(iVar1 != 0);
}

                             DAT_00301020                                    XREF[1]:     FUN_0010080a:00100893(*)  
        00301020 67              ??         67h    g
        00301021 68              ??         68h    h
        00301022 7d              ??         7Dh    }
        00301023 77              ??         77h    w
        00301024 5f              ??         5Fh    _
        00301025 7b              ??         7Bh    {
        00301026 61              ??         61h    a
        00301027 50              ??         50h    P
        00301028 44              ??         44h    D
        00301029 53              ??         53h    S
        0030102a 6d              ??         6Dh    m
        0030102b 6b              ??         6Bh    k
        0030102c 24              ??         24h    $
        0030102d 63              ??         63h    c
        0030102e 68              ??         68h    h
        0030102f 26              ??         26h    &
        00301030 72              ??         72h    r
        00301031 2b              ??         2Bh    +
        00301032 41              ??         41h    A
        00301033 68              ??         68h    h
        00301034 2d              ??         2Dh    -
        00301035 26              ??         26h    &
        00301036 46              ??         46h    F
        00301037 7c              ??         7Ch    |
        00301038 14              ??         14h
        00301039 7a              ??         7Ah    z
        0030103a 11              ??         11h
        0030103b 50              ??         50h    P
        0030103c 15              ??         15h
        0030103d 10              ??         10h
        0030103e 1d              ??         1Dh
        0030103f 52              ??         52h    R
        00301040 1e              ??         1Eh

以下の処理をして比較している。

・入力文字の1文字ずつに対して以下の処理
 ・(index値 +10)とXOR  - 2

逆算してフラグにする。

def decrypt(s):
    dec = ''
    for i in range(len(s)):
        code = (ord(s[i]) + 2) ^ (i + 10)
        dec += chr(code)
    return dec

enc = 'gh}w_{aPDSmk$ch&r+Ah-&F|\x14z\x11P\x15\x10\x1dR\x1e'
flag = decrypt(enc)
print flag
castorsCTF{x0rr1n6_w17h_4_7w157}

Reverse-me (Reversing)

Ghidraでデコンパイルする。

undefined8 FUN_00100b3e(void)

{
  FILE *__stream;
  size_t sVar1;
  undefined8 uVar2;
  long in_FS_OFFSET;
  int local_74;
  char local_68 [32];
  char local_48 [40];
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  __stream = fopen("flag.txt","r");
  if (__stream == (FILE *)0x0) {
    printf("flag.txt not found.");
    fflush(stdout);
    uVar2 = 1;
  }
  else {
    fgets(&DAT_00302030,0x1e,__stream);
    fclose(__stream);
    FUN_0010096a(&DAT_00302030);
    strcpy(local_68,&DAT_00302030);
    FUN_00100a68(local_68);
    FUN_001009c7(local_68);
    puts("System Error...\nDumping memory...");
    local_74 = 0;
    while( true ) {
      sVar1 = strlen(local_68);
      if (sVar1 <= (ulong)(long)local_74) break;
      printf("%x ",(ulong)(uint)(int)local_68[(long)local_74]);
      local_74 = local_74 + 1;
    }
    printf("\nEnter password: ");
    fflush(stdout);
    fgets(local_48,0x32,stdin);
    FUN_0010096a(local_48);
    FUN_00100a68(local_48);
    FUN_001009c7(local_48);
    FUN_00100abf(local_48,local_68,local_68);
    uVar2 = 0;
  }
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return uVar2;
}

void FUN_00100a68(char *pcParm1)

{
  size_t sVar1;
  int local_10;
  
  sVar1 = strlen(pcParm1);
  local_10 = 0;
  while (local_10 < (int)sVar1) {
    pcParm1[(long)local_10] = pcParm1[(long)local_10] + 2;
    local_10 = local_10 + 1;
  }
  return;
}

void FUN_001009c7(char *pcParm1)

{
  size_t sVar1;
  int local_10;
  
  sVar1 = strlen(pcParm1);
  local_10 = 0;
  while (local_10 < (int)sVar1) {
    if (('`' < pcParm1[(long)local_10]) && (pcParm1[(long)local_10] < '{')) {
      pcParm1[(long)local_10] =
           (char)((int)pcParm1[(long)local_10] + -0x57) +
           (char)(((int)pcParm1[(long)local_10] + -0x57) / 0x1a) * -0x1a + 'a';
    }
    local_10 = local_10 + 1;
  }
  return;
}

void FUN_00100abf(char *pcParm1,char *pcParm2)

{
  int iVar1;
  
  iVar1 = strcmp(pcParm1,pcParm2);
  if (iVar1 == 0) {
    puts("Correct!");
    printf("castorsCTF{%s}",&DAT_00302030);
    fflush(stdout);
  }
  else {
    puts("Wrong!");
    fflush(stdout);
  }
  return;
}

以下の処理をしている。

・フラグ文字の1文字ずつに対して以下の処理
 ・a = フラグ文字 + 2
 ・aが英小文字の場合は、さらに以下の処理
  b = (((a - 0x57) - ((((a - 0x57) % 256) / 0x1a) % 256) * 0x1a + ord('a')) % 256

英小文字の場合のみ逆算の計算が面倒なので、あらかじめ英小文字の結果を取得しておき、元に戻す。
flag.txtに以下を書き、試す。

_`abcdefghijklmnopqrstuvwx
$ ./reverse_me 
System Error...
Dumping memory...
6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 61 62 63 64 65 66 67 68 69 6a 
Enter password: _`abcdefghijklmnopqrstuvwx
Correct!
castorsCTF{_`abcdefghijklmnopqrstuvwx}

以上のことから元に戻すスクリプトを作成し、サーバに対して実行する。

import socket

lower_codes = ['6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74',
    '75', '76', '77', '78', '79', '7a', '61', '62', '63', '64', '65', '66',
    '67', '68', '69', '6a']

chars = '_`abcdefghijklmnopqrstuvwx'

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals20.cybercastors.com', 14427))

data = recvuntil(s, ': ')

codes = data.split('\n')[-2].rstrip().split(' ')
password = ''
for code in codes:
    if code in lower_codes:
        index = lower_codes.index(code)
        password += chars[index]
    else:
        password += chr(int(code, 16) - 2)

print data + password
s.sendall(password + '\n')
data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '}')
print data

実行結果は以下の通り。

System Error...
Dumping memory...
64 35 68 35 64 37 33 7a 38 6b 33 37 6b 72 67 7a
Enter password: r3v3r51n6_15_fun
Correct!
castorsCTF{r3v3r51n6_15_fun}
castorsCTF{r3v3r51n6_15_fun}

Goose Chase (Crypto)

2つの画像のRGBの数値をXORして画像を生成する。

from PIL import Image

img1 = Image.open('chal.png').convert('RGB')
img2 = Image.open('goose_stole_the_key.png').convert('RGB')

w, h = img1.size

output_img = Image.new('RGB', (w, h), (255, 255, 255))

for y in range(h):
    for x in range(w):
        r1, g1, b1 = img1.getpixel((x, y))
        r2, g2, b2 = img2.getpixel((x, y))
        r = r1 ^ r2
        g = g1 ^ g2
        b = b1 ^ b2
        output_img.putpixel((x, y), (r, g, b))

output_img.save('flag.png')

生成した画像にフラグが書いてあった。
f:id:satou-y:20200608220953p:plain

castorsCTF{m355_w1th_7h3_h0nk_y0u_g3t_7h3_b0nk}

One Trick Pony (Crypto)

$ nc chals20.cybercastors.com 14422
> 1
b'R'
> 123
b'RS@'
> 11111111111111111111111111111111111111111111
b'RPBE^CBrewJZ\x02\x02AnH\x01DCnZ\x02H\x04n\x04\x02RC\x02\x06n\x05_UnU\x01_\x06nC\x02'
>

入力文字列とXORされたものが出力されているようだ。XOR鍵がフラグと推測する。\x00の文字列を平文として指定すれば、フラグが表示されるはず。

import socket

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

def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals20.cybercastors.com', 14422))

try_pt = '\x00' * 64
data = recvuntil(s, '> ')
print data + try_pt
s.sendall(try_pt + '\n')
data = recvuntil(s, '\n').rstrip()
print data

実行結果は以下の通り。

>
b'castorsCTF{k33p_y0ur_k3y5_53cr37_4nd_d0n7_r3u53_7h3m!}castorsCTF'
castorsCTF{k33p_y0ur_k3y5_53cr37_4nd_d0n7_r3u53_7h3m!}

Bagel Bytes (Crypto)

$ nc chals20.cybercastors.com 14420

 ______  ______  ______  ______  __           ______  __  __  ______ ______  ______    
/\  == \/\  __ \/\  ___\/\  ___\/\ \         /\  == \/\ \_\ \/\__  _/\  ___\/\  ___\   
\ \  __<\ \  __ \ \ \__ \ \  __\\ \ \____    \ \  __<\ \____ \/_/\ \\ \  __\\ \___  \  
 \ \_____\ \_\ \_\ \_____\ \_____\ \_____\    \ \_____\/\_____\ \ \_\\ \_____\/\_____\ 
  \/_____/\/_/\/_/\/_____/\/_____/\/_____/     \/_____/\/_____/  \/_/ \/_____/\/_____/ 
                                                                                       
Welcome to Bagel Bytes!
Our ovens are known for baking ExtraCrispyBagels!
We bake 16 bagels per rack and the last rack is always full!
Today's special is flag bagels!

Select:
    1) Bake your own bagels!
    2) Bake the flag!
Your choice: 2
Add your bagels:
> 1

Thank you for baking with us! Here are your baked bytes:
5aa7c425b025966d8cf06149038fe69e2749234fac85fec430becdb8c824ef6c23a6f85858a2f04f13fc5758c2afba57

Select:
    1) Bake your own bagels!
    2) Bake the flag!
Your choice: 2
Add your bagels:
> 2

Thank you for baking with us! Here are your baked bytes:
ba1a164991d63589a4cd4bb3a9f7f55d2749234fac85fec430becdb8c824ef6c23a6f85858a2f04f13fc5758c2afba57

Select:
    1) Bake your own bagels!
    2) Bake the flag!
Your choice: 1
Add your bagels:
> 11

Thank you for baking with us! Here are your baked bytes:
2477b150ef383a265ea6a453184e6552

Select:
    1) Bake your own bagels!
    2) Bake the flag!
Your choice: 

AES暗号のようだが、Add your bagelsの意味がわからない。

Select:
    1) Bake your own bagels!
    2) Bake the flag!
Your choice: 2
Add your bagels:
> 111111

Thank you for baking with us! Here are your baked bytes:
1686f128021d97491fbcc16c4807ad82ae782968412a8a6a73fb3fc05f507a2a93c0f899b9a1e5060f1fac207d850461

Select:
    1) Bake your own bagels!
    2) Bake the flag!
Your choice: 2
Add your bagels:
> 1111111111111111111111

Thank you for baking with us! Here are your baked bytes:
66e32e645b7ee61b72962e7717b50e2a1686f128021d97491fbcc16c4807ad82ae782968412a8a6a73fb3fc05f507a2a93c0f899b9a1e5060f1fac207d850461

1686f128021d97491fbcc16c4807ad82ae782968412a8a6a73fb3fc05f507a2a93c0f899b9a1e5060f1fac207d850461

66e32e645b7ee61b72962e7717b50e2a
1686f128021d97491fbcc16c4807ad82ae782968412a8a6a73fb3fc05f507a2a93c0f899b9a1e5060f1fac207d850461

前に文字をaddして暗号化していると思われる。16の倍数のデータの場合、通常と違いパディングなし

Select:
    1) Bake your own bagels!
    2) Bake the flag!
Your choice: 2
Add your bagels:
> 111111

Thank you for baking with us! Here are your baked bytes:
1686f128021d97491fbcc16c4807ad82ae782968412a8a6a73fb3fc05f507a2a93c0f899b9a1e5060f1fac207d850461

Select:
    1) Bake your own bagels!
    2) Bake the flag!
Your choice: 2
Add your bagels:
> 1111111

Thank you for baking with us! Here are your baked bytes:
b59584584b933c35549d97f5a53e48051cb1d5c0b2d1bf1700a7443ebd49aa90d4dc5054965afba3dde5fa80ec3d7a427123e38a8266811053a13936adffb5f1

以下のようなイメージで、1文字ずつはみ出させながら、暗号結果が一致するものを探していく。

0123456789abcdef
AAAAAAFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF

0123456789abcdef
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFPPPPPPP
import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals20.cybercastors.com', 14420))

flag = ''
for i in range(42):
    try_pt = '#' * (47 - i)
    data = recvuntil(s, 'choice: ')
    print data + '2'
    s.sendall('2\n')
    data = recvuntil(s, '> ')
    print data + try_pt
    s.sendall(try_pt + '\n')

    data = recvuntil(s, '\n').rstrip()
    print data
    data = recvuntil(s, '\n').rstrip()
    print data
    data = recvuntil(s, '\n').rstrip()
    print data
    ct2 = data[64:96]

    for code in range(32, 127):
        if i < 16:
            try_pt = '#' * (15 - i) + flag + chr(code)
        else:
            try_pt = flag[-15:] + chr(code)
        data = recvuntil(s, 'choice: ')
        print data + '1'
        s.sendall('1\n')
        data = recvuntil(s, '> ')
        print data + try_pt
        s.sendall(try_pt + '\n')

        data = recvuntil(s, '\n').rstrip()
        print data
        data = recvuntil(s, '\n').rstrip()
        print data
        data = recvuntil(s, '\n').rstrip()
        print data
        print ct2
        if data == ct2:
            flag += chr(code)
            break

print flag

実行結果は以下の通り。

        :
Select:
    1) Bake your own bagels!
    2) Bake the flag!
Your choice: 1
Add your bagels:
> l5_3x7r4_cr15pY}

Thank you for baking with us! Here are your baked bytes:
93c0f899b9a1e5060f1fac207d850461
93c0f899b9a1e5060f1fac207d850461
castorsCTF{I_L1k3_muh_b4G3l5_3x7r4_cr15pY}
castorsCTF{I_L1k3_muh_b4G3l5_3x7r4_cr15pY}

Magic School Bus (Crypto)

$ nc chals20.cybercastors.com 14421

 __    __  ______  ______  __  ______       ______  ______  __  __  ______  ______  __           ______  __  __  ______    
/\ "-./  \/\  __ \/\  ___\/\ \/\  ___\     /\  ___\/\  ___\/\ \_\ \/\  __ \/\  __ \/\ \         /\  == \/\ \/\ \/\  ___\   
\ \ \-./\ \ \  __ \ \ \__ \ \ \ \ \____    \ \___  \ \ \___\ \  __ \ \ \/\ \ \ \/\ \ \ \____    \ \  __<\ \ \_\ \ \___  \  
 \ \_\ \ \_\ \_\ \_\ \_____\ \_\ \_____\    \/\_____\ \_____\ \_\ \_\ \_____\ \_____\ \_____\    \ \_____\ \_____\/\_____\ 
  \/_/  \/_/\/_/\/_/\/_____/\/_/\/_____/     \/_____/\/_____/\/_/\/_/\/_____/\/_____/\/_____/     \/_____/\/_____/\/_____/ 
                                                                                                                           

All right kids!
The Magic Flag Bus is about to leave!
Make sure to fill each row as you step in.
Wait, what! All the kids got moved around.
I *knew* I should've stayed home today!
Nevermind... Load your own bus and let's leave.

Select:
    1) Load magic school bus
    2) View magic flag bus
Your choice: 2

Flag bus seating: SNESYT3AYN1CTISL7SRS31RAFSKV3C4I0SOCNTGER0COM5

Select:
    1) Load magic school bus
    2) View magic flag bus
Your choice: 1

Who's riding the bus?: aaaaaaaa
Bus seating: AAAAAAAA

Select:
    1) Load magic school bus
    2) View magic flag bus
Your choice: 1   

Who's riding the bus?: abcdefgh
Bus seating: GDACBHEF

Select:
    1) Load magic school bus
    2) View magic flag bus
Your choice: 1

Who's riding the bus?: ABCDEFGHIJKL
Bus seating: GDLAICKBJHEF

Select:
    1) Load magic school bus
    2) View magic flag bus
Your choice:

どうやら並び替えをしているようだ。フラグを同じ長さの文字列を入れて、変更後の順序をあてはめて、戻していけばよい。

import socket
import string

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals20.cybercastors.com', 14421))

data = recvuntil(s, 'choice: ')
print data + '2'
s.sendall('2\n')
data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data
flag_enc = data.split(' ')[-1]

assert len(flag_enc) == 46

indexes = []

try_pt = string.uppercase[:23] + 'Z' * 23
data = recvuntil(s, 'choice: ')
print data + '1'
s.sendall('1\n')
data = recvuntil(s, ': ')
print data + try_pt
s.sendall(try_pt + '\n')
data = recvuntil(s, '\n').rstrip()
print data
try_ct = data.split(' ')[-1]

for i in range(23):
    index = try_ct.index(string.uppercase[i])
    indexes.append(index)

try_pt = 'Z' * 23 + string.uppercase[:23]
data = recvuntil(s, 'choice: ')
print data + '1'
s.sendall('1\n')
data = recvuntil(s, ': ')
print data + try_pt
s.sendall(try_pt + '\n')
data = recvuntil(s, '\n').rstrip()
print data
try_ct = data.split(' ')[-1]

for i in range(23):
    index = try_ct.index(string.uppercase[i])
    indexes.append(index)

flag = ''
for index in indexes:
    flag += flag_enc[index]
print flag

実行結果は以下の通り。

        :
 __    __  ______  ______  __  ______       ______  ______  __  __  ______  ______  __           ______  __  __  ______
/\ "-./  \/\  __ \/\  ___\/\ \/\  ___\     /\  ___\/\  ___\/\ \_\ \/\  __ \/\  __ \/\ \         /\  == \/\ \/\ \/\  ___\
\ \ \-./\ \ \  __ \ \ \__ \ \ \ \ \____    \ \___  \ \ \___\ \  __ \ \ \/\ \ \ \/\ \ \ \____    \ \  __<\ \ \_\ \ \___  \
 \ \_\ \ \_\ \_\ \_\ \_____\ \_\ \_____\    \/\_____\ \_____\ \_\ \_\ \_____\ \_____\ \_____\    \ \_____\ \_____\/\_____\
  \/_/  \/_/\/_/\/_/\/_____/\/_/\/_____/     \/_____/\/_____/\/_/\/_/\/_____/\/_____/\/_____/     \/_____/\/_____/\/_____/
                                                                                

All right kids!
The Magic Flag Bus is about to leave!
Make sure to fill each row as you step in.
Wait, what! All the kids got moved around.
I *knew* I should've stayed home today!
Nevermind... Load your own bus and let's leave.

Select:
    1) Load magic school bus
    2) View magic flag bus
Your choice: 2

Flag bus seating: SNESYT3AYN1CTISL7SRS31RAFSKV3C4I0SOCNTGER0COM5

Select:
    1) Load magic school bus
    2) View magic flag bus
Your choice: 1

Who's riding the bus?: ABCDEFGHIJKLMNOPQRSTUVWZZZZZZZZZZZZZZZZZZZZZZZ
Bus seating: GOWZZDLTZZZAIQZZZCKSZZZBJRZZZHPZZZEMUZZZFNVZZZ

Select:
    1) Load magic school bus
    2) View magic flag bus
Your choice: 1

Who's riding the bus?: ZZZZZZZZZZZZZZZZZZZZZZZABCDEFGHIJKLMNOPQRSTUVW
Bus seating: ZZZHPZZZEMUZZZBJRZZZDLTZZZCKSZZAIQZZZFNVZZZGOW
CASTORSCTFR3C0N4ISSANCEISK3YTOS0LV1NGMYS73R1E5
CASTORSCTF{R3C0N4ISSANCE_IS_K3Y_TO_S0LV1NG_MYS73R1E5}

Jigglypuff's Song (Crypto)

高さ5ピクセルごとに別の画像が入っているので、切り出す。

from PIL import Image

img = Image.open('chal.png').convert('RGB')
w, h = img.size

output_img = Image.new('RGB', (w, h / 5 + 1), (255, 255, 255))

for y in range(0, h, 5):
    for x in range(0, w):
        r, g, b = img.getpixel((x, y))
        output_img.putpixel((x, y / 5), (r, g, b))

output_img.save('flag.png')

f:id:satou-y:20200608221708p:plain
切り出した画像が粗いので、Stegsolveで開き、[Analyse]-[Data Extract]の画面からRGBのMSB(7)すべてにチェックを入れると、文章が出てくる。

We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
I just wanna tell you how I'm feeling
Gotta make you understand
Never gonna give you up
Never gonna let you down
        :
Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you
castorsCTF{r1ck_r0ll_w1ll_n3v3r_d3s3rt_y0uuuu}We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
I just wanna tell you how I'm feeling
        :
Never gonna say goodbye
Never gonna tell a lie and hurt you
Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

この文章中にフラグがあった。

castorsCTF{r1ck_r0ll_w1ll_n3v3r_d3s3rt_y0uuuu}

Warmup (Crypto)

chal.pngの直角三角形の画像から以下の法則のことがわかる。

・a**2 + b**2 = c**2
・a * b / 2 = A

chal.txtにはこう書いてある。

・a = p + q
・b = p - q

このことから式を変換していく。

a * b = A * 2

p = (a + b) / 2
q = (a - b) / 2

(a + b)**2 = a**2 + b**2 + 2*a*b
           = c**2 + A*4
->(2*p)**2 = c**2 + A*4
-> p = sqrt(c**2 + A*4) / 2

(a - b)**2 = a**2 + b**2 - 2*a*b
           = c**2 - A*4
->(2*q)**2 = c**2 - A*4
-> q = sqrt(c**2 - A*4) / 2

sqrtの値が整数にならない。chal.txtの中のcはc**2の値として計算する。

import gmpy2
from Crypto.Util.number import long_to_bytes

c = 41027546415588921135190519817388916847442693284567375482282571314638757544938653824671437300971782426302443281077457253827782026089649732942648771306702020
A = 1780602199528179468577178612383888301611753776788787799581979768613992169436352468580888042155360498830144442282937213247708372597613226926855391934953064
e = 0x10001
enc = 825531027337680366509171870396193970230179478931882005355846498785843598000659828635030935743236266080589740863128695174980645084614454653557872620514117

p = gmpy2.iroot(c + A * 4, 2)[0] / 2
assert pow(p, 2) * 4 == c + A * 4

q = gmpy2.iroot(c - A * 4, 2)[0] / 2
assert pow(q, 2) * 4 == c - A * 4

phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = pow(enc, d, p * q)
flag = long_to_bytes(m)
print flag
castorsCTF{n0th1ng_l1k3_pr1m3_numb3r5_t0_w4rm_up_7h3_3ng1n3s}

Two Paths (Crypto)

PNGの後ろに2進数8桁の文字列が複数くっついている。デコードすると、URLになる。

https://go.aws/2zuCFCp

ここにアクセスすると、以下のURLになり、換字式暗号ようなページになる。

https://ctf-emoji-cipher.s3.amazonaws.com/decode_this.html

文字に置き換える。

ABCDEFGHIFGJBCK!_JL_MBH_AFC_ENFO_GPJK,_GPNC_MBH_PFQN_KBIQNO_GPN_AJRPNE!_SN_THKG_PBRN_MBH_LBHCO_F_UBEN_NLLJAJNCG_SFM_LBE_ONAJRPNEJCD_GPFC_DBJCD_BCN_VM_BCN_BE_NIKN_MBH_SBC'G_DNG_GPEBHDP_GPN_CNWG_RFEG_QNEM_XHJAYIM._UFMVN_GEM_VNJCD_F_IJGGIN_UBEN_IFZM!
ALC_YJWRZVC_DWVEBYPPWP_UHK{OHJPHAQ_HPYY}KOTPLB_YH{WHPCRCU}O_ZECRCCNM_LR_AYCWEEN{BZI_AGDGEPCA}WK_{BLFLOTRBDG_BMRILABW}ZV_MPB{GPLWXWL_XN}WRZBNVHQ_ACMKWG_GZIS_ZOC{QDONTSB_TI_WNTKGUXF_HQ}TVBLQNAH_UYW{ERVLXKA_L}SJESTLTTC_GL{TTKDAT}PG_PDXEKOFRZY_KK{BRRBMXFD_CB}VECZZFGH_CWMIOYI{RRI_X}LPWZMUALF_KO{RRNTNDPS_UVTPAPZ_CN_ERFSMMCEZH_RW}ETYTLLMQ_JFIWMEW{QYN_KHJE_SNYGHB_GL{TTKDAT}PG_PDXEKOFRZY_KK{BRRBMX_FD_CB}VECZZFGH_CWMIOYI{RRI_MPB{GPLPB{GPLWXWL_XN}WRZBNVHQ_ACMKWG_GZIS_ZOC{QDONTSB_TI_WNTKGUXF_HQ}TVBLQNAH_UYW{ERVLXKA_L}SJESTLTTC_GL{TTKDAT}PG_PDXEKOFRZY_KK{BRRBMXFD_CB}VECZZFGHWXWL_XN}WRZBNVHQ_ACMKWGGZIS_ZOC{Q_DONTSB_TIWNTKGUXF_HQ}TVBLQNAH_UY_W{ERVLXKA_L}SJESTL_KHJE_SNYGHB_GL{TTKDAT}PG_PDXEKOFRZY_KK{BRRBMX_FD_CB}VECZDWVEBYPPWP_UHK{OHJPHAQ_HPYY}KOTPLB_YH{WHPCRCU}O_ZECRCCNM_LR_AYCWEEN{BZI_AGDGEPCA}WK_{BLFLOTRBDGZFGH_CWMIOYI{RRI_MPB{GPLWXWL_XN}WRZBNVHQ_ACMKWGGZISTTC_GL{TTKDAT}PG_PDXEKOFRZY_UVTPAPZW_CN_ERFSMMCEZH_RW}ETYTLLMQ_JFIW_MEW{QYN_KHJESNYGHB_VIQ{FAVSGPD_OFVNBZMNSB_VBCQWIFBJP_DF}HNFMUCDX_I{VCTCNACLC_N}W_VMYGDNMG_VBTKUTWLFU_DKUQ{VCGZSL_QVNFXGKVSV_DHRVBGYQV}E_AFKGBEKAGL{KFCABAPB_LIFD_XTZUIRD}_RL{GSXIJEXE_BYVEZZGSEN_UZUIDRUXYW_SOYHUNCVRY_QA}HLGNNXGZ_CGK{ZYKTJGO_JGOSOZ_H}H_AYP{MXPGMW_WAFCVENWLC_Z}ZYZZTYYLD_JSBRJ{SBNEA_OIFU}POLS_PS

_をスペースにしてquipqiupで復号すると、冒頭部分はこうなる。

CONGRATULATIONS! IF YOU CAN READ THIS, THEN YOU HAVE SOLVED THE CIPHER! WE JUST HOPE YOU FOUND A MORE EFFICIENT WAY FOR DECIPHERING THAN GOING ONE BY ONE OR ELSE YOU WON'T GET THROUGH THE NEXT PART VERY QUICKLY. MAYBE TRY BEING A LITTLE MORE LAZY!

あとは同じ対応ですべてを復号する。

with open('msg_enc.txt', 'r') as f:
    enc = f.read()

C = 'FVAONLDPJTYIUCBRXEKGHQSWMZ'
P = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

dec = ''
for c in enc:
    if c in C:
        index = C.index(c)
        dec += P[index]
    else:
        dec += c
print dec

実行結果は以下の通り。

CONGRATULATIONS!_IF_YOU_CAN_READ_THIS,_THEN_YOU_HAVE_SOLVED_THE_CIPHER!_WE_JUST_HOPE_YOU_FOUND_A_MORE_EFFICIENT_WAY_FOR_DECIPHERING_THAN_GOING_ONE_BY_ONE_OR_ELSE_YOU_WON'T_GET_THROUGH_THE_NEXT_PART_VERY_QUICKLY._MAYBE_TRY_BEING_A_LITTLE_MORE_LAZY!
CFN_KIXPZBN_GXBROKHHXH_MUS{DUIHUCV_UHKK}SDJHFO_KU{XUHNPNM}D_ZRNPNNEY_FP_CKNXRRE{OZL_CTGTRHNC}XS_{OFAFDJPOGT_OYPLFCOX}ZB_YHO{THFXQXF_QE}XPZOEBUV_CNYSXT_TZLW_ZDN{VGDEJWO_JL_XEJSTMQA_UV}JBOFVECU_MKX{RPBFQSC_F}WIRWJFJJN_TF{JJSGCJ}HT_HGQRSDAPZK_SS{OPPOYQAG_NO}BRNZZATU_NXYLDKL{PPL_Q}FHXZYMCFA_SD{PPEJEGHW_MBJHCHZ_NE_RPAWYYNRZU_PX}RJKJFFYV_IALXYRX{VKE_SUIR_WEKTUO_TF{JJSGCJ}HT_HGQRSDAPZK_SS{OPPOYQ_AG_NO}BRNZZATU_NXYLDKL{PPL_YHO{THFHO{THFXQXF_QE}XPZOEBUV_CNYSXT_TZLW_ZDN{VGDEJWO_JL_XEJSTMQA_UV}JBOFVECU_MKX{RPBFQSC_F}WIRWJFJJN_TF{JJSGCJ}HT_HGQRSDAPZK_SS{OPPOYQAG_NO}BRNZZATUXQXF_QE}XPZOEBUV_CNYSXTTZLW_ZDN{V_GDEJWO_JLXEJSTMQA_UV}JBOFVECU_MK_X{RPBFQSC_F}WIRWJF_SUIR_WEKTUO_TF{JJSGCJ}HT_HGQRSDAPZK_SS{OPPOYQ_AG_NO}BRNZGXBROKHHXH_MUS{DUIHUCV_UHKK}SDJHFO_KU{XUHNPNM}D_ZRNPNNEY_FP_CKNXRRE{OZL_CTGTRHNC}XS_{OFAFDJPOGTZATU_NXYLDKL{PPL_YHO{THFXQXF_QE}XPZOEBUV_CNYSXTTZLWJJN_TF{JJSGCJ}HT_HGQRSDAPZK_MBJHCHZX_NE_RPAWYYNRZU_PX}RJKJFFYV_IALX_YRX{VKE_SUIRWEKTUO_BLV{ACBWTHG_DABEOZYEWO_BONVXLAOIH_GA}UEAYMNGQ_L{BNJNECNFN_E}X_BYKTGEYT_BOJSMJXFAM_GSMV{BNTZWF_VBEAQTSBWB_GUPBOTKVB}R_CASTORSCTF{SANCOCHO_FLAG_QJZMLPG}_PF{TWQLIRQR_OKBRZZTWRE_MZMLGPMQKX_WDKUMENBPK_VC}UFTEEQTZ_NTS{ZKSJITD_ITDWDZ_U}U_CKH{YQHTYX_XCANBREXFN_Z}ZKZZJKKFG_IWOPI{WOERC_DLAM}HDFW_HW

この中でCASTORSCTF{で始まる部分が1箇所ある。

castorsCTF{sancocho_flag_qjzmlpg}