DCTF 2021 Writeup

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

Sanity Check (Welcome 50)

トップページのSANITY CHECKのエリアにフラグが書いてあった。

dctf{welc0m3_t0_dCTF}

Dragon (Misc 100)

StegSolveで開き、Blue plane 0を見ると、フラグが書いてあった。
f:id:satou-y:20210526203344p:plain

dctf{N0w_Y0u_s3e_m3}

Don't let it run (Misc 100)

PDF Stream Dumperで開くと、Object 3にJavaScriptが書いてある。

<<
	
	/Type /Action
	/S /JavaScript
	/JS var _0x4ac9=['663aCYhYK','9qwaGGO','log','1PtCftm','1068uRYmqT','dctf{pdf_1nj3ct3d}','768577jhhsbr','717342hAzOOQ','722513PAXCbh','833989PQKiti','1447863RVcnTo','125353VtkXUG'];(function(_0x3b1f6b,_0x1ad8b7){var _0x566ee2=_0x5347;while(!![]){try{var _0x2750a5=parseInt(_0x566ee2(0x16e))+-parseInt(_0x566ee2(0x16d))+parseInt(_0x566ee2(0x16c))+-parseInt(_0x566ee2(0x173))*-parseInt(_0x566ee2(0x171))+parseInt(_0x566ee2(0x172))*-parseInt(_0x566ee2(0x16a))+parseInt(_0x566ee2(0x16f))*parseInt(_0x566ee2(0x175))+-parseInt(_0x566ee2(0x170));if(_0x2750a5===_0x1ad8b7)break;else _0x3b1f6b['push'](_0x3b1f6b['shift']());}catch(_0x5764a4){_0x3b1f6b['push'](_0x3b1f6b['shift']());}}}(_0x4ac9,0x8d97f));function _0xa(){var _0x3c6d20=_0x5347;console[_0x3c6d20(0x174)](_0x3c6d20(0x16b));}var a='bkpodntjcopsymlxeiwhonstykxsrpzy',b='exrbspqqustnzqriulizpeeexwqsofmw';_0xb(a,b);function _0x5347(_0x37de35,_0x19ac26){_0x37de35=_0x37de35-0x16a;var _0x4ac9ea=_0x4ac9[_0x37de35];return _0x4ac9ea;}function _0xb(_0x39b3ee,_0xfae543){var _0x259923=_0x39b3ee+_0xfae543;_0xa();}

>>

このスクリプトChromeデベロッパーツールのConsoleで実行する。

dctf{pdf_1nj3ct3d}

Hidden message (Misc 100)

$ zsteg fri.png 
b1,rgb,lsb,xy       .. text: "dctf{sTeg0noGr4Phy_101}"
b3,g,lsb,xy         .. text: "I@4I)$Xl"
b3,abgr,msb,xy      .. text: "v\rWv)WvM"
b4,r,lsb,xy         .. text: "\nfb@DHfBHH"
b4,r,msb,xy         .. text: "E`@Q'g3@D@tr"
b4,g,msb,xy         .. text: "ND@&B$rp"
b4,b,lsb,xy         .. text: "D\"$ \"\"\"$bN"
b4,b,msb,xy         .. text: "DDD$Fr0U3p@f"
b4,rgb,lsb,xy       .. text: "HDd(\"b(Dd\""
b4,rgb,msb,xy       .. text: "GpD@FdD#"
b4,bgr,lsb,xy       .. text: "H$b(\"dH$`"
b4,bgr,msb,xy       .. text: "t@@DFd$#"
b4,rgba,lsb,xy      .. text: "`OP/S/b/b?"
b4,abgr,msb,xy      .. text: "O@OdOdO2/"
dctf{sTeg0noGr4Phy_101}

Bell (Reverse 100)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  uint uVar2;
  time_t tVar3;
  
  tVar3 = time((time_t *)0x0);
  srand((uint)tVar3);
  iVar1 = rand();
  uVar2 = iVar1 % 5 + 8;
  printf("%d\n",(ulong)uVar2);
  process(uVar2);
  return 0;
}

undefined8 process(int param_1)

