UMDCTF 2021 Writeup

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

Hello World (Init)

問題にフラグが書いてあった。

UMDCTF-{h3ll0_w0rld}

Resume (Init)

適当な履歴書を作って、送信すると、フラグが表示された。

UMDCTF-{th@nk_y0uuuu}

Discord (Init)

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

UMDCTF-{w3lc0m3_t0_d1sc0rd}

Bomb1 - Quiz Time (Misc)

jarファイルが添付されているので、JD-GUIデコンパイルする。Bomb1クラスのbombOne()メソッドのflag変数にフラグが設定されていることがわかる。

UMDCTF-{c00l_math_f0r_c0llege_kidZ}

Starbucks (Reverse Engineering)

classファイルが添付されているので、JD-GUIデコンパイルする。

import java.io.PrintStream;

public class Challenge
{
  public static String f1(String s)
  {
    StringBuilder b = new StringBuilder();
    char[] arr = s.toCharArray();
    for (int i = 0; i < arr.length; ++i) {
      b.append((char)(arr[i] + i));
    }

    return b.toString();
  }

  public static String f1_rev(String s) {
    StringBuilder b = new StringBuilder();
    char[] arr = s.toCharArray();
    for (int i = 0; i < arr.length; ++i) {
      b.append((char)(arr[i] - i));
    }

    return b.toString();
  }

  public static String f2(String s) {
    int half = s.length() / 2;
    return s.substring(half + 1) + s.substring(0, half + 1);
  }

  public static String f3() {
    return f1(f2("$aQ\"cNP `_\29[eULB@PA'thpj]"));
  }

  public static void main(String[] args) {
    System.out.println("You really thought finding the flag would be so easy?");
  }
}

f3()の結果を出力する。

def f1(s):
    d = ''
    for i in range(len(s)):
        d += chr(ord(s[i]) + i)
    return d

def f2(s):
    half = len(s) / 2
    return s[half+1:] + s[:half+1]

flag = f1(f2("$aQ\"cNP `_\x1d[eULB@PA'thpj]"))
print flag
UMDCTF-{pyth0n_1s_b3tt3r}

The Matrix (Web)

$ curl http://chals5.umdctf.io:4000

<!doctype html>

<html lang="en">

<head>
    
    <link rel= "stylesheet" type= "text/css" href= "/static/main.css">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
    
    <meta charset="utf-8">
    <title>Enter the Matrix</title>
</head>
<body>
    <h1>The Matrix</h1>

    <nav id="nav" class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="#">Navbar</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>
  <div class="collapse navbar-collapse" id="navbarNav">
    <ul class="navbar-nav">
      <li class="nav-item active">
        <a class="nav-link" href="/">Home</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="/the-matrix">The Matrix</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="/about">About</a>
      </li>
    </ul>
  </div>
</nav>
</body>

<div class='centered-wrapper'>
    <a href="/the-matrix" class='button centered'>Enter the matrix!</a>
</div>

$ curl http://chals5.umdctf.io:4000/the-matrix
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/403">/403</a>.  If not click the link.

$ curl http://chals5.umdctf.io:4000/the-matrix -L
<h2>This page is for robots only, you are not allowed to access this content!</h2>

GooglebotのUserAgentを指定してアクセスしてみる。

$ curl http://chals5.umdctf.io:4000/the-matrix -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"

<!doctype html>

<html lang="en">

<head>
    
    <link rel= "stylesheet" type= "text/css" href= "/static/main.css">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
    
    <meta charset="utf-8">
    <title>Enter the Matrix</title>
</head>
<body>
    <h1>The Matrix</h1>

    <nav id="nav" class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="#">Navbar</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>
  <div class="collapse navbar-collapse" id="navbarNav">
    <ul class="navbar-nav">
      <li class="nav-item active">
        <a class="nav-link" href="/">Home</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="/the-matrix">The Matrix</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="/about">About</a>
      </li>
    </ul>
  </div>
</nav>
</body>

