Crypto CTF 2020 Writeup

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

Welcome

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

CCTF{Crypt0Graphy_is_C00l_lets_s33_its_beauty}

Amsterdam

添付のコードの処理概要は以下の通り。

■comb(n, k)
・k = min(k, n - k)
・u = n * (n-1) * ... * (n-k+1)
・d = 1 * 2 * ... * k
・u//d(=組合せの数)を返す。

■encrypt(msg, n, k)
・m = ['1'] + ['0' for i in range(n - 1)]
・i = 1~nについて、comb(n - i, k)とmsgの比較の結果により計算
・3進数として見てそれにより計算

以上の処理から逆算する。

from Crypto.Util.number import *
from functools import reduce
import operator

def comb(n, k):
    if k > n :
        return 0
    k = min(k, n - k)
    u = reduce(operator.mul, range(n, n - k, -1), 1)
    d = reduce(operator.mul, range(1, k + 1), 1)
    return u // d

def get_m(n):
    z = []
    r = n
    while True:
        z.insert(0, r % 3)
        r = r // 3
        if r == 0:
            break
    m = 0
    for v in z:
        if v == 1:
            m += 1
        elif v == 2:
            m -= 1
        m *= 2
    return m // 2

with open('output.txt', 'r') as f:
    enc = int(f.read().rstrip().split(' = ')[1])

m = get_m(enc)
m = list(bin(m)[2:])
n = len(m)

msg = 0
k = 0
for i in range(n, 1, -1):
    if m[i-1] == '1':
        k += 1
        msg += comb(n - i, k)

flag = long_to_bytes(msg)
print flag

実行結果は以下の通り。

..:: CCTF{With_Re3p3ct_for_Sch4lkwijk_dec3nt_Encoding!} ::..
CCTF{With_Re3p3ct_for_Sch4lkwijk_dec3nt_Encoding!}

Gambler

$ nc 05.cr.yp.toc.tf 33371
Please submit a printable string X, such that md5(X)[-6:] = 436d29 and len(X) = 15
aaaaaaaaaaa3b c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Hi, there is a strong relation between philosophy and the gambling!  +
+ Gamble as an ancient philosopher and find the flag :)                +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| Options:
|       [C]ipher flag!
|       [E]ncryption function!
|       [T]ry the encryption
|       [Q]uit
E
def encrypt(m, p, a, b):
    assert m < p and isPrime(p)
    return (m ** 3 + a * m + b) % p

| Options:
|       [C]ipher flag!
|       [E]ncryption function!
|       [T]ry the encryption
|       [Q]uit
C
| encrypt(bytes_to_long(flag)) = 4339765736830935414327965338053684580043454821290341525216211716629855521006492403426648253523243969172076764249860030760412141126792833762698294650295993
| Options:
|       [C]ipher flag!
|       [E]ncryption function!
|       [T]ry the encryption
|       [Q]uit
T
| please enter your message to encrypt:
0
| encrypt(0) = 1089301495525915394959217783050238720360823156044460562942045915433765226363143224942220585780797051983913183133330187520635392023778046748500434599782334

この暗号化の処理から以下のようになることがわかる。

m=0
c = b % p = b

m=1
c = (a + b + 1) % p

m=2
c = (a*2 + b + 8) % p

差分を計算し、aを数回求め、正負の値が出るようにする。両方ともmod pでは同じ値を示すので、以下の式が成り立つ。

p = 正の値 - 負の値

あとはsageでmodulo p上の三次方程式を解けば、フラグが求められる。

#!/usr/bin/env sage
import socket
import itertools
import re
import string
import hashlib
from Crypto.Util.number import *

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(('05.cr.yp.toc.tf', 33371))

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

pattern = 'that (.+)\(X\)\[-6:\] = (.+) and len\(X\) = (.+)'
m = re.search(pattern, data)
alg = m.group(1)
h = m.group(2)
l = int(m.group(3))