{
  long lVar1;
  bool bVar2;
  long lVar3;
  long in_FS_OFFSET;
  int local_24;
  long local_20;
  
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  bVar2 = true;
  local_24 = 1;
  while (local_24 <= param_1) {
    lVar3 = triangle(param_1,local_24,local_24);
    __isoc99_scanf();
    if (lVar3 != local_20) {
      bVar2 = false;
    }
    local_24 = local_24 + 1;
  }
  if (bVar2) {
    system("cat flag.txt");
  }
  else {
    puts("Better luck next time.");
  }
  if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

long triangle(int param_1,int param_2)

{
  long lVar1;
  long lVar2;
  
  if (param_1 < param_2) {
    lVar1 = 0;
  }
  else {
    if ((param_1 == 1) && (param_2 == 1)) {
      lVar1 = 1;
    }
    else {
      if (param_2 == 1) {
        lVar1 = triangle(param_1 + -1,param_1 + -1,param_1 + -1);
      }
      else {
        lVar2 = triangle(param_1,param_2 + -1,param_2 + -1);
        lVar1 = triangle(param_1 + -1,param_2 + -1,param_2 + -1);
        lVar1 = lVar1 + lVar2;
      }
    }
  }
  return lVar1;
}

このコードを元に、param_1回、triangle()の結果を答える。

import socket
from Crypto.Util.number import *

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

def triangle(n1, n2):
    if n1 < n2:
        lVar1 = 0
    else:
        if n1 == 1 and n2 == 1:
            lVar1 = 1
        else:
            if n2 == 1:
                lVar1 = triangle(n1 - 1, n1 - 1)
            else:
                lVar2 = triangle(n1, n2 - 1)
                lVar1 = triangle(n1 - 1, n2 - 1)
                lVar1 = lVar1 + lVar2
    return lVar1

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('dctf-chall-bell.westeurope.azurecontainer.io', 5311))

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

param1 = int(data)
for i in range(1, param1 + 1):
    lVar3 = triangle(param1, i)
    print lVar3
    s.sendall(str(lVar3) + '\n')

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

実行結果は以下の通り。

10
21147
25287
30304
36401
43833
52922
64077
77821
94828
115975
dctf{f1rst_step_t0wards_b3ll_l4bs}
dctf{f1rst_step_t0wards_b3ll_l4bs}

Tiny interpreter (Reverse 400)

binファイルをパラメータとして指定して、実行するだけ。

$ ./interpreter bin
I
n
t
e
r
p
r
e
t
e
r
_
w
r
i
t
t
e
n
_
i
n
_
C
_
i
s
_
a
_
g
r
e
a
t
_
i
d
e
a
dctf{Interpreter_written_in_C_is_a_great_idea}

Very secure website (Web 200)

ソースコードを見ると、こう書いてある。

<?php
    if (isset($_GET['username']) and isset($_GET['password'])) {
        if (hash("tiger128,4", $_GET['username']) != "51c3f5f5d8a8830bc5d8b7ebcb5717df") {
            echo "Invalid username";
        }
        else if (hash("tiger128,4", $_GET['password']) == "0e132798983807237937411964085731") {
            $flag = fopen("flag.txt", "r") or die("Cannot open file");
            echo fread($flag, filesize("flag.txt"));
            fclose($flag);
        }
        else {
            echo "Try harder";
        }
    }
    else {
        echo "Invalid parameters";
    }
?>

"51c3f5f5d8a8830bc5d8b7ebcb5717df"を検索すると、"admin"のtiger128,4ハッシュであることがわかる。
"0e132798983807237937411964085731"は該当するものが見つからないが、"0e(10進数値のみ)"となっているので、0と同等である。tiger128,4が0eから始まり、他が10進数値のものであれば、何でもよい。
調べると、https://blog.csdn.net/fastergohome/article/details/102514264 にこの手法の代表的な値が掲載されていた。
以下でログインしてみると、フラグが表示された。

Username: admin
Password: 479763000
dctf{It's_magic._I_ain't_gotta_explain_shit.}

Strong password (Crypto 100)

