好奇心の足跡

飽きっぽくすぐ他のことをしてしまうので、忘れないため・形にして頭に残すための備忘録。

Rice Tea Cat Panda CTF writeup [Cryptography]

2020 1/21(火) 1:00 ~ 1/25(土) 16:59 JST に開催された、Rice Tea Cat Panda CTF の Cryptography ジャンルのwriteup。

writeup一覧・戦績はこちら

kusuwada.hatenablog.com

[Cryptography] HOOOOOOOOOOMEEEEEE RUNNNNNNNNNNNNN!!!!! (50pt)

AND JAKE IS ROUNDING THE BASES HE PASSES BASE 32!!! HE ROUNDS BASE 64!!!!!!! WE'RE WITNESSING A MIRACLE!!!!!!!!!!!!!

Just one more base to go ;D

Hints

Ecbf1HZ_kd8jR5K?[";(7;aJp?[4>J?Slk3<+n'pF]W^,F>._lB/=r

問題文を見ると、base32, base64のあとに「もう一つのbase」とあります。知らないので base32, base64でググってみると、候補にbase85というのが出てきました。記号が増えているので、きっとbaseの数は増える方向だと当たりをつけていたので有力そう。

pythonにも関数があるそうなので、これを使ってdecodeしてみました。

import base64

enc = b"""Ecbf1HZ_kd8jR5K?[";(7;aJp?[4>J?Slk3<+n'pF]W^,F>._lB/=r"""
print(base64.a85decode(enc))

実行結果

$ python solve.py 
b"rtcp{uH_JAk3_w3REn't_y0u_4t_Th3_uWust0r4g3}"

[Cryptography] Hump's Day (1050pt)

Happy Hump's Day Happy TGIF! Happy Saturday Happy Tuesday +1 Happy Thursday +1 Happy TGIF -2 Happy Monday! Happy Hump's day +5 x3 Happy Saturday +3 Happy TGIF +1 Happy Tuesday +0 Happy Friday +3 Happy TGIF -1 Happy Wednesday Happy TGIF Happy Sunday Happy Saturday -5

Hints

flag is entered in the format rtcp{tis_i_the_frenchiest_fry}

ゆっくりめの時間にとき始めましたが、50ptなのにまだ誰も解いていない…!と思ったら、あとで 1050ptに変更されていました。

とりあえず曜日に関連のあるもののようだ。TGIFThank God, It's Friday !の略なので、金曜日のことだと思われる。Hump's Dayはぐぐってみるとコブの日ということで週の真ん中、水曜日のことらしい。

ヒントの "tis i the frenchiest fry" については、ググってみるとそのフレーズは出てくるものの、私の英語力では意味がわからない。なんかステッカーとか出てきた

2つ目・3つ目のヒントが途中で追加されました。

Hump's day is on what day of this ctf?

The Lotus would be proud.

Decimate your enemies, gather more hexenon, string the grineer up!

simple. Isn't it?

3つ目のヒントは「Grineer」というゲームの用語のようだ。まぁよくわからない。
このCTFの Hump's day は 1/22。水曜日。これを基準に考えるのかな?

曜日に番号をつけて計算した答えや、それを hex -> string にした値を入力してみたけど、違うようだ。月曜始まり・日曜始まりなど試してみたのだけど。更に言うと、stringに直す際に、偶数じゃなくて奇数個しか文字列がなくてコレジャナイと思って諦めた。

競技終了後、下記を読み違えていたことが発覚。

Happy Hump's day +5 x3

この行は、(WED+5*3)でも(WED+ (5*3))でもなく、(WED+5)を3回、だった。
前2つは試していたものの、3回、というのは思いつかなかったなぁ。

ということで、

import binascii

alphabets = 'abcdefghijklmnopqrstuvwxyz'

MON = 1
TUE = (MON + 1) % 7
WED = (MON + 2) % 7
THU = (MON + 3) % 7
FRI = (MON + 4) % 7
SAT = (MON + 5) % 7
SUN = (MON + 6) % 7

days = [WED, FRI, SAT, TUE+1, THU+1, FRI-2, MON, WED+5, WED+5, WED+5, SAT+3, FRI+1, TUE+0, FRI+3, FRI-1, WED, FRI, SUN, SAT-5]

num = ''
for d in days:
    num += str(d)
print('joined number: ' + num)
hexlihy = hex(int(num))
print('hexlihy: ' + hexlihy)
print(binascii.unhexlify(hexlihy[2:].encode()))

※試行錯誤のあとが残っているが、alphabetsは使わなかったし、曜日の対応表の計算はもっとシンプルで良い。

実行結果

$ python solve.py 
joined number: 3563531888962843501
hexlihy: 0x3174355f6368336d
b'1t5_ch3m'

flag: rtcp{1t5_ch3m}

うーん、これは悔しいけど時間を使っても解けなかっただろうなぁ。

[Cryptography] Don't Give The GIANt a COOKie (100pt)

It was just a typical day in the bakery for Delphine. She was preparing her famous chocolate cake, when all of a sudden a GIANt burst through the doors of her establishment and demanded a cookie. Being the strong-willed girl she was, Delphine refused and promptly threw her rolling pin at the GIANt. Doing what any sensible being would do when faced with projectiles, the GIANt let out a shriek and ran out of the shop. Delphine smiled to herself, it was another day well done.

But oh? What's this? It seems the GIANt dropped this behind while he was screaming and scrambling out of the shop.

69acad26c0b7fa29d2df023b4744bf07

Hints

This challenge still follows typical flag format, just wrap your answer with rtcp{answer_here}.

Non-case sensitive.

タイトルがcookieとのことなのでcookie関連を見てみたけれども違いそう。
この16進数の文字列、32文字なのでなにかしらのhashかもしれない。ということで、オンラインのhashデータベースサイトで調べてみたらヒットしました!

f:id:kusuwada:20200126112900p:plain

形式を揃えて

flag: rtcp{chocolate_mmm}

[Cryptography] 15 (100pt)

Lhzdwt eceowwl: Dhtnwt Pcln Eaao Qwoohvw

Okw qsyo okcln bah'i fslo cl baht Dhtnwt Pcln dhtnwt cy yazwalw'y eaao ehlnhy. Dho sy co ohtly aho, okso zcnko dw fkso bah nwo. S 4vksllwt hmqasiwi s mkaoa slalbzahyqb oa okw ycow ykafvsycln kcy ewwo cl s mqsyocv dcl ae qwoohvw, fcok okw yosowzwlo: "Okcy cy okw qwoohvw bah wso so Dhtnwt Pcln." Sizcoowiqb, kw ksi ykawy al. Dho okso'y wgwl fatyw.

Okw mayo fwlo qcgw so 11:38 MZ al Xhqb 16, sli s zwtw ofwlob zclhowy qsowt, okw Dhtnwt Pcln cl rhwyocal fsy sqwtowi oa okw tanhw wzmqabww. So qwsyo, C kamw kw'y tanhw. Kaf ici co ksmmwl? Fwqq, okw DP wzmqabww ksil'o twzagwi okw WJCE isos etaz okw hmqasiwi mkaoa, fkcvk yhnnwyowi okw vhqmtco fsy yazwfkwtw cl Zsbecwqi Kwcnkoy, Akca. Okcy fsy so 11:47. Oktww zclhowy qsowt so 11:50, okw Dhtnwt Pcln dtslvk siitwyy fsy mayowi fcok fcykwy ae ksmmb hlwzmqabzwlo. Ecgw zclhowy qsowt, okw lwfy yosocal fsy valosvowi db slaokwt 4vksllwt. Sli oktww zclhowy qsowt, so 11:58, s qclp fsy mayowi: DP'y "Owqq hy sdaho hy" alqclw eathz. Okw eaao mkaoa, aokwtfcyw plafl sy wjkcdco S, fsy soosvkwi. Vqwgwqsli Yvwlw Zsnsuclw valosvowi okw DP cl rhwyocal okw lwjo isb. Fkwl rhwyocalwi, okw dtwspesyo ykceo zslsnwt ysci "Ak, C plaf fka okso cy. Kw'y nwoocln ectwi." Zbyowtb yaqgwi, db 4vksl. Laf fw vsl sqq na dsvp oa wsocln aht esyo eaai cl mwsvw.

tovm{v4T3Ehq_f1oK_3J1e_i4O4}

Challenge Author: Jess (the other one)/J

キタコレ!好きなやつ。単一換字暗号。

tovm{v4T3Ehq_f1oK_3J1e_i4O4} はフラグと思われるので、tovm -> rtcpokwがたくさん出てくる、&otなので、the。あとはそれっぽい英文になるように頑張る。

出来た変換表を使ってdecryptoするスクリプトがこちら。

cipher = """Lhzdwt eceowwl: Dhtnwt Pcln Eaao Qwoohvw

Okw qsyo okcln bah'i fslo cl baht Dhtnwt Pcln dhtnwt cy yazwalw'y eaao ehlnhy. Dho sy co ohtly aho, okso zcnko dw fkso bah nwo. S 4vksllwt hmqasiwi s mkaoa slalbzahyqb oa okw ycow ykafvsycln kcy ewwo cl s mqsyocv dcl ae qwoohvw, fcok okw yosowzwlo: "Okcy cy okw qwoohvw bah wso so Dhtnwt Pcln." Sizcoowiqb, kw ksi ykawy al. Dho okso'y wgwl fatyw.

Okw mayo fwlo qcgw so 11:38 MZ al Xhqb 16, sli s zwtw ofwlob zclhowy qsowt, okw Dhtnwt Pcln cl rhwyocal fsy sqwtowi oa okw tanhw wzmqabww. So qwsyo, C kamw kw'y tanhw. Kaf ici co ksmmwl? Fwqq, okw DP wzmqabww ksil'o twzagwi okw WJCE isos etaz okw hmqasiwi mkaoa, fkcvk yhnnwyowi okw vhqmtco fsy yazwfkwtw cl Zsbecwqi Kwcnkoy, Akca. Okcy fsy so 11:47. Oktww zclhowy qsowt so 11:50, okw Dhtnwt Pcln dtslvk siitwyy fsy mayowi fcok fcykwy ae ksmmb hlwzmqabzwlo. Ecgw zclhowy qsowt, okw lwfy yosocal fsy valosvowi db slaokwt 4vksllwt. Sli oktww zclhowy qsowt, so 11:58, s qclp fsy mayowi: DP'y "Owqq hy sdaho hy" alqclw eathz. Okw eaao mkaoa, aokwtfcyw plafl sy wjkcdco S, fsy soosvkwi. Vqwgwqsli Yvwlw Zsnsuclw valosvowi okw DP cl rhwyocal okw lwjo isb. Fkwl rhwyocalwi, okw dtwspesyo ykceo zslsnwt ysci "Ak, C plaf fka okso cy. Kw'y nwoocln ectwi." Zbyowtb yaqgwi, db 4vksl. Laf fw vsl sqq na dsvp oa wsocln aht esyo eaai cl mwsvw.

tovm{v4T3Ehq_f1oK_3J1e_i4O4}"""

chr_map = {"a":"O", "b":"Y", "c":"I", "d":"B", "e":"F", "f":"W", "g":"V", "h":"U", "i":"D", "j":"X", "k":"H", "l":"N", "m":"P", "n":"G", "o":"T", "p":"K", "q":"L", "r":"Q", "s":"A", "t":"R", "u":"Z", "v":"C", "w":"E", "x":"J", "y":"S", "z":"M"}

plain = ''

for c in cipher:
    if c.isupper():
        plain += chr_map[c.lower()].upper()
    elif c.islower():
        plain += chr_map[c].lower()
    else:
        plain += c
print(plain)

実行結果

$ python solve.py 
Number fifteen: Burger King Foot Lettuce

The last thing you'd want in your Burger King burger is someone's foot fungus. But as it turns out, that might be what you get. A 4channer uploaded a photo anonymously to the site showcasing his feet in a plastic bin of lettuce, with the statement: "This is the lettuce you eat at Burger King." Admittedly, he had shoes on. But that's even worse.

The post went live at 11:38 PM on July 16, and a mere twenty minutes later, the Burger King in question was alerted to the rogue employee. At least, I hope he's rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. Three minutes later at 11:50, the Burger King branch address was posted with wishes of happy unemployment. Five minutes later, the news station was contacted by another 4channer. And three minutes later, at 11:58, a link was posted: BK's "Tell us about us" online forum. The foot photo, otherwise known as exhibit A, was attached. Cleveland Scene Magazine contacted the BK in question the next day. When questioned, the breakfast shift manager said "Oh, I know who that is. He's getting fired." Mystery solved, by 4chan. Now we can all go back to eating our fast food in peace.

rtcp{c4R3Ful_w1tH_3X1f_d4T4}

[Cryptography] notice me senpai (100pt)

uwu...senpai placed this note on my desk before class but i cant wead what it says!!!!!! can you hewp me????????? uwu tysm

tlyrc_o_0pnvhu}{137rmi__i_omwm

Challenge Author: Jess (the other one)/J

senpaiって日本語のあれやんなぁ…?tysmは "thank you so much" の略らしい。hewpは "help" の間違いかな?

rtcp{lyoonvhuietrmiiomwm_____を並べ替え}だと思うんだけど、英語の並び替えは難しい…。でもこういうのが好きでCTFやっているのもあるので、とにかくやってみる。
mが使いにくいのに3つも出てきていることから、mを使う単語にフォーカスして考えたりした。それくらい。

いろいろ試した結果、これが怪しい。まさかの展開。

im_in_love_with_your_mom

ただ、leet文字がi=1o=0なので、それぞれ3箇所試さないとだめ。そんな問題ある?と思いつつ下記パターンを試した所、correct!のところでflagが通った。

rtcp{1m_in_l0v3_wi7h_your_mom}
rtcp{im_1n_l0v3_wi7h_your_mom}
rtcp{im_in_l0v3_w17h_your_mom}
rtcp{1m_in_lov3_wi7h_y0ur_mom}
rtcp{im_1n_lov3_wi7h_y0ur_mom} // correct!
rtcp{im_in_lov3_w17h_y0ur_mom}
rtcp{1m_in_lov3_wi7h_your_m0m}
rtcp{im_1n_lov3_wi7h_your_m0m}
rtcp{im_in_lov3_w17h_your_m0m} 

とおったので良し!フラグゲット٩(๑❛ᴗ❛๑)尸

[Cryptography] IdleRPG (800pt)

Adrian's a great guy. IdleRPG is too, if only I actually played it...

Hints

Y9xwh`iXm<Vy==0x957d3d19b4d2a__dZGmZ6=j?I%Q||0o112575172146646270--ASLyRE>;9zt,==0x2123efad3594b3__n>#M`=DmchH8||0o411076765515312077--t?V5{I{gMU|U==0x14a976197c2915__;+dC,.R/G~kw||0o245227303137024262---,8f`zTVPdNt==0x93dbfd1c5928f__xt\8X*]zyGL1||0o111733772161311037--%o]c"&z9?1b+==0x147f84671e56d__lV!*DK"1p*qq||0o12177410634362362--V9ik@"E]^\;|==0xf036125564a45__vDgZ*k.>imxm||0o170066044525444727--f!h3bI`YK|x5==0x723a8f0523b99__%z<9//w@S<'%||0o71072436024435551--sP]R0lq*[N:S==0x1b05a7c82fd37a__75=~hT~Q~fA?||0o330132371013751406--gbh(YZ>+"Gs~==0xdd2ea5ce253cf__,1yS%GL?*k;G||0o156456513470451560--Y?L5Ug:EI_A&==0x962f5786b2bc8__9\_65mO4<I:R||0o113057257032625565--^&QlEKy{o=SD==0xbb0e5e66a974a__;U^"Auqq,^K\||0o135416274632513432--3{RI(9`\~O~|==0x6c1cbf73e807b__hj6Nd(1Oro7e||0o66034576717500034--)=)MXYb~oA-m==0x267abd0c92e17__S7@`w(.e)h:%||0o23172572062226746--d1$dXWtmmZ[1==0x191edd987d99db__7tBUDZ$Fp0O3||0o310755663037314567--_nzHHj1KU#/Y==0xc3018045513b0__>}UA.s+SDkW%||0o141401400425211504--WfuvK}2)@XLd==0x4810904aed9ad__o!lr_oS1JuDJ||0o44020440453554572--%`53NS?<NyzQ==0x1ffa7a900063e4__:*+KK)"7*SkV||0o377647522000061605--!rI<c/~6Zc7$==0x1185db7ab832fc__R"%xF/*^#E6f||0o214135557256031310--,_rdR4O;uAo#==0x208c01f7ff8f55__iC5nt]G\.%#M||0o404300076777707357--&z=pJ5Fs(_^w==0x1204c1acf2a0cf__P:sPjc6,-XBh||0o220114065474520133--0d.rnoZ%t@:B==0x194749d970ca7__65fGWLm!z7,(||0o14507223545606164--OxCdv)/{2+/G==0xcc26fb68a1315__W7mtQfg>6/zy||0o146046766642411303--ZBrIi^,<%U)W==0x70d920ffd960__BSILih9EK-Rz||0o3415444077754401--G[@>aOM,CLcC==0x19ded68575f483__61U8qn0}5mIJ||0o316755320535372042--|1v<xMUqbcOq==0x2f81c8def09eb__:h6YYBB%^l9l||0o27601621573604672--kTx{=gbY#O26==0x4a21a24bfe0a7__S|]Re7U}^<s0||0o45041504457760136--L2E!4Z_~y_qh==0x2091f66c71e4ad__qJA|4AEE{aqC||0o404437315434362060--

Adrian called it the "ideal solution"

SECCON2019でも出ていた、base64 + シェル芸の匂いを感じる。…けど、一筋縄ではいかなさそう。base64, 16進数, 8進数が入り混じってる気配がするけど、それに使用されないはずの記号がたくさん入っている。

これも後で配点が変わったっぽい。当初はもう少し配点が低かったような…?

ここで、16進数が出てきたときに必ず頭に==,お尻に__が付いてきていることに気づく。
同様に、パターンがありそうなので、パターンの部分で改行してみる。

Y9xwh`iXm<Vy==0x957d3d19b4d2a__dZGmZ6=j?I%Q||0o112575172146646270--
ASLyRE>;9zt,==0x2123efad3594b3__n>#M`=DmchH8||0o411076765515312077--
t?V5{I{gMU|U==0x14a976197c2915__;+dC,.R/G~kw||0o245227303137024262--
-,8f`zTVPdNt==0x93dbfd1c5928f__xt\8X*]zyGL1||0o111733772161311037--
%o]c"&z9?1b+==0x147f84671e56d__lV!*DK"1p*qq||0o12177410634362362--
V9ik@"E]^\;|==0xf036125564a45__vDgZ*k.>imxm||0o170066044525444727--
f!h3bI`YK|x5==0x723a8f0523b99__%z<9//w@S<'%||0o71072436024435551--
sP]R0lq*[N:S==0x1b05a7c82fd37a__75=~hT~Q~fA?||0o330132371013751406--
gbh(YZ>+"Gs~==0xdd2ea5ce253cf__,1yS%GL?*k;G||0o156456513470451560--
Y?L5Ug:EI_A&==0x962f5786b2bc8__9\_65mO4<I:R||0o113057257032625565--
^&QlEKy{o=SD==0xbb0e5e66a974a__;U^"Auqq,^K\||0o135416274632513432--
3{RI(9`\~O~|==0x6c1cbf73e807b__hj6Nd(1Oro7e||0o66034576717500034--
)=)MXYb~oA-m==0x267abd0c92e17__S7@`w(.e)h:%||0o23172572062226746--
d1$dXWtmmZ[1==0x191edd987d99db__7tBUDZ$Fp0O3||0o310755663037314567--
_nzHHj1KU#/Y==0xc3018045513b0__>}UA.s+SDkW%||0o141401400425211504--
WfuvK}2)@XLd==0x4810904aed9ad__o!lr_oS1JuDJ||0o44020440453554572--
%`53NS?<NyzQ==0x1ffa7a900063e4__:*+KK)"7*SkV||0o377647522000061605--
!rI<c/~6Zc7$==0x1185db7ab832fc__R"%xF/*^#E6f||0o214135557256031310--
,_rdR4O;uAo#==0x208c01f7ff8f55__iC5nt]G\.%#M||0o404300076777707357--
&z=pJ5Fs(_^w==0x1204c1acf2a0cf__P:sPjc6,-XBh||0o220114065474520133--
0d.rnoZ%t@:B==0x194749d970ca7__65fGWLm!z7,(||0o14507223545606164--
OxCdv)/{2+/G==0xcc26fb68a1315__W7mtQfg>6/zy||0o146046766642411303--
ZBrIi^,<%U)W==0x70d920ffd960__BSILih9EK-Rz||0o3415444077754401--
G[@>aOM,CLcC==0x19ded68575f483__61U8qn0}5mIJ||0o316755320535372042--
|1v<xMUqbcOq==0x2f81c8def09eb__:h6YYBB%^l9l||0o27601621573604672--
kTx{=gbY#O26==0x4a21a24bfe0a7__S|]Re7U}^<s0||0o45041504457760136--
L2E!4Z_~y_qh==0x2091f66c71e4ad__qJA|4AEE{aqC||0o404437315434362060--

[a-z,A-Z,0-9,symbols]==[hex]__[a-z,A-Z,0-9,symbols]||[oct]--
全部の行がこんな構文になっている。

hex, octの部分はわかるとして、それ以外が Base** でも他いろいろ試しても意味の有りそうな形にならない。ので、とりあえずhex, octの部分だけ抜き出してみた。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import re

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

arry = re.split('==|__|\|\||--', data)

arry_hex = []
arry_oct = []
for i in range(len(arry)//4):
    arry_hex.append(int(arry[i*4+1][2:], 16))
    arry_oct.append(int(arry[i*4+3][2:], 8))
print(arry_hex)
print(arry_oct)

実行結果

$ python solve.py 
[2629842056727850, 9328186541577395, 5815824233015573, 2601168857830031, 360606636041581, 4225840423782981, 2009533330373529, 7606042547442554, 3891078719755215, 2642081217194952, 3290726203561802, 1901931630854267, 676937593335319, 7070811512740315, 3430579430560688, 1267775640099245, 9001128586929124, 4932252307305212, 9161139338317653, 5071779457573071, 444703557749927, 3591484780188437, 124077863852384, 7281887361234051, 835751477447147, 1304133076246695, 9167686822126765]
[2629842056727736, 9328186541577279, 5815824233015474, 2601168857829919, 360606636041458, 4225840423782871, 2009533330373481, 7606042547442438, 3891078719755120, 2642081217194869, 3290726203561754, 1901931630854172, 676937593335270, 7070811512740215, 3430579430560580, 1267775640099194, 9001128586929029, 4932252307305160, 9161139338317551, 5071779457572955, 444703557749876, 3591484780188355, 124077863852289, 7281887361233954, 835751477447098, 1304133076246622, 9167686822126640]

んー。。。。ん?
どうやら同じ行の数たちは、結構近い値のようだ。

print(hex(arry_hex[i]-arry_oct[i]))

を入れて、hexとoctの値の差分を見てみると、

0x72
0x74
0x63
0x70
0x7b
0x6e
0x30
0x74
0x5f
0x53
0x30
0x5f
0x31
0x64
0x6c
0x33
0x5f
0x34
0x66
0x74
0x33
0x52
0x5f
0x61
0x31
0x49
0x7d

( ✧Д✧) カッ!! これは見慣れた 0x7d 〆!すなわち}

という事で、hexとoctの差分をchrにしてあげるとflagが出てきました。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import re

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

arry = re.split('==|__|\|\||--', data)

arry_hex = []
arry_oct = []
for i in range(len(arry)//4):
    arry_hex.append(int(arry[i*4+1][2:], 16))
    arry_oct.append(int(arry[i*4+3][2:], 8))
    print(chr(arry_hex[i]-arry_oct[i]), end='')

実行結果

$ python solve.py 
rtcp{n0t_S0_1dl3_4ft3R_a1I}

not so idle after all... どういうメッセージだろう…。
すべての情報を使うわけじゃないのでちょっと気持ち悪いし、余計な情報に対して色々余計なことをしてしまった。Forensicのほうがスッキリするなー。

[Cryptography] Wrong Way (150pt)

Did you know that you've been going the wrong way entire time?

E7Rq<G:Kǒ

Hint

Format in rtcp{} format, adding _ underscores as needed. The flag should be case insensitive

これは色々やってみたけど気づかなかった…!
競技終了後にもう一度見てみたら、似たような問題は解けてたのにショック。

base64 encodeするだけだった…。

import base64

dec = "E7Rq<G:Kǒ".encode()
enc = base64.b64encode(dec)
print(enc)

実行結果

$ python solve.py 
b'RTcPUnEXPEcTEDpLAceS'

多分やってみてたけど、問題をコピーしたときにwirteupきれいに書こうと思って文字の間に空白入れてしまい、変な出力になっていた。出だしがRTcPになっている & ヒントに case insensitive とあるので、これがflagっぽい。

flag: rtcp{UnEXPEcTED_pLAceS}

[Cryptography] That's Some Interesting Tea(rs)....... (175pt)

You know, the tears of one's enemies works lovely in tea. Turns out, there's tons of different bases for tea. In fact, I think I heard Delphine talk about this chef website she used for her tea base combinations. . .

Oh! Speaking of which, GIANt wants Delphine to make him tea. . . all he has is the tea leaves and the cup though. Maybe you can help Delphine, since she's really busy with cooking other things?

O53GG4CSJRHEWQT2GJ5HC4CGOM4VKY3SOZGECZ2YNJTXO6LROV3DIR3CK4ZEMWCDHFMTOWSXGRSHU23DLJVTS5BXOQZXMU3ONJSFKRCVO5BEGVSELJSGUNSYLI2XQ32UOI3FKWDYMJQWOMKQOJ4XIU2WN5KTKWT2INUW44SZONGUUN2BMFRTQQJYKM3WGSSUNVXGEU3THFIFUSDHIVWVEQ3LJVUXEMSXK5MXSZ3TG5JXORKTMZRFIVQ=

文字列のレンジがbase32のような感じなので、まずは base32 decode。そしたら今度はまた違うレンジの文字列が出てきたので…と繰り返し base** dedode を試していきます。
問題文から、base** のバリエーションを試す問題だと思ったので、ぐぐったりして色々試しながらやりました。

import base64
import base58
import base62

enc = b"""O53GG4CSJRHEWQT2GJ5HC4CGOM4VKY3SOZGECZ2YNJTXO6LROV3DIR3CK4ZEMWCDHFMTOWSXGRSHU23DLJVTS5BXOQZXMU3ONJSFKRCVO5BEGVSELJSGUNSYLI2XQ32UOI3FKWDYMJQWOMKQOJ4XIU2WN5KTKWT2INUW44SZONGUUN2BMFRTQQJYKM3WGSSUNVXGEU3THFIFUSDHIVWVEQ3LJVUXEMSXK5MXSZ3TG5JXORKTMZRFIVQ="""

data = base64.b32decode(enc)
data = base58.b58decode(data)
data = base62.decodebytes(data.decode('utf-8'))
data = base64.b64decode(data)
data = base64.a85decode(data)

print(data)

実行結果

$ python solve.py 
b'rtcp{th4t5_50m3_54lty_t34_1_bl4m3_4ll_th0s3_t34rs}'

padding errorは見飽きたよ…。涙でしょっぱくなったってか。

[Cryptography] I Love You 3000 (700pt)

❤️ 144, 588, 1869, 1425, 1267, 1708, 1588, 1600, 1889, 1497, 482, 696, 731, 337, 491, 1314, 437, 1514, 1384, 1561, 419, 382, 835, 325, 1835, 1562, 1092 💔

Hint

I don't read books... do/should you?

submit in the form rtcp{OHMYGAWDTHISISAWHOLEWORD}

当初200ptだったのが、競技終了時には700ptに格上げされていた。

最初見たときに「なにかの歌詞か本の○文字目の抜き出しとかかな?」と考えてちょっと調べてみたけども、手頃なものが見つからずあまり深く追っていませんでした。

競技後にDiscordに投稿されたつぶやき

I think the "I love you 3000" challenge won the Most Guessy Award

より、ゲスパーが必要な問題らしい。
ちょっとぐぐってみると、誰もが涙した "I love you 3000" の意味を泣きながら解説 | Sabot House こんなサイトが引っかかりました。アベンジャーズ

rtcp{AVEMGERSENDGAME}

単純な私はもう「これだ!」と思ってすぐフラグ投げちゃいますよね…。問題文の数列ガン無視。

Stephanie Poetri – I Love You 3000 Lyrics | Genius Lyrics

Stephanie Petri さんの I Love You 3000 という歌の歌詞からn文字目を抜き出したりしてみましたが、違いそう。

どうにもわからなかったのでギブアップ。OpenToAllチームのwriteupを見たところ、I LOVE YOUとも呼ばれるウィルスLOVELETTERソースコードがもとのテキストになるらしい。

ソースコードこちらから入手可能(2020年1月現在)。

さらに、配列は○文字目、ではなく、○単語目の先頭の文字、というふうに読むらしい。hintのbookはやはり大きなヒントだったようで、Book Cipherと言われる手法だそうだ。

Book Cipher - Dictionary - Decoder, Encoder, Solver, Translator

こちらにかけると、答えが出てくる。

f:id:kusuwada:20200130145001p:plain

flag: rtcp{I10V3H0WBROKENMY3MAILZR}

いやこれは解けないでしょ、しょうがないわー。と思ったけど、ウィルスの名前は聞いたことあったし、Book Cipherっていうのも有名な暗号っぽいので、復習してよかった。

[Cryptography] That's a Lot of Stuff . . . (275pt)

Do you want some numbers? Here, take these numbers. I don't need them anyways. I have too many numbers at home, so go on, take them. Shoves numbers towards the computer screen

36 31 20 36 31 20 36 34 20 34 30 20 36 31 20 36 31 20 36 36 20 34 30 20 37 31 20 37 31 20 34 30 20 36 31 20 36 31 20 36 32 20 34 30 20 36 31 20 36 32 20 36 33 20 34 30 20 37 31 20 37 31 20 34 30 20 36 34 20 37 30 20 34 30 20 36 31 20 36 31 20 36 30 20 34 30 20 36 31 20 36 31 20 37 30 20 34 30 20 36 35 20 36 31 20 34 30 20 36 31 20 36 31 20 36 34 20 34 30 20 36 31 20 36 31 20 36 35 20 34 30 20 36 34 20 37 31 20 34 30 20 36 34 20 37 30 20 34 30 20 36 31 20 36 31 20 36 30 20 34 30 20 36 31 20 36 31 20 36 35 20 34 30 20 37 31 20 36 35 20 34 30 20 37 31 20 36 37 20 34 30 20 36 31 20 36 31 20 36 34 20 34 30 20 36 35 20 36 31 20 34 30 20 37 31 20 36 35 20 34 30 20 36 35 20 36 32 20 34 30 20 37 31 20 36 35 20 34 30 20 37 31 20 37 31 20 34 30 20 36 34 20 37 30 20 34 30 20 36 34 20 37 30 20 34 30 20 36 37 20 36 36 20 34 30 20 37 31 20 36 35 20 34 30 20 37 31 20 37 31 20 34 30 20 36 35 20 36 32 20 34 30 20 36 31 20 36 31 20 36 36 20 34 30 20 36 31 20 36 31 20 36 35 20 34 30 20 36 31 20 36 32 20 36 35

まずは、3文字おきに20が登場していること、それ以外の数字が30番台なことから、それぞれの数字をhexとみなしてascii変換してあげると、文字量が1/3に減るぞ(ascii(0x20)はblank)と思い、やってみた。

61 61 64 40 61 61 66 40 71 71 40 61 61 62 40 61 62 63 40 71 71 40 64 70 40 61 61 60 40 61 61 70 40 65 61 40 61 61 64 40 61 61 65 40 64 71 40 64 70 40 61 61 60 40 61 61 65 40 71 65 40 71 67 40 61 61 64 40 65 61 40 71 65 40 65 62 40 71 65 40 71 71 40 64 70 40 64 70 40 67 66 40 71 65 40 71 71 40 65 62 40 61 61 66 40 61 61 65 40 61 62 65

今度は定期的に40が出てきて、それ以外の数字は60~71。眺めていると、114 116と変換すると、asciiでrt…。これはこの線で行こう。c99だから、次の71 7199に変換されて欲しい。あとは他の出力を眺めて、708と出力してほしかったので、これもエイヤで処理に入れました。

import base64

numbers = "36 31 20 36 31 20 36 34 20 34 30 20 36 31 20 36 31 20 36 36 20 34 30 20 37 31 20 37 31 20 34 30 20 36 31 20 36 31 20 36 32 20 34 30 20 36 31 20 36 32 20 36 33 20 34 30 20 37 31 20 37 31 20 34 30 20 36 34 20 37 30 20 34 30 20 36 31 20 36 31 20 36 30 20 34 30 20 36 31 20 36 31 20 37 30 20 34 30 20 36 35 20 36 31 20 34 30 20 36 31 20 36 31 20 36 34 20 34 30 20 36 31 20 36 31 20 36 35 20 34 30 20 36 34 20 37 31 20 34 30 20 36 34 20 37 30 20 34 30 20 36 31 20 36 31 20 36 30 20 34 30 20 36 31 20 36 31 20 36 35 20 34 30 20 37 31 20 36 35 20 34 30 20 37 31 20 36 37 20 34 30 20 36 31 20 36 31 20 36 34 20 34 30 20 36 35 20 36 31 20 34 30 20 37 31 20 36 35 20 34 30 20 36 35 20 36 32 20 34 30 20 37 31 20 36 35 20 34 30 20 37 31 20 37 31 20 34 30 20 36 34 20 37 30 20 34 30 20 36 34 20 37 30 20 34 30 20 36 37 20 36 36 20 34 30 20 37 31 20 36 35 20 34 30 20 37 31 20 37 31 20 34 30 20 36 35 20 36 32 20 34 30 20 36 31 20 36 31 20 36 36 20 34 30 20 36 31 20 36 31 20 36 35 20 34 30 20 36 31 20 36 32 20 36 35"

array1 = numbers.split()
reduce1 = ''
for a in array1:
    reduce1 += chr(int(a,16))
# print(reduce1)

array2 = reduce1.split()
reduce2 = ''
fragment = ''
for b in array2:
    if b == '40':
        reduce2 += chr(int(fragment))
        fragment = ''
    elif b == '71':
        fragment += str(9)
    elif b == '70':
        fragment += str(8)
    else:
        fragment += str(int(b)-60)
reduce2 += chr(int(fragment))
print(reduce2)

実行結果

$ python solve.py 
rtcp{c0nv3rs10ns_ar3_4_c00L_c4ts}

[Cryptography] Pandas Like Salads (350pt)

Did you know a new panda was added to the Washington DC zoo recently? Yep, apparently she really like salads. Interesting, yeah? Also, the panda keepers of the zoo said that the key to happiness in life is a little CUTENESS every day. You know, all the keepers who are on the panda's rotation all said the same thing to me. Very interesting.

pngが配布されます。

f:id:kusuwada:20200126112946p:plain

なんか見たことある暗号…。象形文字だと思ったら良いのかな?手書きのを起こすのは大変。
いつもこれ系の問題、そう言えば解けていないので、これは解きたい…!

と思ったけど解けなかった。

競技終了後、ピッグペン暗号 というものであることを teapot(@grncbg)さんのwriteupを見て知る。おー、いつもこれが解けなかったのか。復習大事。

解読すると、ysay{hjkahr_qqgdia_unr_kw_yrq_pm_nnfb}
rtcpのハズの部分がysay,yが二回も出てきてる。単一換字暗号かなと思って挑戦したときも、flagフォーマットの前に同じ文字が出てきて無理だと思ったんでした。

rtcp{にするには、文字をずらしてやらねばならぬ。

import binascii

alphabets = 'abcdefghijklmnopqrstuvwxyz'
enc = 'ysay{hjkahr_qqgdia_unr_kw_yrq_pm_nnfb}'
prefix = 'rtcp'

key = ''
for i in range(len(prefix)):
    key += alphabets[alphabets.index(enc[i])-alphabets.index(prefix[i])]
print(key)

実行結果

$ python solve.py 
hzyj

ということで、keyをhzyjに設定してVigenère暗号にすれば良さそう。鍵はもっと長いかもしれないけどわからないのでとりあえず4文字でやってみます。

import binascii

alphabets = 'abcdefghijklmnopqrstuvwxyz'
enc = 'ysay{hjkahr_qqgdia_unr_kw_yrq_pm_nnfb}'
prefix = 'rtcp'

key = ''
for i in range(len(prefix)):
    key += alphabets[alphabets.index(enc[i])-alphabets.index(prefix[i])]
print(key)

flag = ''
cnt = 0
for c in enc:
    if c == '{' or c == '}' or c == '_':
        flag += c
    else:
        shift = alphabets.index(key[cnt % len(key)])
        flag += alphabets[(alphabets.index(c) - shift) % 26]
        cnt += 1
print(flag)

実行結果

$ python solve.py 
hzyj
rtcp{akmras_shzekr_not_bp_zth_in_peyc}

うーん、なんか違いそうだけど投げてみた。ら、やっぱり違うらしい。
ツールを使ったらいい感じにkeyを見つけてくれるのとかありそうだけど、競技期間終わってるし、時間かかるけど楽しいのでこのままやってみます。

key長を探るために、幾つか試してみます。最初の4つはあってるはず。

for i in range(10):
    key2 = key + 'a' * i
    flag = ''
    print(str(i+4) + ': ', end='')
    cnt = 0
    for c in enc:
        if c == '{' or c == '}' or c == '_':
            flag += c
        else:
            if cnt % len(key2) == 0:
                flag += '*'
            shift = alphabets.index(key2[cnt % len(key2)])
            flag += alphabets[(alphabets.index(c) - shift) % 26]
            cnt += 1
    print(flag)

実行結果

4: *rtcp{*akmr*as_sh*zekr_*not_b*p_zth_*in_pe*yc}
5: *rtcp{h*clcyr_*jriui*t_vpi_k*p_zth_p*f_opwb}
6: *rtcp{hj*dbji_qq*zekr_un*k_ly_prq_*in_pefb}
7: *rtcp{hjk*tit_hqgd*bb_wer_kw_*rss_gm_nn*yc}
8: *rtcp{hjka*as_shgdia_*not_bw_yrq_*in_pefb}
9: *rtcp{hjkah*k_rsxdia_un*k_ly_prq_pm_n*ggd}
10: *rtcp{hjkahr_*jriuia_unr_k*p_zth_pm_nnfb}
11: *rtcp{hjkahr_q*jhfza_unr_kw_y*kr_rd_nnfb}
12: *rtcp{hjkahr_qq*zekr_unr_kw_yrq_*in_pefb}
13: *rtcp{hjkahr_qqg*wjc_lnr_kw_yrq_pm_*gohs}

この*が来たところがkeyが一周回ったところなので、*のあと4文字がそれっぽいkey長が正解だと思われる。見てみると、key長8のときが as_sh, not_b, in_pe であってる気がする。

あと4文字なので、地道に一つずつ試しながら見てみます。

5文字目を試したところ

$ python solve.py
a: *rtcp{hjka*as_shgdia_*not_bw_yrq_*in_pefb}
b: *rtcp{gjka*as_shfdia_*not_bv_yrq_*in_peeb}
c: *rtcp{fjka*as_shedia_*not_bu_yrq_*in_pedb}
d: *rtcp{ejka*as_shddia_*not_bt_yrq_*in_pecb}
e: *rtcp{djka*as_shcdia_*not_bs_yrq_*in_pebb}
f: *rtcp{cjka*as_shbdia_*not_br_yrq_*in_peab}
g: *rtcp{bjka*as_shadia_*not_bq_yrq_*in_pezb}
h: *rtcp{ajka*as_shzdia_*not_bp_yrq_*in_peyb}
i: *rtcp{zjka*as_shydia_*not_bo_yrq_*in_pexb}
j: *rtcp{yjka*as_shxdia_*not_bn_yrq_*in_pewb}
k: *rtcp{xjka*as_shwdia_*not_bm_yrq_*in_pevb}
l: *rtcp{wjka*as_shvdia_*not_bl_yrq_*in_peub}
m: *rtcp{vjka*as_shudia_*not_bk_yrq_*in_petb}
n: *rtcp{ujka*as_shtdia_*not_bj_yrq_*in_pesb}
o: *rtcp{tjka*as_shsdia_*not_bi_yrq_*in_perb}
p: *rtcp{sjka*as_shrdia_*not_bh_yrq_*in_peqb}
q: *rtcp{rjka*as_shqdia_*not_bg_yrq_*in_pepb}
r: *rtcp{qjka*as_shpdia_*not_bf_yrq_*in_peob}
s: *rtcp{pjka*as_shodia_*not_be_yrq_*in_penb}
t: *rtcp{ojka*as_shndia_*not_bd_yrq_*in_pemb}
u: *rtcp{njka*as_shmdia_*not_bc_yrq_*in_pelb}
v: *rtcp{mjka*as_shldia_*not_bb_yrq_*in_pekb}
w: *rtcp{ljka*as_shkdia_*not_ba_yrq_*in_pejb}
x: *rtcp{kjka*as_shjdia_*not_bz_yrq_*in_peib}
y: *rtcp{jjka*as_shidia_*not_by_yrq_*in_pehb}
z: *rtcp{ijka*as_shhdia_*not_bx_yrq_*in_pegb}

syが怪しいです。一旦sとして、6文字目も見てみます。

$ python solve.py
a: *rtcp{pjka*as_shodia_*not_be_yrq_*in_penb}
b: *rtcp{pika*as_shocia_*not_be_xrq_*in_pena}
c: *rtcp{phka*as_shobia_*not_be_wrq_*in_penz}
d: *rtcp{pgka*as_shoaia_*not_be_vrq_*in_peny}
e: *rtcp{pfka*as_shozia_*not_be_urq_*in_penx}
f: *rtcp{peka*as_shoyia_*not_be_trq_*in_penw}
g: *rtcp{pdka*as_shoxia_*not_be_srq_*in_penv}
h: *rtcp{pcka*as_showia_*not_be_rrq_*in_penu}
i: *rtcp{pbka*as_shovia_*not_be_qrq_*in_pent}
j: *rtcp{paka*as_shouia_*not_be_prq_*in_pens}
k: *rtcp{pzka*as_shotia_*not_be_orq_*in_penr}
l: *rtcp{pyka*as_shosia_*not_be_nrq_*in_penq}
m: *rtcp{pxka*as_shoria_*not_be_mrq_*in_penp}
n: *rtcp{pwka*as_shoqia_*not_be_lrq_*in_peno}
o: *rtcp{pvka*as_shopia_*not_be_krq_*in_penn}
p: *rtcp{puka*as_shooia_*not_be_jrq_*in_penm}
q: *rtcp{ptka*as_shonia_*not_be_irq_*in_penl}
r: *rtcp{pska*as_shomia_*not_be_hrq_*in_penk}
s: *rtcp{prka*as_sholia_*not_be_grq_*in_penj}
t: *rtcp{pqka*as_shokia_*not_be_frq_*in_peni}
u: *rtcp{ppka*as_shojia_*not_be_erq_*in_penh}
v: *rtcp{poka*as_shoiia_*not_be_drq_*in_peng}
w: *rtcp{pnka*as_shohia_*not_be_crq_*in_penf}
x: *rtcp{pmka*as_shogia_*not_be_brq_*in_pene}
y: *rtcp{plka*as_shofia_*not_be_arq_*in_pend}
z: *rtcp{pkka*as_shoeia_*not_be_zrq_*in_penc}

jかな。。。
こんな感じでやっていくと、key=hzyjsjxxのときに、下記のflagが出てきました。

x: *rtcp{pand*as_should_*not_be_put_*in_pens}

*を除いて

flag: rtcp{pandas_should_not_be_put_in_pens}

ピッグペン暗号、覚えたぞ!

[Cryptography] FBI (375pt)

Happy MLK day! (January 20th for y'alls non-American folk).

Hints

What's does the challenge name have to do with its theme?

Flag format is rtcp(...) not rtcp{}

The text you use to solve this challenge may slightly vary by a few characters, spacing etc. If these exists, correct them to make it a valid string of english words separated by -s

配布されたmessage.txtはこちら。

KING,

1 4 33
6 1 43
6 3 6
2 4 54
3 6 6
2 2 33
4 8 42
4 3 2
2 4 54
4 10 9
4 11 24
4 1 60
4 2 26
6 0 32
2 3 8
6 5 0
3 4 60
4 0 26
4 5 9
4 11 24
1 0 32
3 0 54
5 1 45
4 11 24
4 1 63
2 4 30
1 4 7
4 11 24
1 1 39
2 3 1
4 1 11
4 5 47
4 6 43
4 15 41
6 4 17
4 11 24
5 2 54
2 3 28
3 0 32
4 11 24
5 1 29
5 0 42
4 7 38
4 6 22
4 11 24
4 4 4
4 11 24
4 5 55
2 2 16
2 2 53
6 3 12
4 11 24
2 2 52
2 1 31
6 0 39
3 6 24

flagフォーマットを見る限り、3 6 6 -> (, 3 6 24 -> ) になるのかな?
ヒントのもう一つ、タイトルと問題の関連性は?FBIとキング牧師

FBIとキング牧師について調べてみると、FBI–King suicide letter - Wikipedia こんなん出てきた。
この手紙の画像、問題文と書き出しの文が一致していること、上記の 3パラグラフ目、6行目、6文字目が ( なので、この線はあってそう。パラグラフは1から数えて、行数・文字数は0からカウントすると合いそう。地道に読む。

rtcp(halpy-fifti th-mll-day-america-has-lome-a-long-way)

こんなん出てきた。
微修正してね、とのことだったので、ちょっとおかしなところを修正。

rtcp(happy-fiftieth-mlk-day-america-has-come-a-long-way)

[Cryptography] Code On (500pt)

My houseplant and I were working on a biology assignment together. Yes, my houseplant. Don't question it. Anyways, she ended up giving me a new cipher to use in my next project! So I'm giving it to my biology friends to see if they can solve it. They are, after all, studying DNA and mRNA right now.

AUGCAAGGUCUCUUGACCCAGUGGAUACUAAAUGCCUGGAAGGUAGCAUACUAG

Key: 6, 3, 4, 3, 1, 9, 8, 3, 3, 2, 7, 4, 1, 2, 4, 1

Hints

Make sure to encase the plaintext with rtcp{} Spaces are represented by a underscore, (_)

mRNA伝令RNA - Wikipedia 参照。
どうも A,C,G,Uからできている文字列のほうがmRNAに見立てられているらしい。3つ単位で意味を持つということで3文字ずつに分けてみた。

mRNA = "AUGCAAGGUCUCUUGACCCAGUGGAUACUAAAUGCCUGGAAGGUAGCAUACUAG"

tRNAs = []
for i in range(len(mRNA)//3):
    tRNAs.append(mRNA[i*3:i*3+3])

print(tRNAs)

['AUG', 'CAA', 'GGU', 'CUC', 'UUG', 'ACC', 'CAG', 'UGG', 'AUA', 'CUA', 'AAU', 'GCC', 'UGG', 'AAG', 'GUA', 'GCA', 'UAC', 'UAG']

コドン表を調べてみると、最初のAUGは開始コドン、UAGは終止コドンとのことなので、ビンゴ!あとは、上記の長さが18, keyの長さが16なので、開始と終止を除いたコドン名の key 文字目をつなげると良さそう。

対応表はこちらを見て作成。オレンジのM-RNA Codonsの列を使いました。wikipediaとかを参考にした対応表ではうまく行かなかった。

対応表

CAA: glutamine
GGU: glycine
CUC: leucine
UUG: leucine
ACC: threonine
CAG: glutamine
UGG: tryptophan
AUA: isoleucine
CUA: leucine
AAU: asparagine
GCC: alanine
UGG: tryptophan
AAG: lysine
GUA: valine
GCA: alanine
UAC: tyrosine
key = [6, 3, 4, 3, 1, 9, 8, 3, 3, 2, 7, 4, 1, 2, 4, 1]
tRNA_names = ["glutamine", "glycine", "leucine", "leucine", "threonine", "glutamine", "tryptopha", "isoleucin", "leucine", "asparagin", "alanine", "tryptopha", "lysine", "valine", "alanine", "tyrosine"]

plain = ''
for i in range(len(tRNA_names)):
    plain += tRNA_names[i][key[i]-1]
print(plain)

実行結果

$ python solve.py 
mycutehouseplant

ということで、ヒントよりflagは

rtcp{my_cute_houseplant}

[Cryptography] Directive (1500pt)

We recently got a weird communication from unknown airspace. Seems like ordnance data. We need these decrypted. Yesterday.

f:id:kusuwada:20200126113024p:plain

Hints

  • 7 [] []

  • Check the ordnance again.

Hull integrity at 31%. Boarding crew, small arms. I want those amateurs off my ship.

KEEP HULL INTEGRITY AT 32% OR HIGHER.

You can submit in rtcp{} format or the format the end flag is given in.

テキストエディタに豆腐たちをコピペしたらなんか記号が色々出てきた。ということは、そういうことだ。

最初のヒント&問題文とリンクしそうなページを発見。

https://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_0000-0FFF:titile

f:id:kusuwada:20200126113038p:plain

unicodeかなぁ?ということで、

with open('question.txt', 'rb') as f:
    data = f.read().decode()

for c in data:
    print(ord(c))

と、文字を数値化してみると、

78332
72726
73232
72229
73224
73224
73226
73228
73227
...

全部7始まりの5桁の数字になりました。これに第一のヒントを当てはめると、

7 [83] [32]
7 [27] [26]
7 [32] [32]
7 [22] [29]
7 [32] [24]
7 [32] [24]
7 [32] [26]
7 [32] [28]
7 [32] [27]
...

となりそう。この配列、何かにならないかなーと、レンジや出現頻度を調べてみます。
chrとって文字列にするには、レンジが低すぎます。が、とりあえず一回出してみると

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

with open('question.txt', 'rb') as f:
    data = f.read().decode()

arr = []
for c in data:
    num1 = str(ord(c))[1:3]
    num2 = str(ord(c))[3:5]
    arr.append(int(num1))
    arr.append(int(num2))

print('array length: ' + str(len(arr)))
print('###')
for a in arr:
    print(chr(a), end='')

実行結果

$ python solve.py 
array length: 1064
###
S         +              .-.         *        *               *             *             /              *         
  ).--.---+---.-.   .-.   `-'  .-.   `)    (           .    .-.-._.)  (     *     .-.  .-._.---/---     *   .  .-. .-.   .-.  
7 /    /   (      /  ) .-.   !   /  .   )     *      )  / ( 0 )(    )     *    ( 0 )( O )  /      *     )/   )   )./.-'_ 
2/      /     `---'/`-'  `-'  `-'-'(_.' `-'  ._____. * (_.'   `-'  `--': ._____.   `-/-'`-'   /       _____. '/   /  ( (__.'  
6  *     *      /                    *     `==='  ..-._)            `==='  -._/          *      `==='    *       `-'    E 

最初がSから始まって、Eで終わってるみたい!この線は怪しそう。
あとは空白と記号が目立ち、数回数字が出てきます。

なんかアスキーアートでflagっぽい気がする…!(最初がrtcに見えなくもない)けど、これ以上読みやすくできず、競技時間は終了。

微調整してみた(改行(10)〜空白(32)を除去)

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

with open('question.txt', 'rb') as f:
    data = f.read().decode()

arr = []
for c in data:
    num1 = str(ord(c))[1:3]
    num2 = str(ord(c))[3:5]
    if int(num1) >= 32 or int(num1) == 10:
        arr.append(int(num1))
    if int(num2) >= 32 or int(num2) == 10:
        arr.append(int(num2))

print('array length: ' + str(len(arr)))
print('###')
for a in arr:
    print(chr(a), end='')

実行結果

S           +              .-.         *          *               *             *                  /                *            
   ).--.---+---.-.   .-.   `-'  .-.   `)    (            .    .-.-._.)  (     *      .-.  .-._.---/---     *   .  .-. .-.   .-.  
7 /  *    /   (      /  ) .-.  (  !   /  .   )     *      )  / ( 0 )(    )     *    ( 0 )( O )   /        *     )/   )   )./.-'_ 
2/       /     `---'/`-'  `-'   `-'-'(_.' `-'  ._____. * (_.'   `-'  `--': ._____.   `-/-'`-'   /       _____. '/   /   ( (__.'  
6  *       *      /                      *     `==='  ..-._)                `==='  -._/          *      `==='    *       `-'    E 

