SarCTF by Saratov State University Writeup

この大会は2020/2/15 23:00(JST)~2020/2/16 23:00(JST)に開催されました。
今回もチームで参戦。結果は 7279点で418チーム中84位でした。
自分で解けた問題をWriteupとして書いておきます。

Deep dive (Misc)

$ file flag.txt
flag.txt: POSIX tar archive (GNU)
$ tar xvf flag.txt
./
./flag.txt
$ file flag.txt
flag.txt: Zip archive data, at least v2.0 to extract
$ file flag.txt
flag.txt: bzip2 compressed data, block size = 900k

何重にも圧縮されている。スクリプトで解凍する。xzを解凍する際には、「ファイルのパーミッションを設定できません: 定義されたデータ型に対して値が大きすぎます」と警告が表示され、すぐに次の処理に入ると、エラーになるので、sleep処理を入れた。

import subprocess
import os
import time

cmd_file = 'file %s'
cmd_tar = 'tar xvf %s'
cmd_zip = 'unzip %s'
cmd_bz2 = 'bzip2 -d %s'
cmd_gz = 'gzip -d %s'
cmd_xz = 'xz -d %s'

filename_base = 'flag'
filename_txt = 'flag.txt'
filename_tar = 'flag.tar'
filename_zip = 'flag.zip'
filename_bz2 = 'flag.bz2'
filename_gz = 'flag.gz'
filename_xz = 'flag.xz'

i = 1
while True:
    print '%d times' % i
    cmd = cmd_file % filename_txt
    ret = subprocess.check_output( cmd.split(" ") )
    print ret
    if 'tar archive' in ret:
        os.rename(filename_txt, filename_tar)
        os.system(cmd_tar % filename_tar)
        os.remove(filename_tar)
    elif 'Zip archive' in ret:
        os.rename(filename_txt, filename_zip)
        os.system(cmd_zip % filename_zip)
        os.remove(filename_zip)
    elif 'bzip2 compressed' in ret:
        os.rename(filename_txt, filename_bz2)
        os.system(cmd_bz2 % filename_bz2)
        os.rename(filename_base, filename_txt)
    elif 'gzip compressed' in ret:
        os.rename(filename_txt, filename_gz)
        os.system(cmd_gz % filename_gz)
        os.rename(filename_base, filename_txt)
    elif 'XZ compressed' in ret:
        os.rename(filename_txt, filename_xz)
        os.system(cmd_xz % filename_xz)
        time.sleep(2)
        os.rename(filename_base, filename_txt)
    else:
        break
    i += 1

実行結果は以下の通り。

        :
722 times
flag.txt: POSIX tar archive (GNU)

./
./flag.txt
723 times
flag.txt: ASCII text, with no line terminators

$ cat flag.txt
FLAG{matri0sha256}
FLAG{matri0sha256}

Blogger (Forensics)

pcapngからUSBのキー入力を読み取る。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from scapy.all import *

keymap = { 0x04: ('a', 'A'), 0x05: ('b', 'B'), 0x06: ('c', 'C'),
           0x07: ('d', 'D'), 0x08: ('e', 'E'), 0x09: ('f', 'F'),
           0x0a: ('g', 'G'), 0x0b: ('h', 'H'), 0x0c: ('i', 'I'),
           0x0d: ('j', 'J'), 0x0e: ('k', 'K'), 0x0f: ('l', 'L'),
           0x10: ('m', 'M'), 0x11: ('n', 'N'), 0x12: ('o', 'O'),
           0x13: ('p', 'P'), 0x14: ('q', 'Q'), 0x15: ('r', 'R'),
           0x16: ('s', 'S'), 0x17: ('t', 'T'), 0x18: ('u', 'U'),
           0x19: ('v', 'V'), 0x1a: ('w', 'W'), 0x1b: ('x', 'X'),
           0x1c: ('y', 'Y'), 0x1d: ('z', 'Z'), 0x1e: ('1', '!'),
           0x1f: ('2', '@'), 0x20: ('3', '#'), 0x21: ('4', '$'),
           0x22: ('5', '%'), 0x23: ('6', '^'), 0x24: ('7', '&'),
           0x25: ('8', '*'), 0x26: ('9', '('), 0x27: ('0', ')'),
           0x28: ('\x0a', '\x0a'), 0x29: ('\x1b', '\x1b'),
           0x2a: ('\x08', '\x08'), 0x2b: ('\x09', '\x09'),
           0x2c: ('\x20', '\x20'), 0x2d: ('-', '_'),
           0x2e: ('=', '+'), 0x2f: ('[', '{'), 0x30: (']', '}'),
           0x31: ('\\', '|'), 0x33: (';', ':'), 0x34: ('\'', '\"'),
           0x35: ('`', '~'), 0x36: (',', '<'), 0x37: ('.', '>'),
           0x38: ('/', '?')}