$ zip2john strong_password.zip > hash.txt
ver 2.0 efh 9901 strong_password.zip/lorem_ipsum.txt PKZIP Encr: cmplen=5171, decmplen=17174, crc=CEFA3672
$ john --wordlist=dict/rockyou.txt hash.txt --rules
Using default input encoding: UTF-8
Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 256/256 AVX2 8x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Bo38AkRcE600X8DbK3600 (strong_password.zip/lorem_ipsum.txt)
1g 0:00:03:06 DONE (2021-05-15 06:30) 0.005372g/s 61022p/s 61022c/s 61022C/s Bobo64..Bitchybo#1
Use the "--show" option to display all of the cracked passwords reliably
Session completed

パスワード "Bo38AkRcE600X8DbK3600" でzipを解凍すると、lorem_ipsum.txtが展開される。この長文の中にフラグが含まれていた。

dctf{r0cKyoU_f0r_tHe_w1n}

Just Take Your Time (Crypto 200)

サーバの処理概要は以下の通り。

・a * bを答える。
 →正しければ、次へ進む。
・key: UNIXTIMEを16バイト"0"パディングした文字列
・secret: ランダム16バイト(16進数文字列、長さ32)
・DES3でkey、iv="00000000"を使って、secretを暗号化して表示
・secretを正しく答えられたら、フラグが表示される。

UNIXTIMEでkeyがわかるので、復号する。

import socket
from Crypto.Cipher import DES3
from time import time

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('dctf-chall-just-take-your-time.westeurope.azurecontainer.io', 9999))

data = recvuntil(s, '> ')
print data
formula = data.split('\n')[1].split(' =')[0]
ans = eval(formula)
print ans
s.sendall(str(ans) + '\n')

key = str(int(time())).zfill(16).encode('utf-8')
print key

data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data
encrypted = data.decode('hex')

cipher = DES3.new(key, DES3.MODE_CFB, '00000000')
secret = cipher.decrypt(encrypted)
print secret
s.sendall(secret + '\n')
data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data

実行結果は以下の通り。

Show me you are worthy and solve for x! You have one second.
2080010430383378 * 7569301504130155 =
>
15744226079307314149567060563590
0000001621028971
You have proven yourself to be capable of taking on the final task. Decrypt this and the flag shall be yours!
9fb7657b8db178abe3411b0b051972b29755b6866e57cd73c224a7f5da22bdc9
1c18c21f26151b7d27b1b1e619014718
> Congratulations! Here is your flag.
dctf{1t_0n1y_t0Ok_2_d4y5...}
dctf{1t_0n1y_t0Ok_2_d4y5...}

Private Encryption Mistake (Crypto 300)

秘密鍵の見えている部分を書き出す。

-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEAupQ7hhy0AQR0LRMZgP/Kl6J3l2+U+wp1YyVB8oDYvslE3AXU
3igwX2LOYgG/JIHQ5UI2G/0Fu5iPPikh3JoUABGFyPwWsBLnohdBBtpvfRLprhbB
lsKwjZfLJIrRex+sSFkcT9zVs1VH4JfcJAbeBNK/aQdMqc1i4JQ1xsQny4ZH7TZe
CXBigK99+V05C+ENRS1uWi9ixgcbMWCCBHsTq0Kl5FIfPvVJVBr075bf7DdARSRU
Wx/FtKVMlWe/nGUTz/ezu2jOx69kd+hvtzX1JVkeY+AFi7Ldta2tNaH/8kitzoXK
JC+6A+LQXynmjQdH9RGsg7QygFjPvIcgwE8LHsMt62OKcIx5LMHlW4lvLK/EZMnr
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
ZEt6WwyEqHhPyP0CggEBAMplAvElBwRTMaT6FfWwi149Q+C1+ogaRc686CkCEs7p
zWjt4+Tg3cndxj/p2Q3Z1AzJH8h/vfZruAQHF/UFwXIAPmuzS1K0HgnNHxr355vs
AYfArpTJeyZoRttQOXvRhM+c887RWGXX278VVS5e5mh16Dn0rKpDcRnsVMahBhTg
+4XheX0zJRa3lOnoWgRLFGcJj9px4Gk7PkZnx24S2bCb7GUbisvtELkLfAvVcGIS
vvJGbeovAGpArRoaCbpnRL96N50zOWGqHeXJFljvNDvfpVAbykf+50d2VApvElQ3
/v7UHVZEfszMk3g1z+RLpgVmtltCsFvDSkDW9omfoJ0CggEBAIBfu08VPrN+B8iD
QpyO2BBUDei8fjdskpvehjWGDqzKNYDxdVcAdERtk6DSWuzpvwPNbTRm6u3v66yu
QkHn9gBlxX1sYe5P9ExqP2p+Au8hR/8s7bhVa8G53WX1Dl47QVSwbKVOWSWtQSwB
hiB9s1YqgAlhcKBWP6vFbavr3VBYY5ln/018rYvR1euDVTUVZdSMmbq3gScF4fhv
NESMd1Je7XjygbVTPJPi1PcT/SgyDRUwz0RPYIvLlA3qT9ae7s5WTp1fanv5MV6p
4LnekTQ/CVjWSorY7xdXTCMfBK1GF7WhVGG4fVSPX8QeIPKUxKkQXgKAFJrCSjj7
CLG5pPkCggEAflfmKUDTC4kfkXwoXzHxHkgialFPbszvzOmyB39q3E2pU5pFTChv
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
[                           Snipped!                           ]
-----END RSA PRIVATE KEY-----

