TUCTF 2018 Writeup

この大会は2018/11/24 8:00(JST)~2018/11/26 8:00(JST)に開催されました。
今回もチームで参戦。結果は 7219点で764チーム中65位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome (Welcome)

Discordの#welcomeチャネルにフラグが書いてあった。

TUCTF{my_f1r57_fl46}

Mrs. White's <br> Messy Maids (Web)

ソースのコメントに以下が書いてある。

<!-- I might kill if I could find him. Stupid Mr. /Boddy -->

http://18.218.152.56/Boddy/にアクセスすると、フラグが書いてあった。

TUCTF{1_4ccu53_Mr5._Wh173_w17h_7h3_c4ndl3571ck_1n_7h3_c0mm3n75}

Literal (Misc)

$ curl http://18.222.124.7
<html>

<head>
	<meta http-equiv="refresh" content="0; URL='Literal.html'" />
</head>

</html>
$ curl http://18.222.124.7/Literal.html
<html>
  <head>
  <meta http-equiv="Refresh" content="1; url=https://en.wikipedia.org/wiki/Fork_bomb">
  </head>
  <body>
  <!--
        *   *                     f   f   f
      *  ** *                   ff  ff  ff
      * * ** ||                ff  ff  ff
    **   ||||T||              fUffffffff
      *   |C|||T| oooooooooooo fFff
           |||||||{ooooooooooRfff3o
          ooo4ooooooooooooooLff.ooooo0
        oooNooooooooooooooo3ooooooo5ooo.
        oooo4oooooRoooooooooo3oooooooooo.
        oooDooooo4oooooNooooooooooooooooo
        ooooooooooooooGoooooooooooooooooo
        ooooooooooooooooooooo3oooooooooRo
         oooooooo0oooooooooooooooooooooo
          oooooooffUoooooooooooooooooo
            ooofff5ooooooooooooooooo
             fff }ooooooooooooooo
            fff
  -->
  Redirecting to Wikipedia...!
  </body>
</html>

英大文字、数字、'{', '}', '.'を抜き出す。

import string
comment = '''
        *   *                     f   f   f
      *  ** *                   ff  ff  ff
      * * ** ||                ff  ff  ff
    **   ||||T||              fUffffffff
      *   |C|||T| oooooooooooo fFff
           |||||||{ooooooooooRfff3o
          ooo4ooooooooooooooLff.ooooo0
        oooNooooooooooooooo3ooooooo5ooo.
        oooo4oooooRoooooooooo3oooooooooo.
        oooDooooo4oooooNooooooooooooooooo
        ooooooooooooooGoooooooooooooooooo
        ooooooooooooooooooooo3oooooooooRo
         oooooooo0oooooooooooooooooooooo
          oooooooffUoooooooooooooooooo
            ooofff5ooooooooooooooooo
             fff }ooooooooooooooo
            fff
'''

chars = string.uppercase + string.digits + '{}.'

flag = ''
for c in comment:
    if c in chars:
        flag += c

print flag
TUCTF{R34L.0N35.4R3.D4NG3R0U5}

RSAyyyy (Crypto)

RSAの基本的な計算をして答えていく。

import socket
import re
from Crypto.Util.number import *
import sympy

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(('3.16.57.250', 12345))

## Level 1 ##
data = recvuntil(s, 'n?\n').strip()
print data
pattern = 'p = (.+)\nq = (.+)\n'
m = re.search(pattern, data)
p = int(m.group(1))
q = int(m.group(2))
n = p * q
print n
s.sendall(str(n) + '\n')

## Level 2 ##
data = recvuntil(s, 'm?\n').strip()
print data
pattern = 'message = \"(.+)\"'
m = re.search(pattern, data)
message = m.group(1)
m = bytes_to_long(message)
print m
s.sendall(str(m) + '\n')

## Level 3 ##
data = recvuntil(s, 'n?\n').strip()
print data
pattern = 'p = (.+)\nq = (.+)\n'
m = re.search(pattern, data)
p = int(m.group(1))
q = int(m.group(2))
n = p * q
print n
s.sendall(str(n) + '\n')

data = recvuntil(s, 'c?\n').strip()
print data
pattern = 'e = (.+)\nm = (.+)\n'
m = re.search(pattern, data)
e = int(m.group(1))
m = int(m.group(2))
c = pow(m, e, p * q)
print c
s.sendall(str(c) + '\n')