packets = rdpcap('usb_here.pcapng')
usb_data = []
for p in packets:
    buf = p['Raw'].load
    if buf[22] == '\x01' and len(buf[27:]) > 0:
        usb_data.append(buf[27:])

msg = ''
for d in usb_data:
    if d[2] == '\x00' or not('\x00' in d[3:8]):
        continue
    elif ord(d[2]) not in keymap:
        continue
    if d[0] == '\x02' or d[0] == '\x20':
        c = keymap[ord(d[2])][1]
        msg += c
    else:
        c = keymap[ord(d[2])][0]
        msg += c

print msg

実行結果は以下の通り。

		Sherlock, John, and Henry then visit the hollow in the hope of finding the hound. On the way, John notices what seems to be FLAG{like_a_b100dh0und}		e
FLAG{like_a_b100dh0und}

Magic of numbers (PPC)

$ nc 212.47.229.1 33004
======================================================================================================
Hey, hello! Just send me an answer to 9 simple examples, so I can check if my machine knows math well.
======================================================================================================
[>] 0 + 1
[>] Result: 1
[>] 1 + 3
[>] Result: 4
[>] 2 + 5
[>] Result: 7
[>] 3 + 7
[>] Result: 10
[>] 4 + 9
[>] Result: 13
[>] 5 + 11
[>] Result: 16
[>] 6 + 13
[>] Result: 19
[>] 7 + 15
[>] Result: 22
[>] 8 + 17
[>] Result: 25
[>] 0.1 + 0.2
[>] Result: 0.3
You made a mistake somewhere! Bay, bay!

小数の計算については、コンピュータの場合の結果を答える必要がありそう。

$ nc 212.47.229.1 33004
======================================================================================================
Hey, hello! Just send me an answer to 9 simple examples, so I can check if my machine knows math well.
======================================================================================================
[>] 0 + 1
[>] Result: 1
[>] 1 + 3
[>] Result: 4
[>] 2 + 5
[>] Result: 7
[>] 3 + 7
[>] Result: 10
[>] 4 + 9
[>] Result: 13
[>] 5 + 11
[>] Result: 16
[>] 6 + 13
[>] Result: 19
[>] 7 + 15
[>] Result: 22
[>] 8 + 17
[>] Result: 25
[>] 0.1 + 0.2
[>] Result: 0.30000000000000004
FLAG{MaGiC_0f_NuMbErS}
FLAG{MaGiC_0f_NuMbErS}

Crossw0rd (Reverse)

$ ./crossw0rd 
Welcome. You're in function check. Please Enter a password to continue. 1 attempt remaining:
aaa
Wrong password! Your attempt is over.

正しいパスワードを入れる必要があるようだ。Ghidraでデコンパイルしてみる。

void check(void)