for c in itertools.product(string.letters + string.digits
    + '!#$%&()+-*=.,:+[]{}', repeat=4):
    X = 'a' * (l - 4) + ''.join(c)
    if alg == 'md5':
        try_h = hashlib.md5(X).hexdigest()
    elif alg == 'sha1':
        try_h = hashlib.sha1(X).hexdigest()
    elif alg == 'sha224':
        try_h = hashlib.sha224(X).hexdigest()
    elif alg == 'sha256':
        try_h = hashlib.sha256(X).hexdigest()
    elif alg == 'sha384':
        try_h = hashlib.sha384(X).hexdigest()
    elif alg == 'sha512':
        try_h = hashlib.sha512(X).hexdigest()
    if try_h[-6:] == h:
        print X
        s.sendall(X + '\n')
        break

data = recvuntil(s, '[Q]uit\n').rstrip()
print data

print 'E'
s.sendall('E\n')
data = recvuntil(s, '[Q]uit\n').rstrip()
print data

print 'C'
s.sendall('C\n')
data = recvuntil(s, '[Q]uit\n').rstrip()
print data
flag_enc = int(data.split('\n')[0].split(' ')[-1])

encs = []
for i in range(8):
    print 'T'
    s.sendall('T\n')
    data = recvuntil(s, ':\n').rstrip()
    print data

    print str(i)
    s.sendall(str(i) + '\n')
    data = recvuntil(s, '[Q]uit\n').rstrip()
    print data
    enc = int(data.split('\n')[0].split(' ')[-1])
    encs.append(enc)

b = encs[0]
a01 = []
for i in range(7):
    a = encs[i+1] - encs[i] - ((i+1)**3 - i**3)
    if a not in a01:
        a01.append(a)

if a01[0] > 0:
    a = a01[0]
    p = a01[0] - a01[1]
else:
    a = a01[1]
    p = a01[1] - a01[0]

print '[+] a =', a
print '[+] b =', b
print '[+] p =', p

PR.<x> = PolynomialRing(Zmod(p))
f = x**3 + a * x + b - flag_enc
x = f.roots()

for x0 in x:
    m = x0[0]
    flag = long_to_bytes(m)
    if flag.startswith('CCTF{'):
        print flag
        break

実行結果は以下の通り。

Please submit a printable string X, such that sha256(X)[-6:] = dcec57 and len(X) = 34
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaau4:H
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Hi, there is a strong relation between philosophy and the gambling!  +
+ Gamble as an ancient philosopher and find the flag :)                +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
E
def encrypt(m, p, a, b):
    assert m < p and isPrime(p)
    return (m ** 3 + a * m + b) % p

| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
C
| encrypt(bytes_to_long(flag)) = 5859516123032408533939710338576303083349657177204288009870620113808657974777306952491312119968507733782625057335054668782484329039657233624056698649884479
| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
T
| please enter your message to encrypt:
0
| encrypt(0) = 3637655651634145666149005129567650238506907370016588111100276114666277871801383820019190490265615402427492341457234915175477949683389630837892818806483316
| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
T
| please enter your message to encrypt:
1
| encrypt(1) = 6684357881294328590827758239283935840508256629140296502574070083663474657706694077011405369969103934322873783266172317422303466082228788559258018890537923
| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
T
| please enter your message to encrypt:
2
| encrypt(2) = 9731060110954511515506511349000221442509605888264004894047864052660671443612004334003620249672592466218255225075109719669128982481067946280623218974592536
| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
T
| please enter your message to encrypt:
3
| encrypt(3) = 3044830652562186554641560518436170068234378799214462545904358445175680930299518352118531703785023105414819829718421119070044005355378749771648575733506042
| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
T
| please enter your message to encrypt:
4
| encrypt(4) = 6091532882222369479320313628152455670235728058338170937378152414172877716204828609110746583488511637310201271527358521316869521754217907493013775817560685
| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
T
| please enter your message to encrypt:
5
| encrypt(5) = 9138235111882552403999066737868741272237077317461879328851946383170074502110138866102961463192000169205582713336295923563695038153057065214378975901615352
| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
T
| please enter your message to encrypt:
6
| encrypt(6) = 2452005653490227443134115907304689897961850228412336980708440775685083988797652884217872917304430808402147317979607322964610061027367868705404332660528930
| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
T
| please enter your message to encrypt:
7
| encrypt(7) = 5498707883150410367812869017020975499963199487536045372182234744682280774702963141210087797007919340297528759788544725211435577426207026426769532744583663
| Options: 
|	[C]ipher flag! 
|	[E]ncryption function! 
|	[T]ry the encryption 
|	[Q]uit
[+] a = 3046702229660182924678753109716285602001349259123708391473793968997196785905310256992214879703488531895381441808937402246825516398839157721365200084054606
[+] b = 3637655651634145666149005129567650238506907370016588111100276114666277871801383820019190490265615402427492341457234915175477949683389630837892818806483316
[+] p = 9732931688052507885543703940280336976276576348173250739617299576482187299217796238877303425591057892698816837165626002845910493524528354230339843325141119
CCTF{__Gerolamo__Cardano_4N_itaLi4N_p0lYma7H}
CCTF{__Gerolamo__Cardano_4N_itaLi4N_p0lYma7H}