## Level 4 ##
data = recvuntil(s, '(n)?\n').strip()
print data
pattern = 'p = (.+)\nq = (.+)\n'
m = re.search(pattern, data)
p = int(m.group(1))
q = int(m.group(2))
phi = (p - 1) * (q - 1)
print phi
s.sendall(str(phi) + '\n')

data = recvuntil(s, 'd?\n').strip()
print data
pattern = 'e = (.+)\n'
m = re.search(pattern, data)
e = int(m.group(1))
d = inverse(e, phi)
print d
s.sendall(str(d) + '\n')

## Level 5 ##
data = recvuntil(s, 'p?\n').strip()
print data
pattern = 'n = (.+)\n'
m = re.search(pattern, data)
n = int(m.group(1))
fac = sympy.factorint(n)
p = fac.keys()[0]
print p
s.sendall(str(p) + '\n')

data = recvuntil(s, 'q?\n').strip()
print data
q = n / p
print q
s.sendall(str(q) + '\n')

## Level 6 ##
data = recvuntil(s, 'p?\n').strip()
print data
pattern = 'c = (.+)\nn = (.+)\ne = (.+)\n'
m = re.search(pattern, data)
c = int(m.group(1))
n = int(m.group(2))
e = int(m.group(3))
fac = sympy.factorint(n)
p = fac.keys()[0]
print p
s.sendall(str(p) + '\n')

data = recvuntil(s, 'q?\n').strip()
print data
q = n / p
print q
s.sendall(str(q) + '\n')

data = recvuntil(s, '(n)?\n').strip()
print data
phi = (p - 1) * (q - 1)
print phi
s.sendall(str(phi) + '\n')

data = recvuntil(s, 'd?\n').strip()
print data
d = inverse(e, phi)
print d
s.sendall(str(d) + '\n')

data = recvuntil(s, 'm?\n').strip()
print data
m = pow(c, d, n)
print m
s.sendall(str(m) + '\n')

data = s.recv(2048)
print data

実行結果は以下のとおり。

$ python solve.py
               AAA               YYYYYYY       YYYYYYYYYYYYYY       YYYYYYYYYYYYYY       YYYYYYY
              A:::A              Y:::::Y       Y:::::YY:::::Y       Y:::::YY:::::Y       Y:::::Y
             A:::::A             Y:::::Y       Y:::::YY:::::Y       Y:::::YY:::::Y       Y:::::Y
            A:::::::A            Y::::::Y     Y::::::YY::::::Y     Y::::::YY::::::Y     Y::::::Y
           A:::::::::A           YYY:::::Y   Y:::::YYYYYY:::::Y   Y:::::YYYYYY:::::Y   Y:::::YYY
          A:::::A:::::A             Y:::::Y Y:::::Y      Y:::::Y Y:::::Y      Y:::::Y Y:::::Y
         A:::::A A:::::A             Y:::::Y:::::Y        Y:::::Y:::::Y        Y:::::Y:::::Y
        A:::::A   A:::::A             Y:::::::::Y          Y:::::::::Y          Y:::::::::Y
       A:::::A     A:::::A             Y:::::::Y            Y:::::::Y            Y:::::::Y
      A:::::AAAAAAAAA:::::A             Y:::::Y              Y:::::Y              Y:::::Y
     A:::::::::::::::::::::A            Y:::::Y              Y:::::Y              Y:::::Y
    A:::::AAAAAAAAAAAAA:::::A           Y:::::Y              Y:::::Y              Y:::::Y
   A:::::A             A:::::A          Y:::::Y              Y:::::Y              Y:::::Y
  A:::::A               A:::::A      YYYY:::::YYYY        YYYY:::::YYYY        YYYY:::::YYYY
 A:::::A                 A:::::A     Y:::::::::::Y        Y:::::::::::Y        Y:::::::::::Y
AAAAAAA                   AAAAAAA    YYYYYYYYYYYYY        YYYYYYYYYYYYY        YYYYYYYYYYYYY

This challenge is designed to act as an introduction to RSA.
If you have a team member that is not already familiar with RSA,
then give this challenge to them.

For the first level, I recommend looking at
https://simple.wikipedia.org/wiki/RSA_algorithm
but any description of the RSA algorithm will do.

Later levels will probably require further research.

Let's get started!



Level 1: Calculating n

p = 4069127677
q = 2789804453
What is n?
11352070513120145681
Whoop whoop!

Congratulations! You beat Level 1!

In order to calculate the ciphertext,
the message needs to be converted to an integer.


Level 2: Calculating m