これでも十分見にくいけど、なんとか読めた。*は飾りだと思って無視。

rtcp:aw_you_got_me (flagはフォーマットに従う)

[Cryptography] Oni (2699)

もう問題タイトルも問題文も崩壊しているので、下記で。

. . Ȍ̻̮̦̠̰͚̠̯̺́͌̆͌̎̀̅͠ͅņ̯̣͇͎̞̣̲̑́̃̓͛͆̿̅͜͢į̱̩̼̲͚̤̎̽́̔͗̏̀̌̄͝ . .
2699
S͊ͩ̉̍́̅ͨī̩̦͓̍͆́̍ͤͨm̐̽̋ͫ̈́ͬp̲͙ͭl͎͓̞͉̙͕̤̃̌̎e̟̰̥̝̪̫͌ ͕ͦ̋̄͌ͩsͩͫͧ̂̂͆t̥̆̌̿̿ͭ̈́̚ͅũ͕̻̐͋f̒̊́ͩ̇͌͌f̺̠̿͗̊͊,̙͎̬ͬͭͭ̐́ ̱̫͎̻̦̪̦̽ͬ̑̇͒̌̌o̰̘͓̯̭̟͙ͤ͗̄ṅ̙̰̠̗̤̖͇̀͌͛̎͊l͖̲͒̐͌̂͌̄̑y̳̝͍̥̞̜̳̓̀̑̍͗͐̒ ̬̳̹͉̻1̼̈ͯͩ̽ͨ͗0̤̱̳̠̤̮͒̈́̌͗͂̍̏ ͓͉͇̼̣̭̪͛̈́̇l͙̜̼ǎ̦̬̼͙y͕̌̃͒̅̈é̳̻̮̬̒̎ͬ̋ṟ̺͓̦̓̈͋s̮̟͑ͤ̎ͨ̈́