Trailing Bits

2進数コードになっているが、そのままでは文字にならない。シフトしながらデコードしてみる。

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

for j in range(8):
    try_enc = enc[j:]
    msg = ''
    for i in range(0, len(try_enc), 8):
        msg += chr(int(try_enc[i:i+8], 2))

    print '[+] shift =', j
    print msg

実行結果は以下の通り。

[+] shift = 0
H禄レXネ[ル・[剱ワ婢][ロ・[・ロロ\][卻[・Yレ]ロロ[][唸リ][ロ慷・H麓YH\ネHワ ・X[・X]Hル・喙禄曰Yレ]摸WHH 兔兔ル[・ネHルレXリン]Hレ]ロ僣ル・ロネワワレX・H麓Y\ヒ・\ルH麓Y\ネ\僣[ワロロ[[ロ・H兔兔ル[ \ネZ]\・ワ ン\・兔兔ル[・][ロ慂ン\ネ拑Kル麓ルY\ヒロ嵳
Wフロ・NZレ筆UンラワメY・モL゚B・Hロワ恙\ワロ・[俛H兢ルY[・\ルH麓Y\ネ[・H\レXン]\ネル・H[・\・Z[卻ンワ郎ルHワ・]唸ルH\ネHX]\・ル・ロロ撕[・[ロ・[・Y劔\兌・\ワレYロ婪[・ネX^H僣\]兌・レ][・Hリ[YH]唸ルHワ・巍ワ麓K X^H僣\レXリ[H[\[Y[・レHロヒ\ン
[+] shift = 1
・0ケエア・キ4コ7ウ4キ37ケ6ーコ4キキ4キ1キカク:コ4キ3・キ224ウエコ0カ1キカカコキ4アーコ4キキ9・*42・0カイ・ケ ・・7ケ:6ーキ:2ーコ・ウ14キ0ケ<・4ウエコ-舒・42・4コ92ク92ケイキ:9・・7ウエアーカ9コ0コ2・エコ47キ2・ウ:;キ・7ケケエア62・0カ:イケ・*42ケイ・0カ:イケ・ケ2・キケコ1キカカキキ6<・2ク92ケイキ:2イ0ケ・エコ42ケ7ケ・1:コ7コ42ケ92ク92ケイキ:0コ4キキ9・コアエ0ケ・9:イ竜0カ9イ・<イケ旅7・列DI7ケ7キキウ30ケ2・キカカキキ*42・60ウ・ケ・。ェ#=エコッ匚:惷/オ*坎/コ'ッケ、ウ:/ヲ卆・42・キケ92ケク7キ22キ1イ・2コ;イイキ:42ケイ・0カ:イケ・キ2:42・4<ケエアーカ9コ0コ2ケ・ウ:42・キ22ケ6<エキ3・コ7ケ0ウイ・ケ22サ4アイ・ケ・・ーコ:2ケ7ウ1キキ;2キ:4キキ0キ224ウ32ケ2キ:0ケケエウキ6イキ:9・ーシ・2・ケイイ2サ2キ;エコ44キ:42・ーカイ・2サ4アイ・ケ897ウケ0カ・$コ6ーシ・2・4<ケエアーカ6<・カク62カイキ:2イ;エコ40・;キ婿コ
[+] shift = 2
 basic unit of information in computing and digital communications. The name is a portmanteau of binary digit.[1] The bit represents a logical state with one of two possible values. These values are most commonly represented as either 0or1, but other representations such as true/false, yes/no, +/竏・ or on/off are common.
The flag is CCTF{it5_3n0u9h_jU5T_tO_sH1ft_M3}
The correspondence between these values and the physical states of the underlying storage or device is a matter of convention, and different assignments may be used even within the same device or program. It may be physically implemented with a two-st
[+] shift = 3
@トツ贅ニ@・メ錙゙フ@メワブ蒂ツ靨゙ワ@メワ@ニ゙レ瑕靨ワホ@ツワネ@ネメホメ霙リ@ニ゙レレ・メニツ靨゙ワ觸@ィミハ@ワツレハ@メ襦ツ@玻蒻レツワ靆ツ鵝゙フ@トメワツ蔗@ネメホメ鐔カbコ@ィミハ@トメ錙萍琅ハ賁ワ韆@ツ@リ゙ホメニツリ@跖ツ靆@迤靤@゙ワハ@゙フ@韶゙@玻跏メトリハ@・リ・觸@ィミハ賁@・リ・襦ツ萍@レ゙跖@ニ゙レレ゙ワリ萍琅ハ賁ワ靆ネ@ツ襦ハメ靤ハ隋`゙臙X@ト・@゙靤ハ隋萍琅ハ賁ワ霙靨゙ワ襦跛ニミ@ツ襦鞴・^フツリ賁X@訖ヷX@V_ナ$X@゙隋゙ワ^゙フフ@ツ萍@ニ゙レレ゙ワ\ィミハ@フリツホ@メ襦・ィ公メ鑠セfワ`靡ミセヤェjィセ陞セ謳bフ霎喃・ィミハ@ニ゙蒿ハ赳゙ワネハワニハ@トハ韶ハハワ@靤ハ賁@・リ・襦ツワネ@靤ハ@獎メニツリ@跖ツ靆襦゙フ@靤ハ@・ネハ蒭ワホ@跖゙萃ホハ@゙隋ネハ・ニハ@メ襦ツ@レツ韋ハ隋゙フ@ニ゙ワ・ワ靨゙ワX@ツワネ@ネメフフハ萍ワ錙ツ跏メホワレハワ韆@レツトハ@・ハネ@ハ・ワ@迤靤メワ@靤ハ@貭レハ@ネハ・ニハ@゙隋琅゙ホ萃レ\@定@レツトハ@獎メニツリリメレ獵ハレハワ靆ネ@迤靤@ツ@韶゙Z跖
[+] shift = 4
♂・・戟ユケ・ミ⊃・・ケ區ノオ・・スク▼ク″スオチユム・ケ怐・吹則擂ム・″スオオユケ・劫ム・スケフク_。煤ケ・煤・フ >・スノムオ・ム腐ヤ⊃・翁ケ・艨則擂ミケlナt_。煤翁ミ∨蔽ノ箆併ムフ>・ス擂劫ー∀ム・煤ン・ム⊃ケ煤ス・ムンシ・スヘヘ・桶煤ル・ユ蔑ク_。箆煤ル・ユ蔑≦ノ煤オスヘミ″スオオスケア艨ノ蔽ノ箆併ム武≦フ&・ム。癖€チスネトー♂ユミ⊃ム。癖∨蔽ノ箆併ム・・スケフ∀ユ頃≦フ・ノユ反刔アヘ伐∝蔑スケシー€ャソ・Hー⊃ネ⊃クスス劍≦ノ煤鎖Q綋ミユ|ヘクチヤ蝪}ゥTユQ}ム=}ヘ ナ厶}4ヘ・Q。煤鎖ノノ箆チスケ装ケ鵠♂篇ン封ク・。箆煤ル・ユ蔑≦ケ吹ム。煤チ。袁・劫ー∀ム・蔑⊃・ム。煤ユケ装ノア螂ケ怐ヘムスノ・煤スネ¢弁・鵠▼フ>・・ム癖⊃・鎖ケル併ム・スクー≦ケ吹則劔碧併ミ≦ヘヘ・攣オ併ムフ・・♂煤ユヘ武&ル丙∂・ム。・ク・。煤ヘ・煤装ル・鵠⊃ネ・ノス數・ク・ミ ・・♂煤チ。袁・劫アア艨・オチア雰併ム武∂・ム>・ンシオヘム
[+] shift = 5
姙ォsK。{1Ks3{徒」K{qKq{kΝ」Ks9s!#K;K」a{kkォsK」K{s冫」C)sk)K・        ボ謄ks」+ゥ{1Ks橡#K;K。rル企」C)K。・ン+・s」・    c{;Ka屮」)サK」A{s){1」サyボ屁Kc)ウcォ+冫」C+・ウcォ+・・k{孱{kk{scノ・ン+・s」+!・+K」C+・ボ痩aォ。{」C+・・ン+・s」」K{s・岫A・」騰){3c・aヒ+几syaYD疎{・{q{{31・{kk{qpR」C)3c9K・「3ロK。ェqΛヒB炫ゥェ「裵z蕓A・「仡幄R」C){涛+寃{s#+s)+」サ++q」C+・ウcォ+・s!」C)イヒ姙a屮」+・{1」C)ォs#+田ヒKs9屮{・;){・#+ウK)K・     k」」+・{1{sウ+s」K{qas!#K33+・s。屁K;sk+s」・kノ)ォ・!+ウ+qサK」CKq」C)・k)#+ウK){・ン{;・iqK。kノ)イヒ姙ccノKkツ+k+s」+!サK」A     」サyk屮 
[+] shift = 6
&6・V譌B匁f・ヨF柳・問6WF匁r襭F没友ツ6ラV譁6F柳・・F・・ヨR・・Fヨ蹤VR&匁'・F没唯蟲メF・&唯&W&W6V蹠2ニ・ツ7FFRv友・RGv・・6・ニRfヌVW2・F・6RfヌVW2&Rヨ・B6ヨヌ・&W&W6V蹤VB2V友・"・ツ'WB・"&W&W6V蹤F柳・7V6・2G'VRヌ6Rツ妨2イ(・ツ・fb&R6ヨ爭F・fニr・45Dgカ佑U・・S妹SUE4・gE7ミ・F・6・&W7FV・R&WGvVV・F・6RfヌVW2襭F・∠6・ツ7FFW2F・V襷W&ヌ末誡7F・vR・FWf・R・ヨGFW"6fV蹤柳篦襭F貿fW&V蹌76没贍V蹠2ヨ・&RW6VBWfV・v友・・F・6ヨRFWf・R・&&メ・唯ヨ・&R ∠6・ニヌ・儲ニVヨV蹤VBv友・Gv7F
[+] shift = 7
-トm縈ョ・フ・-フ・・・・・m縨ョュヘ,l.・腠eト
・M-フ.O$・・・f+、
?m,M研フ-汐ョeト・$
遣ャュホ軒・・・$種螳n・ 、、l-ャ、鍵ヘ,l、ァ-曻ヲェ駒雅靖,ホ矩ヲo。J・、m琮Lョ

shift=2の時に復号できている。

CCTF{it5_3n0u9h_jU5T_tO_sH1ft_M3}