<h2>Greetings Fellow Robot Overlord</h2>
<p>UMDCTF-{r0b0t_r3b3ll!0n}</p>
UMDCTF-{r0b0t_r3b3ll!0n}

Not Slick (Forensics)

PNGファイルのバイナリデータがバイト単位で逆になっているので戻す。

with open('notslick.png', 'rb') as f:
    data = f.read()

with open('flag.png', 'wb') as f:
    f.write(data[::-1])

f:id:satou-y:20210427074340p:plain

UMDCTF-{abs01ute1y_r3v3r53d}

Protocol One and Zero (Forensics)

ICMPのパケットが大量にある。RAWデータの後半が\x00のパターンと\xffのパターンがあるので、0, 1と判定し、2進数としてデコードする。

from scapy.all import *

packets = rdpcap('protocol_one_and_zero.pcapng')

b_flag = ''
for p in packets:
    if p[ICMP].type == 0:
        if p[Raw].load[-1] == '\x00':
            b_flag += '0'
        else:
            b_flag += '1'

flag = ''
for i in range(0, len(b_flag), 8):
    flag += chr(int(b_flag[i:i+8], 2))
print flag
UMDCTF-{b1n_p1Ng_P0ng}

Testudo's Pizza (Steganography)

$ strings hiddenmsg.jpg | grep UMDCTF
\f0\fs24 \cf0 \'93UMDCTF-{W3_ar3_th3_b3st_P1ZZ3r1a}\'94}
UMDCTF-{W3_ar3_th3_b3st_P1ZZ3r1a}

Art Class (Crypto)

国際信号旗の暗号。https://ja.wikipedia.org/wiki/国際信号旗を参考に復号する。

UMDCTF-{F1AG_0F_7LA9S}

Celebration (Crypto)

踊る人形の暗号。https://www.dcode.fr/dancing-men-cipherで復号する。

UMDCTF-{yo_ITS_A_PARTYYY}

Office Secrets (Crypto)

同じnで異なるeでRSAの暗号化をしているものが2つあるので、Common Modules Attackで復号する。

import gmpy2
from Crypto.Util.number import *

def commom_modules_attack(c1, c2, e1, e2, n):
    gcd, s1, s2 = gmpy2.gcdext(e1, e2)
    if s1 < 0:
        s1 = -s1
        c1 = inverse(c1, n)
    elif s2 < 0:
        s2 = -s2
        c2 = inverse(c2, n)
 
    v = pow(c1, s1, n)
    w = pow(c2, s2, n)
    x = (v*w) % n
    return x

with open('details.txt', 'r') as f:
    data = f.read().split('\n')

n = int(data[0].split(' ')[-1])
e1 = int(data[1].split(' ')[-1])
e2 = int(data[2].split(' ')[-1])
c1 = int(data[3].split(' ')[-1])
c2 = int(data[4].split(' ')[-1])

m = commom_modules_attack(c1, c2, e1, e2, n)
flag = long_to_bytes(m)
print flag
UMDCTF-{Sh4r1ng_I5_d3f1n1t3ly_n0t_c4r!ng}

To Be Xor Not To Be (Crypto)

ciphertext.txtは2進数で書かれているので、デコードした後keyとXORをして復号する。試した見たところkeyのサイズ分だけ暗号の末尾だけ復号すればよいことがわかり、復号できた。最終的なコードは以下の通り。

from Crypto.Util.number import *

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

enc = long_to_bytes(int(enc, 2))

with open('key.txt', 'r') as f:
    key = f.read().rstrip()

len_pt = len(enc) - len(key)

flag = enc[:len_pt]
for i in range(len(key)):
    flag += chr(ord(enc[i+len_pt]) ^ ord(key[i]))
print flag
UMDCTF-{w3lc0m3_t0_crypt0}

Computer Troubles (Crypto)

base85文字列と推測し、デコードすると、hexエンコードされた文字列になった。以降、hexデコード、base64デコード、hexデコードを行うとフラグになる。

#!/use/bin/python3
import base64
import binascii