͚̩͗͑͗͐̎̊̋ ͎̣̦̳̤ ͚̤̪̩͎̹͚ͫ̆͌ͥ̐̓h̗̫̰͔̦è̘͇̪͍̓͑i͉̳̗̦͚͈͒ͯg̜͚͔͂̃̈ͅͅh͉͙͓̯̬̤͉̊̏ͬ̓͗t͍̋ͅ:͉̫́̄ͨͣ ̠͈̻̜͉̰͓͊̈́̈́̔͋ͬ1̲͖͍͋͗͌̓͊̚ͅ1ͨͥͭ̾2̫͔̱̩̖0͎̭̖͙̯̤̼̔̿̆͛́͛

Simple stuff only 10 layers

height 1120

と見えます。

Hint

b̺̟̞̗̬͍̯͋ͥͣ̚̚ȃ͍̭̖͋͒͂ͤp̼͔̺͚̤͕͇t̹̘̣͐̉̊̊i̝̹̫͔ͧ͗ͨ͌z̬̭̪̘̼̐ͅe̞̮͚̣̼̻̓͋̎̃ ̣̯̮͇̉̿ͣÿ͖̂̓͂͗ͭ̍o͒̔͗ṳ̩̙̏̿ͤ̏r͔̮̙͚͎͋̆̄ ̟͔̣̮͓̟̤̽ͪ̈͐p̖̩ͥ͒͌à̖͈̻̳͎̩͗͆ͪͤͅn͍̥̟͎̦͒͐̈́d̟̝̩ă͗̐ͯͪ͑̈́ ̤̄ͮͩ̑b̙̰̯̙̰̜̤͒ͮ͗̉͒̉e̺͍̝̟̜͎̽f͉̱̥͍̈́́̔ͩo̭̲̳̾̈́ͥ́ͩrͧ̽e̠͇̪̤̱̙̠͛ ͇̰͖̘̹ͧ̅̋̍͛̊f̗̙̳͐̐ͣ͂̿͒ͭa͉̙̥̰̙͒ͤͦ̌̑c̝͙͖̹ͥ̚i̱̱̯̓̔ͭn͍̘̖͉̉g̩͎̝̓ͬ̎ͧ ̱͈̙̭̠͆ͅa̤̫̱͎̼̞͕ ̥ͪ̔ͦd̽̄ͩ͑̾e͎̽̑̄ͭͬm̫̩̹͓̱̜͗o͕̪̬̱ͬ͒̄̇̍̿ͤn͋͐̓ͣͮ̋
````

