HackPack CTF 2021 Writeup

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

A sense of community... (sanity)

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

flag{w3Lc0mE_2_tH3_p4ck}

Aurora (misc)

jpgファイルが添付されている。EXIFを見てみる。

$ exiftool Aurora.jpg 
ExifTool Version Number         : 10.80
File Name                       : Aurora.jpg
Directory                       : .
File Size                       : 80 kB
File Modification Date/Time     : 2021:04:17 00:12:28+09:00
File Access Date/Time           : 2021:04:17 00:14:32+09:00
File Inode Change Date/Time     : 2021:04:17 00:12:28+09:00
File Permissions                : rwxrwxrwx
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.02
Resolution Unit                 : None
X Resolution                    : 100
Y Resolution                    : 100
Comment                         : flag{6e4ut1fuL_Aur0r4}
Quality                         : 60%
DCT Encode Version              : 100
APP14 Flags 0                   : [14], Encoded with Blend=1 downsampling
APP14 Flags 1                   : (none)
Color Transform                 : YCbCr
Image Width                     : 960
Image Height                    : 641
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:4:4 (1 1)
Image Size                      : 960x641
Megapixels                      : 0.615

Commentにフラグがセットされていた。

flag{6e4ut1fuL_Aur0r4}

Euler's Identity (misc)

減点なしのHintを見たら、以下のように書いてある。

For example, given MD5(This), SHA1(is), and SHA2(SHA1(MD5(future))), the key is flag{Thisisfuture}.

それぞれhttps://crackstation.net/でクラックする。

d8d540ae49aadd151b96feb4e0ff124f → e^
abb07ca45c9e7719e66e766b958d943f561b8de6 → ipi=
c037c03ee627047a85df540c42d59c6b6028841704a7c706feff584a997fd2a3 → Not found.

等式になっているので、最後のハッシュは"-1"のハッシュと推測する。

>>> from hashlib import *
>>> sha256(sha1(md5('-1').hexdigest()).hexdigest()).hexdigest()
'c037c03ee627047a85df540c42d59c6b6028841704a7c706feff584a997fd2a3'

最後のハッシュの元の文字列は"-1"で合っている。

flag{e^ipi=-1}

Regex World! (misc)

正規表現から交わる文字は両方使えるようわかるものから解いていく。

YOUG
OTTH
ISRE
GEX!
flag{YOUGOTTHISREGEX!}

Function Pointer Fun (reverse)

Ghidraでデコンパイルする。

int main(int argc,char **argv)

{
  long lVar1;
  bool bVar2;
  anon_subr_int *paVar3;
  long in_FS_OFFSET;
  _Bool changed;
  int i;
  anon_subr_int_varargs *fp;
  char seed [5];
  
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  setvbuf(stdout,(char *)0x0,2,0);
  seed._0_4_ = 0;
  seed[4] = '\0';
  printf("Hello, Mr. Eusk. \nPassword > ");
  __isoc99_scanf(&DAT_00102026,seed);
  bVar2 = false;
  i = 0;
  while (i < 4) {
    if (seed[i] != '\0') {
      bVar2 = true;
    }
    i = i + 1;
  }
  if (bVar2) {
    paVar3 = pickFunction(seed);
    (*paVar3)();
  }
  else {
    puts("You gotta give an input!");
  }
  if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return (int)(uint)!bVar2;
}

anon_subr_int * pickFunction(char *seed)

{
  byte bVar1;
  code *pcVar2;
  long in_FS_OFFSET;
  char resOne;
  char resTwo;
  char res;
  anon_subr_int_varargs *funcPtr [5];
  
  bVar1 = (seed[1] | *seed) & (seed[3] | seed[2]);
  if (bVar1 == 0x49) {
    pcVar2 = funTwo;
  }
  else {
    if (((char)bVar1 < '\x01') || ('\x1f' < (char)bVar1)) {
      if (((char)bVar1 < ' ') || ('?' < (char)bVar1)) {
        if (((char)bVar1 < '@') || ('_' < (char)bVar1)) {
          pcVar2 = funFive;
        }
        else {
          pcVar2 = funFour;
        }
      }
      else {
        pcVar2 = funThree;
      }
    }
    else {
      pcVar2 = funOne;
    }
  }
  if (*(long *)(in_FS_OFFSET + 0x28) != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return pcVar2;
}

int funTwo(void)

{
  long lVar1;
  FILE *__stream;
  long in_FS_OFFSET;
  FILE *fp;
  char flag [25];
  
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  __stream = fopen("flag","r");
  fgets(flag,0x19,__stream);
  puts(flag);
  if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 1;
}

このコードから以下のことがわかる。

・seedは4バイト
・bVar1が0x49の場合、フラグが表示される。

総当たりで条件を満たすものを探す。

import itertools
import string

chars = string.letters + string.digits

for c in itertools.product(chars, repeat=4):
    seed = ''.join(c)
    bVar1 = (ord(seed[1]) | ord(seed[0])) & (ord(seed[3]) | ord(seed[2]))
    if bVar1 == 0x49:
        print seed
        break

この結果、以下の文字列は条件を満たすことが分かる。

ahAH
$ nc ctf2021.hackpack.club 10998
Hello, Mr. Eusk. 
Password > ahAH
flag{c1RcU1t5_R_fUn!2!}
flag{c1RcU1t5_R_fUn!2!}

BF means best friend, right? (reverse)

Brainf*ck言語。https://www.dcode.fr/brainfuck-languageで実行

Console
Memory:
[0] = b (98)
[1] = r (114)
[2] = a (97)
[3] = i (105)
[4] = n (110)
[5] = - (45)
[6] = b (98)
[7] = l (108)
[8] = a (97)
[9] = s (115)
[10] = t (116)
flag{brain-blast}