message = "epidemiology ruminant posy provident condemnatory"
What is m?
3996904368156142538059084847112340597446843790219386982825830622566068551003043414749611727228669241263353749935452793
Yeah! You do that RSA!

Congratulations! You beat Level 2!

Now, we are going to actually calculate ciphertext.


Level 3: Calculating c

p = 2443367911
q = 2356648727
What is n?
5758159877050799297
Whoop whoop!

e = 65537
m = 113667960107631
What is c?
1146535438296311460
Nice job!

Congratulations! You beat Level 3!

In order for RSA to be asymmetrical,
the private exponent, d, needs to be calculated.


Level 4: Calculating d

p = 3288422173
q = 4282201841
What is tot(n)?
14081687475635196480
Ayyyyy

e = 65537
What is d?
4110390793122988673
Way to go!

Congratulations! You beat Level 4!

The easiest way to break RSA is factoring n, if it is possible.


Level 5: Factoring n

n = 6158577329169411137
What is p?
2316690071
Nice job!

What is q?
2658351847
Yeah! You do that RSA!

Congratulations! You beat Level 5!

Now, let's put everything together and break RSA!


Level 6: Breaking simple RSA

c = 5755984274299477791
n = 13316707131108976583
e = 65537
What is p?
3710062907
Way to go!

What is q?
3589348069
Yeah! You do that RSA!

What is tot(n)?
13316707123809565608
Way to go!

What is d?
4877462163356955809
Nice job!

Finally, what is m?
418548049262

That was adequate.

Congratulations! You beat Level 6!


Congratulations on finishing this introduction to RSA!
I hope this was fun and informative.

Here's your flag:
TUCTF{RSA_1$_R34LLY_C00L_4ND_1MP0RT4NT_CRYPT0}
TUCTF{RSA_1$_R34LLY_C00L_4ND_1MP0RT4NT_CRYPT0}

AESential Lesson (Crypto)

$ nc 18.218.238.95 12345

Lol. You think you can steal my flag?
I'll even encrypt your input for you,
but you can't get my secrets!

Enter your text here: 11
Here's your encrypted text:
61537177fe7ceb9cc7eb9e57c64fc9a5492544386adac7645752ed14e253dc501c2da954187caa36ed893ca14c05e767ae1c6abac2df0bbffea785521c616e54

フラグを1文字ずつはみ出させ、ブルートフォースでブロック単位で暗号が一致するものを求めていく。

0123456789abcdef
1111111111111111
111111111111111A
1111111111111111
111111111111111F
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFP

※F: フラグ(32文字)
import socket

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(('18.218.238.95', 12345))

flag = ''
for i in range(32):
    for code in range(32, 127):
        plain = '#' * (31 - i) + flag + chr(code) + '#' * (31 - i)
        data = recvuntil(s, 'here: ')
        print data + plain
        s.sendall(plain + '\n')
        data = recvuntil(s, '\n')
        cipher = recvuntil(s, '\n').strip()
        print data + cipher
        if cipher[32:64] == cipher[96:128]:
            flag += chr(code)
            break

print flag
TUCTF{A3S_3CB_1S_VULN3R4BL3!!!!}

Jimmy's Crypto (Crypto)

2つのファイルが同じXOR鍵で暗号化されている。https://github.com/SpiderLabs/cribdragのツールを使って、英単語を推測しながら復号していく。
まず、2つの暗号ファイルのXORを16進数で取得する。

def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

with open('secret', 'rb') as f:
    enc_secret = f.read()

with open('flag', 'rb') as f:
    enc_flag = f.read()

xor_str = str_xor(enc_secret, enc_flag)
print xor_str.encode('hex')

'secret' や'this'などよく出てきそうな英単語から推測していく。フラグより後は復号していないが、最終的には以下の結果。

>cribdrag.py 2a1106060001125604001100061c45155512010317475a310b50461c1743060d1d5d00370a0d5511540d2030223866163d103d313c2736472c7a687a39734d22467f2a413c7f2a5f3d343d26547f32406d4737244073355d2b56515e0f00010c5200031415411a06184f532765
                                               :
Your message is currently:
0       This is a secret file. Top secret. Don't
40       steal my secrets. If you are looking at
80       this file,__________________
Your key is currently:
0       ~you have been authorized for this secre
40      t~TUCTF{D0NT_US3_TH3_S4M3_K3Y_F0R_TH3_S4
80      M3_M3SS4G3}__________________
TUCTF{D0NT_US3_TH3_S4M3_K3Y_F0R_TH3_S4M3_M3SS4G3}