{
  char cVar1;
  long in_FS_OFFSET;
  char local_28 [24];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  puts(
      "Welcome. You\'re in function check. Please Enter a password to continue. 1 attemptremaining:"
      );
  scanf("%s",local_28);
  cVar1 = e(local_28);
  if (cVar1 == 0) {
    puts("Wrong password! Your attempt is over.");
  }
  else {
    puts("You cracked the system!");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}
ulong e(char *param_1)

{
  byte bVar1;
  char cVar2;
  
  if ((((param_1[7] == '5') && (param_1[0x11] == 'g')) && (param_1[2] == 'A')) &&
     (cVar2 = b(param_1), cVar2 != 0)) {
    bVar1 = 1;
  }
  else {
    bVar1 = 0;
  }
  return (ulong)bVar1;
}

ここから読み取れる条件は以下の通り。

・param_1[7] == '5'
・param_1[17] == 'g'
・param_1[2] == 'A'
・b(param_1) == 1
ulong b(char *param_1)

{
  byte bVar1;
  char cVar2;
  
  if ((((param_1[0xf] == 'i') && (param_1[9] == 'r')) && (param_1[1] == 'L')) &&
     (cVar2 = d(param_1), cVar2 != 0)) {
    bVar1 = 1;
  }
  else {
    bVar1 = 0;
  }
  return (ulong)bVar1;
}

ここから読み取れる条件は以下の通り。

・param_1[15] == 'i'
・param_1[9] == 'r'
・param_1[1] == 'L'
・d(param_1) == 1
ulong d(char *param_1)

{
  byte bVar1;
  char cVar2;
  
  if ((((param_1[10] == '3') && (param_1[0x12] == '}')) && (param_1[6] == 'a')) &&
     (cVar2 = f(param_1), cVar2 != 0)) {
    bVar1 = 1;
  }
  else {
    bVar1 = 0;
  }
  return (ulong)bVar1;
}

ここから読み取れる条件は以下の通り。

・param_1[10] == '3'
・param_1[18] == '}'
・param_1[6] == 'a'
・f(param_1) == 1
ulong f(char *param_1)

{
  byte bVar1;
  char cVar2;
  
  if ((((*param_1 == 'F') && (param_1[0xe] == '5')) && (param_1[0x10] == 'n')) &&
     (cVar2 = c(param_1), cVar2 != 0)) {
    bVar1 = 1;
  }
  else {
    bVar1 = 0;
  }
  return (ulong)bVar1;
}

ここから読み取れる条件は以下の通り。

・param_1[0] == 'F'
・param_1[14] == '5'
・param_1[16] == 'n'
・c(param_1) == 1
ulong c(char *param_1)

{
  byte bVar1;
  char cVar2;
  
  if ((((param_1[3] == 'G') && (param_1[0xb] == 'v')) && (param_1[5] == '3')) &&
     (cVar2 = a(param_1), cVar2 != 0)) {
    bVar1 = 1;
  }
  else {
    bVar1 = 0;
  }
  return (ulong)bVar1;
}

ここから読み取れる条件は以下の通り。

・param_1[3] == 'G'
・param_1[11] == 'v'
・param_1[5] == '3'
・a(param_1) == 1
undefined8 a(char *param_1)

{
  undefined8 uVar1;
  
  if ((((param_1[4] == '{') && (param_1[0xc] == '3')) && (param_1[8] == 'y')) &&
     (param_1[0xd] == 'r')) {
    uVar1 = 1;
  }
  else {
    uVar1 = 0;
  }
  return uVar1;
}

ここから読み取れる条件は以下の通り。

・param_1[4] == '{'
・param_1[12] == '3'
・param_1[8] == 'y'
・param_1[13] == 'r'

条件を順に並べる。

・param_1[0] == 'F'
・param_1[1] == 'L'
・param_1[2] == 'A'
・param_1[3] == 'G'
・param_1[4] == '{'
・param_1[5] == '3'
・param_1[6] == 'a'
・param_1[7] == '5'
・param_1[8] == 'y'
・param_1[9] == 'r'
・param_1[10] == '3'
・param_1[11] == 'v'
・param_1[12] == '3'
・param_1[13] == 'r'
・param_1[14] == '5'
・param_1[15] == 'i'
・param_1[16] == 'n'
・param_1[17] == 'g'
・param_1[18] == '}'

これでフラグはわかった。

FLAG{3a5yr3v3r5ing}

Find Moriarty (Stego)

3つのPNGファイルと1つのJPGファイルが添付されている。JPGファイルには"PASS: IATA"の文字がある。3つのPNGファイルは空港の画像のようだ。画像検索でどこの空港かを調べ、その情報をまとめてみる。

35d6d33467aae9a2e3dccb4b6b027878.png
ロンドン・ガトウィック空港
IATA空港コード: LGW
ファイル名逆md5: three

b8a9f715dbb64fd5c56e7783c6820a61.png
ロンドン・ヒースロー空港
IATA空港コード: LHR
ファイル名逆md5: two

f97c5d29941bfb1b2fdab0874906ab82.png
ロンドン・スタンステッド空港
IATA空港コード: STN
ファイル名逆md5: one

ファイル名をmd5クラックした結果のone, two, threeの順にIATA空港コードを並べたものがパスになる。steghideでこのパス"STNLHRLGW"を指定し、隠されたファイルを抽出する。

$ steghide extract -sf fly.jpg
Enter passphrase: 
wrote extracted data to "key.txt".
$ cat key.txt
FLAG{l375_fly_w17h_bu65}
FLAG{l375_fly_w17h_bu65}