これは

baptize your panda before facing a demon

と読める。

他、Oni.pngが配布されます。

これは [Forensics] Clean Pandas の続きの問題だそうで。
そういえば、CleanPandasにも、demonやpandaが登場していました。

pngを拡大してみると、

f:id:kusuwada:20200130145024p:plain

こんな感じで2値の画像のようです。

Clean Pandas問題の、最後の出力を思い出してみます。もともと平文の行もあったので、ちょっといじって

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from cryptography.fernet import Fernet
import pandas as pd

soap_df = pd.read_pickle('soap.pkl')
pandas_df = pd.read_pickle('pandas.pkl')

for j in range(13):
    print(str(pandas_df.columns[j]) + ': ', end='')
    if pandas_df.columns[j] == 'unknown step 890348':
        for i in range(4):
            print(pandas_df['unknown step 890348'][i], end='')
            continue
    for i in range(4):
        for k in range(12):
            key = soap_df[k]
            f = Fernet(key)
            try:
                dec = f.decrypt(pandas_df[pandas_df.columns[j]][i])
                print(dec.decode(), end='')
                #print(str(k))
                break
            except:
                continue
    print()

実行結果

$ python solve.py 
Todolist: slayDemon
Side note: notUseful
unknown step: binary image
unknown step 0: binary to string
unknown step 1246523: shift by lucky number
unknown step 39865432: binary to string
unknown step 26745643547: base 32
unknown step 69821830: byteencode to integerthough bytes from hex
unknown flag 394052487124: rtcp{CAr3fu1_wH^t_y0u_c134n_696}
unknown step 446537364: split in intervals of 5
unknown step 53920324379187589: base 85
unknown step 890348: divide by the demon-summoning number
unknown step 923426390324272983: bytedecode integer

