⚠️
Wrteupではなく"参加記"です。読み物としてご覧ください。書いてみたらそんなことはないかも、いやそうかも?わかんない
はじめに
一人チーム(UdagawaWhiteBears
)で出て、42位2602点でした。
内訳としては、cryptoとrevは全部解き、他ジャンルは簡単な問題をいくつか解きました。
普通にwriteup書いてもな〜(なんで?)となったので、今回はどのような流れでcryptoを解いていったのか流れを書いていこうと思います。 なんでこんなことをやりだしたかというと、ptr-yudaiが書いたTSG LIVE ! 6 CTFのWriteupが面白かったからです。 あとは、数式を書くのが面倒だからです(ぶっちゃけ)。
作問者writeupも出ていますし、SECCON Beginnersなので日本人の参加者は多いと思うので、詳細は他の人が書いてくれるでしょうと信じています。 あと、全然理解せずにこいつ解いてるんだな〜みたいな様子が伝わればいいなと思います😇
開始前
16:00から開始だと思ってたら14:00からで、ちょっと焦った記憶があります(遅刻はしてないけど)。 pwnerと違ってcrytpoでは特に準備することがありません。 わくわくしておけば💯だと思います。
真面目な話をすると、先述のptr-yudaiの記事ではpythonとsageをまず実行しておくと次に起動するときに早くなるらしいですが、全然知らなかったしやってないです。 まあ誤差でしょう()
戦略的にはcrypto全部解きたいな〜ぐらいに考えていて、beginner向けなので、難しい問題から手をつけていこうと思っていました。
はじまりはじまり〜🎉
14:00! はじまり〜〜、わくわくしすぎてワクワクさんになりました。
早速、問題に取り掛かろうと思うのですが、ファイルのダウンロードができません😢 問題サーバーは生きてるので、ncする問題(Imaginary)があったのでこれを見てみることにしました(難しい問題からやる?そんなことはとうの昔に忘れました)。
[crypto] Imaginary
接続した感じと問題文で、わぁ〜なんか虚数じゃん、ツラと思います。
$ nc imaginary.quals.beginners.seccon.jp 1337
Welcome to Secret IMAGINARY NUMBER Store!
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
>
べそをかいていると、ファイルのダウンロードができるようになりました。 さすがLMT、30秒もかかってないんじゃないでしょうか(適当)。
ファイルを見ていきます。
うえ〜〜、100行以上あるじゃん、ぴえんです。
CTFというのはフラグと呼ばれる文字列を見つけるゲームなので、とりあえずCtrl + s
でflag
を探してみます(emacs user)。
def _secret(self):
if '1337i' in self.numbers:
self.request.sendall(b'Congratulations!\n')
self.request.sendall(f'The flag is {flag}\n'.encode())
いますね。
self.numbers
に1337i
が含まれていればOKっぽいです。
あとは_secret(self)
を呼んでいる箇所を探します。
Ctrl + s
で(略)
elif num == 5:
self._secret()
???、おまっ?どこから湧いてきたんや!?
失礼、近所の関西弁のおじさんが出てきてしまいましたが、気にせずにいきましょう。
接続した際にはいなかった、選択肢がありましたね。
とりあえず、この辺でflag
を得る道筋がすこし見えてきました。
今わかっている限りでは、self.numbers
に1337i
をsave
とかで含めて、5
を呼べば良さそうです。
とりあえず、self.numbers
に値を入れたいので、_save()
を見に行きます。
def _save(self):
try:
self.request.sendall(b'Real part> ')
re = int(self.request.recv(128).strip())
self.request.sendall(b'Imaginary part> ')
im = int(self.request.recv(128).strip())
name = f'{re} + {im}i'
self.numbers[name] = [re, im]
except ValueError:
pass
なんのcheckも入っていません。
てか、self.numbers
が配列かな〜と勝手に推測していたのですが、dictです。
え?これ入れるだけでは?と思って試してみます。
$ nc imaginary.quals.beginners.seccon.jp 1337
Welcome to Secret IMAGINARY NUMBER Store!
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 1
Real part> 0
Imaginary part> 1337
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 2
--------------------------------------------------
0 + 1337i: (0, 1337)
--------------------------------------------------
う、なるほど、0
が入るのね〜とわかります。
ここで、これpythonで hoge in dict
って、比較されるのkey?value?と思って確認します。
$ python3
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = {"x": 1}
>>> "x" in a
True
>>> 1 in a
False
>>>
御意。
さっきやった実験では、0 + 1337i
がkeyに入っているので、これではダメそうです。
まだ、_save()
を見ただけなので、もう一つ値を入れれそうな_import()
を見ます。
def _import(self):
self.request.sendall(b'Exported String> ')
data = self.request.recv(1024).strip().decode()
enc = bytes.fromhex(data)
cipher = AES.new(key, AES.MODE_ECB)
plaintext = unpad(cipher.decrypt(enc), AES.block_size)
self.numbers = json.loads(plaintext.decode())
self.request.sendall(b'Imported.\n')
self._show()
AES
の文字がちらつきます。
cryptoになってきました。
虚数から数学問の可能性がありましたが、そうではなさそうです。
与えた文字列をAES
のECB
モードで復号してから、self.number
にjson.dump
したものを代入しています。
ECB
モードなので、まぁ、なんかできるでしょう!という気持ちになります。
残るは_export
を見ていきます(_show()
には申し訳ないが、スルーさせてもらう)。
def _export(self):
cipher = AES.new(key, AES.MODE_ECB)
dump = pad(json.dumps(self.numbers).encode(), AES.block_size)
self.request.sendall(dump + b'\n')
enc = cipher.encrypt(dump)
self.request.sendall(b'Exported:\n')
self.request.sendall(enc.hex().encode() + b'\n')
こちらも、特に変わったことはしてなくて、self.number
を文字列にして暗号化したものがもらえます。
ここで、ECB
モードなので、“いい感じ"にブロックを分けて作ってから、がっちゃんこすればOKみたいな方針が立ちます。ECB
モードだと、基本的には同じ平文は同じ暗号文になることを使いがちムーブをしていきます。
さて、どうやるといい感じになるかな〜と手元で試してみます。
from json import dumps
def gen_blocks(s):
return [s[i:i+16] for i in range(0, len(s), 16)]
x = {"-100 + 1000i": [-100, 1000], "0 + 1337i": [0, 1337]}
s = dumps(x)
blocks = gen_blocks(s)
for block in blocks:
print(block)
$ python3 test.py
{"-100 + 1000i":
[-100, 1000], "
0 + 1337i": [0,
1337]}
なんで、マイナスとかつけちゃってるの?という気もしますが、つけたい気分だったんでしょう。 わかりません。
...], "
と1337i":...
をがっちゃんこすれば...], "1337i":...
となり、良さそうですね。
あとは、1337i":...
から始まるブロックを調整して作って、後ろの2ブロックを使えば、念願のフラグゲットです。
save
で、{"-100 + 1000i": [-100, 1000], "0 + 1337i": [0, 1337]}
となるように値を入れます。
これを、export
して前の2ブロックを使います。
一度、接続を切ってから、次はx = {"10 + 1000i": [10, 1000], "0 + 1337i": [0, 1337]}
となるようにsave
していきます。
また、export
して次は後ろの2ブロックを使って、前の2ブロックと今回の後ろ2ブロックをつなげたhex値をimport
します。
これで隠し選択肢5
を選択すれば、フラグゲットです🚩
ctf4b{yeah_you_are_a_member_of_imaginary_number_club}
たぶん、30分前後で解けたと思います。 記憶が正しければfirst bloodを頂いていたはずですが、今回のスコアサーバーでは解いた問題の情報が見れないので真相は闇の中…
ここまで書いてきましたが、普通にWriteupを書くより大変ということに気づいて泣いています。 解法の詳細は他の人に譲って、考えてることを軽く書こうとしてたら、試行錯誤付きのwriteupになってしまって大変疲労しています。 ここからは、解法の詳細は他の人、と復唱しながら書いていきます。
[crypto] p-8RSA
気を取り直して、次こそは一番むずかしい問題に取り組みます。
ファイルを落としてきます。 問題名からRSAであることは自明ですが、簡単な問題であることを祈ります🙏
ばっと見た感じ暗号化は普通のRSAで、素数の生成が特殊なようです。
q
を生成した後、p
の生成はq
から8を引いていって素数になるまで回してそうです。
お!これはp
とq
の差がそこまでなさそうという気持ちになるので、fermat法とかで素因数分解できそうです。
fermat法のスクリプトもってねーと思ったので、google先生にsage fermat factorization
と聞いてみると、いい感じのコードが乗っているサイトを見つけました。
コピって、ペってします。
yoshiking@yoshiking-vm:~/ctf/seccon_begginers/p-8RSA$ sage
┌────────────────────────────────────────────────────────────────────┐
│ SageMath version 9.2, Release Date: 2020-10-24 │
│ Using Python 3.8.5. Type "help()" for help. │
└────────────────────────────────────────────────────────────────────┘
sage: n = 1692217701880003415077640053307690427052236117123084244791201925961363188187081357161572555509365632685003108528944898394703205166453173384730181508859979770089258399395605909244353802395195544
....: 75266121835753044660177349444503693993991253475530436734034224314165897550185719665717183285653938232013807360458249
....: e = 17
....: c = 1002331319313602783327343416523045558140944872521511317352860746165554027951907976470018896694722907709258390131313562125744552746904221132780155717506533655129986694531619553020085990299191012
....: 44702933443124944274359143831492874463245444294673660944786888148517110942002726017336219552279179125115273728023902
....:
sage: def fermatfactor(N):
....: if N <= 0: return [N]
....: if is_even(N): return [2,N/2]
....: a = ceil(sqrt(N))
....: while not is_square(a^2-N):
....: a = a + 1
....: b = sqrt(a^2-N)
....: return [a - b,a + b]
....:
sage: fermatfactor(n)
[13008526826201356667891590694678121516071641430494347349438757349219893000439927852950504383765791466428599814640460028507882213264934492728368742844727189,
13008526826201356667891590694678121516071641430494347349438757349219893000439927852950504383765791466428599814640460028507882213264934492728368742844741541]
わいわい
できてそうですね。
あとは、復号するだけじゃん〜〜と思っていたんですが、素数生成時にp
が素数になるまでと他にも条件がありました…
if isPrime(p) and GCD(phi, e) != 1:
break
GCD(phi, e) != 1
はツラ
といっても、よくあることなのでがんばります。
片方の素数は $(p-1)\nmid e$だったので、p
でニセd
作って復号できんじゃね?と思ったら、しっかりとパディングされていました。ぴえん。
flag = flag.encode("utf-8") + urandom(64)
のこる手札は、GCD(phi, e)
で割ってやる方法ですが、これもGCD(phi, e) = 17
なので、ぴえんこえてぱおん。
もう手札0だよ〜〜ぴえんぴえんとしていると、ある記事を思い出します(bookmarkにあった)。
中国語の記事だけど、よくまとまってると思います。 https://0xdktb.top/2020/02/28/Summary-of-Crypto-in-CTF-RSA/
ここに乗ってなかったかな〜と思うと、ありました!! ばんざい🙌
ここでもコピってペっとすればOKと言いたいところですが、記事に乗っている方法は$(p-1) | e$ かつ $(q-1) | e$なので少々オーバーキルです。 そのあたりのスクリプトをいい感じに修正してやると、フラグゲットです。
from binascii import unhexlify
from Crypto.Util.number import *
def rthroot(c, r, q):
c %= q
assert(isPrime(r) and (q - 1) % r == 0 and (q - 1) % (r**2) != 0)
l = ((q - 1) % (r**2)) // r
alpha = (-inverse(l, r)) % r
root = pow(c, ((1 + alpha * (q - 1) // r) // r), q)
return root
def allroot(r, q, root):
all_root = set()
all_root.add(root)
while len(all_root) < r:
new_root = root
unity = pow(getRandomRange(2, q), (q - 1) // r, q)
for i in range(r - 1):
new_root = (new_root * unity) % q
all_root.add(new_root)
return all_root
def crt(ai, mi):
a1, m1 = ai[0], mi[0]
a2, m2 = ai[1], mi[1]
return (a1 * inverse(m2, m1) * m2 + a2 * inverse(m1, m2) * m1) % (m1 * m2)
def decrypt(proot, qroot, p, q):
count = 0
total = len(proot) * len(qroot)
t1 = inverse(q, p)
t2 = inverse(p, q)
for i in proot:
for j in qroot:
count += 1
root = (i, j)
m = crt(root, (p, q))
m = (i * t1 * q + j * t2 * p) % (p * q)
flag = long_to_bytes(m)
if flag.startswith(b"ctf"):
print('\n', flag)
def main():
p = 13008526826201356667891590694678121516071641430494347349438757349219893000439927852950504383765791466428599814640460028507882213264934492728368742844727189
q = 13008526826201356667891590694678121516071641430494347349438757349219893000439927852950504383765791466428599814640460028507882213264934492728368742844741541
n = 169221770188000341507764005330769042705223611712308424479120192596136318818708135716157255550936563268500310852894489839470320516645317338473018150885997977008925839939560590924435380239519554475266121835753044660177349444503693993991253475530436734034224314165897550185719665717183285653938232013807360458249
e = 17
c = 100233131931360278332734341652304555814094487252151131735286074616555402795190797647001889669472290770925839013131356212574455274690422113278015571750653365512998669453161955302008599029919101244702933443124944274359143831492874463245444294673660944786888148517110942002726017336219552279179125115273728023902
print('[+] Calculating e-th root...')
proot = rthroot(c, e, p)
#qroot = rthroot(c, e, q)
print('[+] Calculating all e-th roots...')
all_proot = allroot(e, p, proot)
#all_qroot = allroot(e, q, qroot)
d_ = inverse(e, q-1)
all_qroot = {pow(c, d_, q)}
print('[+] CRT cracking...')
decrypt(all_proot, all_qroot, p, q)
if __name__ == '__main__':
main()
ctf4b{4r3_y0u_up5id3_d0wn?_Fr0m_6310w?_0r_60th?}
何もわからずに解いてるが、解ければOK
想定解のカーマイケルの定理、全然頭になかったので、へ〜ってなりました。 手札が増えたので、とりあえずおけまろ水産🐟
これも30分前後で解いた気がする。 わかりません。 (書くのは詳細をだいぶ省いたのですぐ書けた、このくらいの気持ちで書いていかないと疲れちゃうよ〜、writeup書く人類全員えらすぎる)
[crypto] Field_trip
次はこれを取り組んでいきました。
配布ファイルを見た感じ、ソースコードcipher
の生成はここでやっていることがわかります。
cipher = sum([int(flag[i]) * pub_key[i] for i in range(length)])
output.txt
にはpub_key
が入っていたので、LLLでよく見るやつやんけ!!と思ってsolverを書きます(ただ、このときはまだflag[i]
が1byteだと思っている)。
flag[i]
を1bytesだと勘違いしているので、雑に書いたら全然違う値が出てきて???となりました。
てか、なんか行列のサイズでかくね??
みたいな違和感から、もうちょっと問題ファイルを見ると、
flag = bin(flag)[2:]
がありました。ぴえん。早とちり早男と申します。
完全にknapsack暗号ですね。 昔に同じような問題を解いていたので、前ブログの記事からsolverを引っ張り出してきます。 厳密には、密度によっては失敗するかもしれないですが、それでもまだ手札はあったのでとりあえずこのsolverで試してみます。
from sage.all import *
from binascii import unhexlify
exec(open("output.txt").read())
pk = pub_key
c = cipher
def create_matrix(c, pk):
n = len(pk)
i = matrix.identity(n) * 2
last_col = [-1]*n
first_row = []
for p in pk:
first_row.append(p)
first_row.append(-c)
m = matrix(ZZ, 1, n+1, first_row)
m = 1000 * m
bottom = i.augment(matrix(ZZ, n, 1, last_col))
m = m.stack(bottom)
return m
def find_short_vector(matrix):
for col in matrix.columns():
if col[0] != 0:
continue
if is_short_vector(col):
return col
def is_short_vector(vector):
for v in vector:
if v != 1 and v != -1 and v != 0:
return False
return True
m = create_matrix(c, pk)
lllm = m.transpose().LLL().transpose()
short_vector = find_short_vector(lllm)
solution_vector = []
for v in short_vector:
if v == 1:
solution_vector.append(1)
elif v == -1:
solution_vector.append(0)
m = "".join(map(str, solution_vector))
print(unhexlify(hex(int(m, 2))[2:]))
.py
“でsageを書くのにハマってる時代&sageに外部パッケージを入れる方法を知らない時代のスクリプトで懐かしくなりました。
今はもう外部パッケージも使えますし、sageは.sage
で書きますが、気にせずに実行します。
$ sage knapsack.sage
b'ctf4b{Y35!_I_ju5t_n33d3d_th353_num63r5!}'
💪 🚩 💪
20分くらい?で解いた?わかんない
ここまで、解ければ後は消化試合という感じですね。 ラストスパートやっていきましょう。
[crypto] GFM
なんか行列がちゃがちゃしてるな〜という所感
key
とenc
はもらえてるっぽいので、これ普通に逆行列計算してやればM
でるな〜とわかります。
あとは、M
をいろいろやっているところを見ていきます。
M = copy(MS.zero())
for i in range(SIZE):
for j in range(SIZE):
n = i * SIZE + j
if n < len(FLAG):
M[i, j] = FLAG[n]
else:
M[i, j] = GF(p).random_element()
M
行列の要素の初めの方はflag
が1文字づつはいってることがわかります。
key
も逆行列を持つことが保証されているので、解けそう。
sageにいれたら、簡単に解けるでしょ〜〜と思ったら、output.txt
の出力が空白くぎりで必殺技"コピってペ"ができないです。
これは試合終了かと思われましたが、yoshiking選手、なんとか持ちこたえて修正していきました。
p = 331941721759386740446055265418196301559
SIZE = 8
MS = MatrixSpace(GF(p), SIZE)
key = [[116401981595413622233973439379928029316,198484395131713718904460590157431383741,210254590341158275155666088591861364763,63363928577909853981431532626692827712,85569529885869484584091358025414174710,149985744539791485007500878301645174953,257210132141810272397357205004383952828,184416684170101286497942970370929735721], [42252147300048722312776731465252376713,199389697784043521236349156255232274966,310381139154247583447362894923363190365,275829263070032604189578502497555966953,292320824376999192958281274988868304895,324921185626193898653263976562484937554,22686717162639254526255826052697393472,214359781769812072321753087702746129144], [211396100900282889480535670184972456058,210886344415694355400093466459574370742,186128182857385981551625460291114850318,13624871690241067814493032554025486106,255739890982289281987567847525614569368,134368979399364142708704178059411420318,277933069920652939075272826105665044075,61427573037868265485473537350981407215], [282725280056297471271813862105110111601, 183133899330619127259299349651040866360, 275965964963191627114681536924910494932,290264213613308908413657414549659883232,140491946080825343356483570739103790896,115945320124815235263392576250349309769,240154953119196334314982419578825033800, 33183533431462037262108359622963646719], [53797381941014407784987148858765520206, 136359308345749561387923094784792612816, 26225195574024986849888325702082920826,262047729451988373970843409716956598743,170482654414447157611638420335396499834,270894666257247100850080625998081047879, 91361079178051929124422796293638533509, 34320536938591553179352522156012709152], [266361407811039627958670918210300057324, 40603082064365173791090924799619398850, 253357188908081828561984991424432114534, 322939245175391203579369607678957356656, 63315415224740483660852444003806482951, 224451355249970249493628425010262408466, 80574507596932581147177946123110074284, 135660472191299636620089835364724566497],[147031054061160640084051220440591645233, 286143152686211719101923153591621514114, 330366815640573974797084150543488528130, 144943808947651161283902116225593922999, 205798118501774672701619077143286382731, 317326656225121941341827388220018201533, 14319175936916841467976601008623679266, 112709661623759566156255015500851204670],[306746575224464214911885995766809188593, 35156534122767743923667417474200538878, 35608800809152761271316580867239668942, 259728427797578488375863755690441758142, 29823482469997458858051644485250558639, 137507773879704381525141121774823729991, 29893063272339035080311541822496817623, 292327683738678589950939775184752636265]]
enc = [[133156758362160693874249080602263044484, 293052519705504374237314478781574255411, 72149359944851514746901936133544542235, 56884023532130350649269153560305458687, 67693140194970657150958369664873936730, 227562364727203645742246559359263307899, 98490363636066788474326997841084979092, 323336812987530088571937131837711189774],[244725074927901230757605861090949184139, 63515536426726760809658259528128105864, 297175420762447340692787685976316634653, 279269959863745528135624660183844601533, 203893759503830977666718848163034645395, 163047775389856094351865609811169485260, 103694284536703795013187648629904551283, 322381436721457334707426033205713602738],[ 17450567396702585206498315474651164931, 105594468721844292976534833206893170749, 10757192948155933023940228740097574294, 132150825033376621961227714966632294973, 329990437240515073537637876706291805678, 57236499879418458740541896196911064438, 265417446675313880790999752931267955356, 73326674854571685938542290353559382428],[270340230065315856318168332917483593198, 217815152309418487303753027816544751231, 55738850736330060752843300854983855505, 236064119692146789532532278818003671413, 104963107909414684818161043267471013832, 234439803801976616706759524848279829319, 173296466130000392237506831379251781235, 34841816336429947760241770816424911200],[140341979141710030301381984850572416509, 248997512418753861458272855046627447638, 58382380514192982462591686716543036965, 188097853050327328682574670122723990784, 125356457137904871005571726686232857387, 55692122688357412528950240580072267902, 21322427002782861702906398261504812439, 97855599554699774346719832323235463339],[298368319184145017709393597751160602769, 311011298046021018241748692366798498529, 165888963658945943429480232453040964455, 240099237723525827201004876223575456211, 306939673050020405511805882694537774846, 7035607106089764511604627683661079229, 198278981512146990284619915272219052007, 255750707476361671578970680702422436637],[ 45315424384273600868106606292238082349, 22526147579041711876519945055798051695, 15778025992115319312591851693766890019, 318446611756066795522259881812628512448, 269954638404267367913546070681612869355, 205423708248276366495211174184786418791, 92563824983279921050396256326760929563, 209843107530597179583072730783030298674],[ 662653811932836620608984350667151180, 304181885849319274230319044357612000272, 280045476178732891877948766225904840517, 216340293591880460916317821948025035163, 79726526647684009633247003110463447210, 36010610538790393011235704307570914178, 284067290617158853279270464803256026349, 45816877317461535723616457939953776625]]
C = matrix(enc)
K = matrix(key)
inv_K = K.inverse()
M = inv_K * C * inv_K
print([bytes([i%p]) for i in list(M)[0]])
flag = b""
for row in list(M):
for x in list(row):
x = x % p
if 0 <= x <= 255:
flag += bytes([x])
else:
flag += b"?"
print(flag)
ctf4b{d1d_y0u_pl4y_w1th_m4tr1x_4nd_g4l0is_f1eld?}
出力形式がぴえんこえてぱおん🐘
[crypto] Logical_SEESAW
複数回、同じ鍵でflag
とandを取っていることがわかります。
andなので、flag
がもともの0
の箇所は鍵が何であれ0
になります。
また、flag
が1
の場合は、鍵が0
の場合は出力が2通りになり、鍵が1
の場合は確率によらす1
となります。
これを使えば、解けそうです (なんか普通のwriteupになってきましたね、非常に疲れてきました、あと簡単な問題は試行錯誤もないので…許してください…)
import collections
exec(open("output.txt").read())
length = len(cipher[0])
flag = ""
for i in range(length):
bits = [c[i] for c in cipher]
cnt = collections.Counter(bits)
if len(cnt) == 2:
flag += "1"
else:
flag += cnt.most_common()[0][0]
flag = int(flag, 2)
print(bytes.fromhex(hex(flag)[2:]))
ctf4b{Sh3_54w_4_SEESAW,_5h3_54id_50}
[crypto] simple_RSA
のこりcrypto一問です、最後の有終の美を飾るのはsimpleなRSA問題でした。
ビット長からわかるように、flag
をe
乗してもn
より大きくなっていなさそうです。
square rootを取れば終わりです!
OK!sagemath!
$ sage
┌────────────────────────────────────────────────────────────────────┐
│ SageMath version 9.2, Release Date: 2020-10-24 │
│ Using Python 3.8.5. Type "help()" for help. │
└────────────────────────────────────────────────────────────────────┘
sage: n = 1768667184240039357473051203420012852133691956973597279167660505628677847323071842695850887894263158470481734230495929306050761407480055367057903339967904133486315690203093489519767754314220211
....: 07816294944514533513969621373774114778994925558309827014496925615941751626235809874531513284088501164540581623702737363560683196485671055124528937368669392002970716029942882582952317511179914081605
....: 69998347640357251625243671483903597718500241970108698224998200840245865354411520826506950733058870602392209113565367230443261205476636664049066621093558272244061778795051583920491406620090704660526
....: 753969180791952189324046618283
....: e = 3
....: c = 2137917515300171115086910841683630246868780573379713198802569241853937371507043427250428414885473159259719603892304533323193718760929680325131490239762871586989902516402983608765893308108131992
....: 60879441426084508864252450551111064068694725939412142626401778628362399359107132506177231354040057205570428678822068599327926328920350319336256613
....:
output.txt
からパラメータペっとして、nth_root()
を使えば…
sage: m = c.nth_root(e)
sage: bytes.fromhex(hex(m)[2:])
b"ctf4b{0,1,10,11...It's_so_annoying.___I'm_done}"
うお〜〜〜〜終わった!!!!!
RTA: 約2h00m00s
正確なタイムはわかりませんが、16:00にはツイートしてたので、2時間かからないぐらいでしょうか。
たぶんこの記事を書くほうが時間かかっています。 完全にImaginaryで燃え尽きていますね。 なんでこんな記事書いてるんだろって何度も思いました。
おわりに
やっべ〜〜、魔女のお茶会に遅刻する〜〜〜