enc = "1hAO$2)R0D2.8<u2)-m@1hAL#1h8Bs1hAL#2)-mB1h8LT2)-mC2*!R'2D[-D1hAUS1bgdA1hAL#2*!TT1hAUS2).$u1h8OR2)[@'1hARR2I\Hu1h8OR2)[@'1hAUS1c[L'1h8OR2*!TT1hARR2)-mC1hJU%2Dd=$"
msg = base64.a85decode(enc)
print('[+] base85 decode:', msg)
msg = binascii.unhexlify(msg)
print('[+] hex decode:', msg)
msg = base64.b64decode(msg)
print('[+] base64 decode:', msg)
msg = binascii.unhexlify(msg)
print('[+] hex decode:', msg)

実行結果は以下の通り。

[+] base85 decode: b'4e5455305a4451304e444d314e4451324d6d5133596a63314e7a41324e4459784e7a517a4d7a566d4e6a6b334d7a566d4e7a497a4d7a59784e6a51334f54646b'
[+] hex decode: b'NTU0ZDQ0NDM1NDQ2MmQ3Yjc1NzA2NDYxNzQzMzVmNjk3MzVmNzIzMzYxNjQ3OTdk'
[+] base64 decode: b'554d444354462d7b7570646174335f69735f72336164797d'
[+] hex decode: b'UMDCTF-{updat3_is_r3ady}'
UMDCTF-{updat3_is_r3ady}

Card Obsession? (Crypto)

DTMFらしき音がが入っている。wavに変換してhttp://dialabc.com/sound/detect/でtoneを出力する。

886322283334443666685554445533222277737777
||>
ガラケーのキーパッドの入力になっているので、意味が通るように適宜連続する数字を切って文字にする。
>||
8863222833344436666685554445533222277737777
U MDC  TF  I  DO  N TL  I  K E C  AR  DS
UMDCTF-{I_DONT_LIKE_CARDS}

Cards Galore (Crypto)

トランプが並んでいるが、フラグの形式から考えると、各カードが1対1でアルファベットと対応していると推測できる。アルファベットは26文字、トランプは1つのマークにつき13枚。色で分けて対応付けをすることを考える。

黒08 赤09 赤02 黒02 赤12 黒07
赤07 黒03 黒06
黒07 黒03 黒06 黒08 赤10 黒02 赤08
黒01 黒13
赤04 赤02 黒06 赤05 黒07

以下の法則でアルファベットにする。

ABCDEFGHIJKLM 黒(クラブ、スペード)
NOPQRSTUVWXYZ 赤(ハート、ダイヤ)
         1111
1234567890123

アルファベットにした結果は以下の通り。

HVOBYG
TCF
GCFHWBU
AM
QOFRG

https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 14:
THANKS
FOR
SORTING
MY
CARDS
UMDCTF-{THANKS_FOR_SORTING_MY_CARDS}

Subway (Crypto)

アルファベットはa~j以外はシフト3、さらにシフト後a~jは0~9に、数字は以下の対応で復号する。

0123456789
ABCDEFGHIJ
import string

ct = 'W74 5o06 8v XP32W5-{qdw_0_vepsog_vx1vwewxwedq_w7ev_wepg}'

pt = ''
for c in ct:
    if c in string.lowercase:
        index = string.lowercase.index(c) - 3
        if index < 0:
            index += 26
        if index < 10:
            pt += str(index)
        else:
            pt += string.lowercase[index]
    elif c in string.uppercase:
        index = string.uppercase.index(c) - 3
        if index < 0:
            index += 26
        if index < 10:
            pt += str(index)
        else:
            pt += string.uppercase[index]
    elif c in string.digits:
        pt += string.uppercase[int(c)]
    else:
        pt += c

print pt
THE FlAG Is UMDCTF-{n0t_A_s1mpl3_suBst1tut10n_tH1s_t1m3}

このままフラグを投入したが、ダメだったので、"{ }"の中を小文字にしたら通った。

UMDCTF-{n0t_a_s1mpl3_subst1tut10n_th1s_t1m3}