step [\d*] の表記になっているので、順番は数値の先頭の数に並べ替えます。

この通りに画像を変換していきます。これがなかなか難しい。
変換している途中に答え合わせができないので、合ってるかどうか不安。

例えば最初のバイナリ変換は、白黒を1,0にするか0,1にするかで運が試された感じだし(base32くらいで答え合わせ)、その前のshift by lucky numberも最初は絶対>> (<<) 7だとばかり思っていた(これもbase32のレンジで答えがわかった)。何となくそうだろうとは思ったけど、demon-summoning number666であること、などなど。

方法が指示されているから自力で解ける!と思ったんですけど、結局解けず。他の方のwriteup読んで解きました。
私のエスパー力では無理だったなーという点。めっちゃある。

  1. 画像がパターンの繰り返しなのは気づいていたが、imageの w=1~8 までしか使わないこと
  2. base32した時点で、途中までstring列、途中からbyte列(\xe2\x96\xa2の繰り返し)、最後にまたstring列になっていたので、途中のパターンを除去する必要があった(多分これが "binary to string")
  3. しかもそのパターンは除去ではなく、base85 decodeしたときに 0 に置き換える必要があった
import base64
from PIL import Image
from pprint import pprint
from Crypto.Util.number import long_to_bytes

img = Image.open("Oni.png")
width, height = img.size