秘密鍵は部分的に2か所がマスクされている。与えられた情報から秘密鍵を復元することができれば、フラグが得られそう。https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pemを参考にした。

RSAPrivateKey ::= SEQUENCE {
  version           Version,
  modulus           INTEGER,  -- n
  publicExponent    INTEGER,  -- e
  privateExponent   INTEGER,  -- d
  prime1            INTEGER,  -- p
  prime2            INTEGER,  -- q
  exponent1         INTEGER,  -- d mod (p-1)
  exponent2         INTEGER,  -- d mod (q-1)
  coefficient       INTEGER,  -- (inverse of q) mod p
  otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

バイナリで構成を見てみると、q, dpがわかる。さらにeは通常の65537と推測して、pを割り出す。あとはdを算出後、n, e, dから秘密鍵を生成する。

from Crypto.Util.number import *
from Crypto.PublicKey import RSA

key1 = '''
MIIJKQIBAAKCAgEAupQ7hhy0AQR0LRMZgP/Kl6J3l2+U+wp1YyVB8oDYvslE3AXU
3igwX2LOYgG/JIHQ5UI2G/0Fu5iPPikh3JoUABGFyPwWsBLnohdBBtpvfRLprhbB
lsKwjZfLJIrRex+sSFkcT9zVs1VH4JfcJAbeBNK/aQdMqc1i4JQ1xsQny4ZH7TZe
CXBigK99+V05C+ENRS1uWi9ixgcbMWCCBHsTq0Kl5FIfPvVJVBr075bf7DdARSRU
Wx/FtKVMlWe/nGUTz/ezu2jOx69kd+hvtzX1JVkeY+AFi7Ldta2tNaH/8kitzoXK
JC+6A+LQXynmjQdH9RGsg7QygFjPvIcgwE8LHsMt62OKcIx5LMHlW4lvLK/EZMnr
'''

key1 = key1.decode('base64')
hex_n_head = key1[12:].encode('hex')

key2 = '''
ZEt6WwyEqHhPyP0CggEBAMplAvElBwRTMaT6FfWwi149Q+C1+ogaRc686CkCEs7p
zWjt4+Tg3cndxj/p2Q3Z1AzJH8h/vfZruAQHF/UFwXIAPmuzS1K0HgnNHxr355vs
AYfArpTJeyZoRttQOXvRhM+c887RWGXX278VVS5e5mh16Dn0rKpDcRnsVMahBhTg
+4XheX0zJRa3lOnoWgRLFGcJj9px4Gk7PkZnx24S2bCb7GUbisvtELkLfAvVcGIS
vvJGbeovAGpArRoaCbpnRL96N50zOWGqHeXJFljvNDvfpVAbykf+50d2VApvElQ3
/v7UHVZEfszMk3g1z+RLpgVmtltCsFvDSkDW9omfoJ0CggEBAIBfu08VPrN+B8iD
QpyO2BBUDei8fjdskpvehjWGDqzKNYDxdVcAdERtk6DSWuzpvwPNbTRm6u3v66yu
QkHn9gBlxX1sYe5P9ExqP2p+Au8hR/8s7bhVa8G53WX1Dl47QVSwbKVOWSWtQSwB
hiB9s1YqgAlhcKBWP6vFbavr3VBYY5ln/018rYvR1euDVTUVZdSMmbq3gScF4fhv
NESMd1Je7XjygbVTPJPi1PcT/SgyDRUwz0RPYIvLlA3qT9ae7s5WTp1fanv5MV6p
4LnekTQ/CVjWSorY7xdXTCMfBK1GF7WhVGG4fVSPX8QeIPKUxKkQXgKAFJrCSjj7
CLG5pPkCggEAflfmKUDTC4kfkXwoXzHxHkgialFPbszvzOmyB39q3E2pU5pFTChv
'''

key2 = key2.decode('base64')
q = bytes_to_long(key2[0x000f:0x000f+0x0101])
dp = bytes_to_long(key2[0x0114:0x0114+0x0101])

e = 65537

for kp in range(3, e):
    p_mul = dp * e - 1
    if p_mul % kp == 0:
        p = (p_mul // kp) + 1
        if isPrime(p):
            break

n = p * q
assert hex(n)[2:].startswith(hex_n_head)

phi = (p - 1) * (q - 1)
d = inverse(e, phi)

key = RSA.construct(map(long, (n,e,d)))
priv_pem = key.exportKey()

with open('privkey.pem', 'w') as f:
    f.write(priv_pem)

秘密鍵生成後、内容を確認し、この鍵でssh接続する。

$ cat privkey.pem
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

$ ssh -i privkey.pem user@dctf1-chall-private-encryption-mistake.westeurope.azurecontainer.io -p 2222
The authenticity of host '[dctf1-chall-private-encryption-mistake.westeurope.azurecontainer.io]:2222 ([20.67.120.180]:2222)' can't be established.
ECDSA key fingerprint is SHA256:P5sEp/Iaie3GJcsicYtMk0gi5FZREmFhLLMh8jjPSy4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[dctf1-chall-private-encryption-mistake.westeurope.azurecontainer.io]:2222,[20.67.120.180]:2222' (ECDSA) to the list of known hosts.
Welcome to OpenSSH Server


dctf{Y0u_Are_N0w_Br34thing_M4nua11y}

Connection to dctf1-chall-private-encryption-mistake.westeurope.azurecontainer closed.
dctf{Y0u_Are_N0w_Br34thing_M4nua11y}

A Simple SP Box! (Crypto 300)

サーバの処理概要は以下の通り。

・フラグを暗号化して表示
・15回以下を繰り返し
 ・フラグを推測し、正しければ、フラグが表示される。

■暗号化
・平文の長さが奇数の場合は、"_"をパディング
・rounds = int(2 * ceil(log(len(message), 2))) 
・rounds回以下を繰り返し
 ・message = [S_box[c] for c in message]
 ・最終回以外は奇数番目グループと偶数番目グループで連結

※rounds = 2の場合
 message = [S_box[c] for c in message]
 0バイト目と1バイト目を交換
 message = [S_box[c] for c in message]

暗号化したフラグの長さからround = 12。S_boxの2回の対応表はわかるので、逆変換で6回行えば元に戻る。奇数番目と偶数番目の連結は11回行われるので、その逆変換も行えばフラグを得られる。

import socket
from string import ascii_letters, digits
from math import ceil, log

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

def inv_perm(s):
    l = len(s) / 2
    odd_s = s[:l]
    even_s = s[l:]
    d = ''
    for i in range(l):
        d += even_s[i]
        d += odd_s[i]
    return d

def inv_sub(s, inv_sbox):
    d = ''
    for c in s:
        d += inv_sbox[c]
    return d

ALPHABET = ascii_letters + digits + "_!@#$%.'\"+:;<=}{"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('dctf1-chall-sp-box.westeurope.azurecontainer.io', 8888))

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

inv_S_box = {}

for i in range(0, len(ALPHABET), 2):
    data = recvuntil(s, '> ')
    print data + ALPHABET[i] + ALPHABET[i+1]
    s.sendall(ALPHABET[i] + ALPHABET[i+1] + '\n')
    data = recvuntil(s, '\n').rstrip()
    print data
    data = recvuntil(s, '\n').rstrip()
    print data
    inv_S_box[data[1]] = ALPHABET[i]
    inv_S_box[data[0]] = ALPHABET[i+1]

rounds = int(2 * ceil(log(len(enc_flag), 2)))
inv_S_box_rounds = rounds / 2
perm_rounds = rounds - 1

tmp_flag = enc_flag
for i in range(rounds // 2):
    tmp_flag = inv_sub(tmp_flag, inv_S_box)

for i in range(rounds - 1):
    tmp_flag = inv_perm(tmp_flag)

flag = tmp_flag.rstrip('_')
print flag

実行結果は以下の通り。

Here's the flag, please decrypt it for me:
RMXXE'oosXIBc6XBXdcCXR''3cxBCX6dTM}=csRJXT
> ab
That doesn't look right, it encrypts to this:
+=
> cd
That doesn't look right, it encrypts to this:
MI
> ef
That doesn't look right, it encrypts to this:
uJ
> gh
That doesn't look right, it encrypts to this:
qX
> ij
That doesn't look right, it encrypts to this:
Ya
> kl
That doesn't look right, it encrypts to this:
jc
> mn
That doesn't look right, it encrypts to this:
vP
> op
That doesn't look right, it encrypts to this:
fz
> qr
That doesn't look right, it encrypts to this:
kB
> st
That doesn't look right, it encrypts to this:
1s
> uv
That doesn't look right, it encrypts to this:
"o
> wx
That doesn't look right, it encrypts to this:
x$
> yz
That doesn't look right, it encrypts to this:
OL
> AB
That doesn't look right, it encrypts to this:
DA
> CD
That doesn't look right, it encrypts to this:
SN
> EF
That doesn't look right, it encrypts to this:
:i
> GH
That doesn't look right, it encrypts to this:
42
> IJ
That doesn't look right, it encrypts to this:
%8
> KL
That doesn't look right, it encrypts to this:
wV
> MN
That doesn't look right, it encrypts to this:
n5
> OP
That doesn't look right, it encrypts to this:
mU
> QR
That doesn't look right, it encrypts to this:
hK
> ST
That doesn't look right, it encrypts to this:
Z<
> UV
That doesn't look right, it encrypts to this:
Hd
> WX
That doesn't look right, it encrypts to this:
9C
> YZ
That doesn't look right, it encrypts to this:
eE
> 01
That doesn't look right, it encrypts to this:
!b
> 23
That doesn't look right, it encrypts to this:
rG
> 45
That doesn't look right, it encrypts to this:
py
> 67
That doesn't look right, it encrypts to this:
tl
> 89
That doesn't look right, it encrypts to this:
'R
> _!
That doesn't look right, it encrypts to this:
g7
> @#
That doesn't look right, it encrypts to this:
_@
> $%
That doesn't look right, it encrypts to this:
Q;
> .'
That doesn't look right, it encrypts to this:
#.
> "+
That doesn't look right, it encrypts to this:
3{
> :;
That doesn't look right, it encrypts to this:
WT
> <=
That doesn't look right, it encrypts to this:
06
> }{
That doesn't look right, it encrypts to this:
F}
dctf{S0_y0u_f0und_th3_cycl3s_in_th3_s_b0x}
dctf{S0_y0u_f0und_th3_cycl3s_in_th3_s_b0x}

This one is really basic (Crypto 300)

繰り返しbase64デコードする。

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

while True:
    try:
        data = data.decode('base64')
    except:
        break

print data
dctf{Th1s_l00ks_4_lot_sm4ll3r_th4n_1t_d1d}