## binary image
#  ## there's repeat pattern of 8 pixel
data = ''
for h in range(height):
    for w in range(8):  # *
        if img.getpixel((w,h))[0] == 0:
            data += '1'
        else:
            data += '0'
# print(data)

## 0: binary to string
string = ''
for i in range(len(data)//8):
    b = data[i*8:i*8+8]
    string += chr(int(b,2))
# print(string)

## 1: shift by lucky number
#  ## ord(s)'s range is 42-82
#  ## base32's range is 50-55, 65-90
#  ## so we should shift +8 to ord(s)
hist = {}
for s in string:
    if ord(s) in hist:
        hist[ord(s)] += 1
    else:
        hist[ord(s)] = 1
# pprint(hist)

shifted = ''
for s in string:
    shifted += chr(ord(s)+8)
# print(shifted)

## 2: base32
base32d = base64.b32decode(shifted)
# print(base32d)

## 3: binary to string
string2 = base32d.replace(b'\xe2\x96\xa2', b'0')
# print(string2)

## 4: split in intervals of 5
## 5: base 85
splited5 = []
for i in range(len(string2)//5):
    b = string2[i*5:i*5+5]
    if b != b'00000':
        splited5.append(base64.b85decode(b))
    else:
        splited5.append(b'0')
# print(splited5)

## 6: byteencode to integerthough bytes from hex
## 8: divide by the demon-summoning number // is 666
devided = ''
for s in splited5:
    devided += str(int(s.decode(), 16) // 666)
# print(devided)

## 9: bytedecode integer
flag = long_to_bytes(int(devided))
# print(flag)

## 10: reverse flag
rev = ''
for f in str(flag):
    rev = f + rev
print(rev)

実行結果

$ python solve.py 
'rtcp{S33mZ_1!k3_tH3_RE^1_DeM0n_h4s_c4ughT_uP_666}'b

データ、出力を観察して、怪しいところを見つけて、正しくなるように変換して、を繰り返した。時間がめっちゃかかる上に、何かしらの知識・技術が身についた気がしないので、できればこういう問題は今後避けて通りたいなぁ…。