Paken CTF 2020 Writeup(express)

#1. はじめに
2020/10/30(金) 09:00 JST — 2020/11/1(日) 16:30 JST で、Paken CTF 2020 にTeam 「N30Z30N」としてソロ参加し、20問を解いて3310点（得点を得た96チーム中13位）でした。
当方のネットワーク環境が貧弱なせいか、途中からnetcatが繋がらなくなり心が折れましたが、最後にスマホのテザリングで奇跡的に繋がってpwnを投入できたのでヨシ！
![Score over Time.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429434/c76b03af-3022-80ba-0408-5a8cd48bf271.png)
![Category Breakdown.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429434/713462ff-8f29-057a-d405-6bc2c852b229.png)
#2. Writeup
問題数が多いので、Crypto、Forensics、OSINTのみを簡潔に記します。ちゃんと復習をしたい方は、「強い」方々が出すWriteupの方が網羅的かつ解き方も的確かと思います。こちらは息抜き的要素、つまり解いた時の紆余曲折や感想などを中心にまとめます。
なお以下、問題文や配布ファイルの改行など、省略して記載するものが多数ありますがご了承ください。
2020/11/03追記：あちこち誤記があったので直しました。

##2-1 salad (Crypto, 70pt)
>これはなんでしょう？
>sdnhqFWI{wklv_lv_wrr_hdvb}

1文字目と2文字目に着目して、p→s、a→dだから、暗号化は「ROT3」（復号は「ROT-3」）。あえて13にしないところが好きです。（逆シャアerにとって、サラダは「死亡フラグ」なので、問題名でうわっ！となったのは内緒です）
**pakenCTF{this_is_too_easy}**

##2-2 JOIushi (Crypto, 150pt)
>フラグは、得られた文字列をpakenCTF{}で括ったものです。大文字小文字は問いません。
>JOOOIOOOIJJIOOJOIJJOIJOIJOJJIJJOOOIJJIJJJIOJOJIOOOIOOIOOIJJIOJIOOJIJJJIOOOIOOOIOJI

3進法だ、よっしゃ！と早とちりしてやってみると結果はグチャグチャ。ヒントの「モー」からもすぐには思いつかず。牛でもヤギでもなくヤキが回ったな、と思いつつボンヤリしていると、そういえばどこかのCTFで似たようなのがあって、そういえばモールスだったかと思い出し、「I」が区切り文字っぽいから残り2パターン（J =｢ . ｣、O = ｢ _ ｣／J = ｢ _ ｣、O = ｢ . ｣）で試して、ちゃんと英数字に復号できた方がフラグでした。
**pakenCTF{JOIQUAL2ISCOMMINGSOON}**

##2-3 Mr.Taher (Crypto, 200pt)
code.txtとpython実行ファイル（pyc）が入ったzipが与えられます。

```code.txt
code1:4421664084074670090821428393252601896707238833568529471788988373423872134415886118801715014521273069823897226704004664676566665964855651716107694758506146136
code2:2120403903076703834514243322316105937757315062520771728791977484839802965205251266018189029958131618069233499450694892261180928293911858348401790013829165266
```
pycを動かしてみると、plaintext（整数値）を入力し、code1とcode2（いずれも整数値）を吐き出すものでした。
pycをstringsしてみると「encodeElgamal.py」なる文字列が…ということで、Elgamal暗号だと推測。
pycでplaintext=1のcode2（=c1）と、plaintext=-1のcode2（=c2）を計算し、
・p = c1 + c2
・inv = c1 の mod.p での逆元
として (code2 * inv) mod.p を計算すれば復号できてフラグゲットしました。

```solve.py
from Crypto.Util.number import *
code2 = 2120403903076703834514243322316105937757315062520771728791977484839802965205251266018189029958131618069233499450694892261180928293911858348401790013829165266

#pyc plaintext=1
c1 = 6366860587003352667857970182594845279741225533847642394435859539640594679397918612387053160915459723497617046772990720701814248533301849094642790994793719278

#pyc plaintext=-1
c2 =  497937073127257047123930616486547937528209766295663014958603919544948503999737439735506479745994831479679264618490137335307739466414794717931237296321337873

p = c1 + c2
inv = inverse(c1,p)
print(long_to_bytes(code2 * inv % p).decode())
```
**pakenCTF{7a6er_4_31GaMa1}**

##2-4 Easy RSA (Crypto, 200pt)
> RSA暗号の説明をよく読んで、復号して下さい。

p,q,e,cが与えらます。引っ掛けでは？と警戒しましたが（パ研の皆様ごめんなさい）、素直に復号処理してOKでした。

```solve.py
from Crypto.Util.number import *
p=663597153354459096027617898476434508660207945468239828363533968290549860343303991194963611564067835277286387691893307863345911157548691505664158805219149207234330939117859591878956911441813275187550714501377099458799143547032214366092699841086204610125426942286615254124579514093595429671084768474297186003099745661174217622292444183443818017038196680179603108188955806877657920484776998647269027516588077276331114296252713940928721220674431241509917747120119302462057479537399479801771964751485273214739189602556169966318505602511379515592539198582446536767097546800556112460176388463136282140197801557927644660100912470218471202526160472664029594136756994322357162994347439136981896746676413
q=918067820161400195233014948116215522540468158808393009438769891765417742510446959088791975091664090485925262195978272433064283611828265152277146953265415154532110345945025967571046135746791394148753341753207371428492202758483057023260827289865982377017938521041970887337854156027974419550990396498344585427530365747156440993214980101994780967635083251371867379581739962237632618158865589675585957466487205454157422047291104798149333161483277212779338903986533821991541659946743712292146153697690230517886693772919210077709342986691685809952888505807140027117980995045882833315789793681886502395667312205459682900298275103705757681702970118867986696862329620804448055197497074862024547984113239
e=65537
c=38354199351710970873017071426763527236369577570951766540884963469638396255709843786165430578416384474613593615737126261343441777288385243034583503237385591930366045785051108492640940553555023015318285056400665848965677481002990509650902607874319238700421563680026741373872383963458977085234568696557141054624090997762594736563425248557009539595420075215401375740665214642623472703007378182218587128611288186017943454120240565118214687143072432907277321703494648550920402665219442477302494328073583147578749167733048388539377282865954407642883592325601017525392152983292512076027105980126877070991774067907302741298034694426702586231048442685164939066384361672337838815100389428423524520669168935733390683796940943986579960070740547100530859886039104906112972841457336573067171433491899813156462622594786570655157254167892663399773510744582022508474010474059510657502176323445518552724210661151458986275356986716268225802125653818948840878989183805395487571224135152114917930310035426304467470502930914121876432269153345215152770275401284105587609136302217736126962785260853192429453980081820494213187060074652382590146062466361972547960172419999799568976214825983451063764721015235850687018639498286663282436645259484348427564704514383976971009774127274316497169162147572385361328358800102267714648011930928725822062797419747472176607528708702849608598112829398912687520625163613595201
n = p * q
phi = (p-1) * (q-1)
d = inverse(e,phi)
print(long_to_bytes(pow(c,d,n)).decode())
```
**pakenCTF{d16_y0u_un6erstand_r5a}**

##2-5 kazoeage (Cryoto, 250pt)
>RSA暗号の公開鍵と暗号文から復号してください。

```kazoeage.txt
N : 1422481401584487840622609273771371336719671044293070644033681128339473646262272075713884957746547606407977247864592463662588647490160789721258277280762741902688725339217703275603104973624174785874280562638754859328821212774250247266998496535966698042617572815493399762249902490860353118901509040616354388602127313636570885096816698317114685833542861824894553327840079524065290345505977297302441889816020208918147424158178318595664848620843168144053489899493412974603392186768969203156822319815547199829896110068255882643530837898002425921244333501728127184540032934352336892543672588491765922255533523449894597554132507697615463004202541984696593425180537883071517054041245789599037508812303669751779949
e : 65537
c : 819457590779718673723999804359306940214964366106154208329747285945801495903063364212620804847681988319861682233840623931810294581542569786222436658676401766818221814195526960465927948437849590481318537899708043934141103876057483426172112123655906550120814881589012622193244951306994617906100050056004927509856530745202322189119201790574165327379144656336588347480574752025641078240702261787744158140208499779935079535797785075501114081494087831563543081995362257044861560243556000590284155432335310387260109860886317191734199338657976691581441635488514176666088347590020313403044426738603101073711254830548335158155234090844296468266121682074813424812015566729182407520503862560165604026787361453897057
```

NをFacrotDBにぶち込んだら素因数1000000007が出てきました。この素数は相当小さいので、復号はもう一つの素数だけでできそうです（以下のsolver参照）。ちなみにこの「1000000007」という数は各所で出てくるので何かと思い調べてみると、よく競技プログラミングで使われる値のようです。

``` solve.py
from Crypto.Util.number import *
N = 1422481401584487840622609273771371336719671044293070644033681128339473646262272075713884957746547606407977247864592463662588647490160789721258277280762741902688725339217703275603104973624174785874280562638754859328821212774250247266998496535966698042617572815493399762249902490860353118901509040616354388602127313636570885096816698317114685833542861824894553327840079524065290345505977297302441889816020208918147424158178318595664848620843168144053489899493412974603392186768969203156822319815547199829896110068255882643530837898002425921244333501728127184540032934352336892543672588491765922255533523449894597554132507697615463004202541984696593425180537883071517054041245789599037508812303669751779949
e = 65537
c = 819457590779718673723999804359306940214964366106154208329747285945801495903063364212620804847681988319861682233840623931810294581542569786222436658676401766818221814195526960465927948437849590481318537899708043934141103876057483426172112123655906550120814881589012622193244951306994617906100050056004927509856530745202322189119201790574165327379144656336588347480574752025641078240702261787744158140208499779935079535797785075501114081494087831563543081995362257044861560243556000590284155432335310387260109860886317191734199338657976691581441635488514176666088347590020313403044426738603101073711254830548335158155234090844296468266121682074813424812015566729182407520503862560165604026787361453897057

p = 1000000007 # by FactorDB
q = N // p
d = inverse(e,q-1)
print(long_to_bytes(pow(c,d,q)).decode())
```
**pakenCTF{k4z0e4ge_1s_4un}**

##2-6 Coprime Crypto (Crypto, 500pt)
>---Decrypt this cryptograph---
"c1ys{4_C0_ky_}_hhaFrnn75ar5!y7p3T8aep1p"
---Examples of plain texts and codes' pair---
"Tsukukoma-Paken" "ea-mkksnkPaouuT"
＜以下略＞

Lを文字長とし、
0オリジンでindex=L-1（つまり末尾）からスタートして、以下 index=(前のindex + n) mod Lを読んでいくと復号できるという仕組み。nとLが互に素（Coprime）なら全文字を網羅できる。フラグの形(pakenCTF{…)をもとにそのようなnを探すと、n=25が該当するっぽかったので以下のsolverを作って確かめました。

```solve.py
enc = "c1ys{4_C0_ky_}_hhaFrnn75ar5!y7p3T8aep1p"
L = len(enc)
n = 25
dec = ""
ind = L-1 #2020/11/03訂正
for _ in range(L):
  dec = dec + enc[ind]
  ind = (ind + n) % L
print(dec)
```
**pakenCTF{7h15_15_an_3asy_cryp708r4phy!}**

##2-7 sandstorm (Forensics, 200pt)
keyとdataの入ったzipが渡されます。
dataはモノクロbitmap画像で、keyはバイナリですが、keyの頭にdataと同じヘッダをつけてbitmap画像化して排他的論理和で重ね合わせたらフラグが出てきました。
![XOR.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429434/9801ffd3-ca6d-01e5-5b2d-4c310846f65f.png)
**pakenCTF{C16uDe_31w00D_5hann0n}**

##2-8 Treasures in JPG Desert (Forensics, 250pt)
picture.jpgが与えらえます。ヒントありです。
>hint : treasures have marks

うさみみハリケーンの「青い空を見上げればいつもそこに白い猫」で文字列抽出すると、「pakenCTF{」が見つかります。以下、同ツールでフラグの構成要素（leetとなる半角英数字や「_」「}」）を抽出してみると

```memo.txt
0002959F	NpakenCTF{
0002F2B5	h4rd_4n
0002F413	d_f34r
0002F610	fvl_tr34
00044150	svr3_hv
0004BA70	n71ng}
```
という具合ですので、結合してフラグとなります。
**pakenCTF{h4rd_4nd_f34rfvl_tr34svr3_hvn71ng}**

##2-9 twitter (OSINT, 50pt)
> パ研の公式Twitterを知っていますか？僕は知っています。

twitter内で「pakenCTF」で検索をかけてパ研のtwitterアカウントを見つけ、ツイートを辿るとフラグを呟いていました。
**pakenCTF{Y0u_are_tvv1tter_m45ter}**

##2-10 Address (OSINT, 100pt)
>この中央の建築物の場所を答えよ フラグの形式は pakenCTF{<郵便番号(間に_)>_<丁目>_<番地>_<号>}

![picture.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429434/834e5be8-9429-97dd-e7ac-35e5ef241167.jpeg)

画像から「三鷹中央病院」の文字が読み取れました。そこで、GoogleMapで「三鷹中央病院 CREATE」で検索をかけると、「クリエイトS・D三鷹下連雀店」がヒットしました。
**pakenCTF{181_0013_4_14_16}**

##2-11 kaishuu (OSINT, 150pt)
>ハッシュタグの中身を当てて下さい。FLAGはハッシュタグの中身 ( '#' なし) をpakenCTF{}で括ったものです。

以下の4枚の画像が与えられます。
![4pics.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429434/c3d0f4ce-7f80-65d9-8126-77b640196af6.png)
Google画像検索などを進めていくと、image1.jpgが渋谷のもので、玉川改札の改修に由来することなどが判明。
しばらくtwitterのあちこちを彷徨った結果、「サンキュートーヨコシブヤ」のハッシュタグを発見。半角カナで答えるもNG。そしてまた暫く彷徨ったところ、数字での表現があることを知り、これがフラグでした。
**pakenCTF{391045428}**

#2-12 Station (OSINT,250pt)
>この駅の名称とこの線の名称をローマ字で答えよ
ただし「都営」などは不要で、地名のような固有名詞で回答せよ
回答形式は、英字はすべて小文字、ヘボン式で
pakenCTF{<駅の名前>_station_<線の名前>_line}
と回答せよ
![station.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429434/63260543-3a06-3b1e-71f1-2f6574f544fb.jpeg)

電車はKEIOのロゴ入りでレインボーカラー（パープル）の帯なので京王1000系（2代目）で間違いなし、すなわち路線は京王井の頭線であることはすぐ分かりました。画像の中で文字を拾うと「永福町（以下略）方面」「吉祥寺方面」という文字が読めるので、永福町～吉祥寺間のいずれかの駅であることが判ります。各駅のホームの写真を画像検索して、駅も久我山であることが分かりました。見るとsolve=0で、お、これはもしかしてまだ誰も解いてない！？的な。しかしフラグが通らない！！まじかー。ググって確認すると、正しい読み方は「イノガシラ線」ではなく「イノカシラ線」でしたorz...
**pakenCTF{kugayama_station_inokashira_line}**

#3 おわりに
総じて楽しいCTFでした。運営の皆様お疲れさまでした。来年もぜひ開催していただけると嬉しいです。
2020/11/03追記：余談ですが、ヘボン式の「ヘボン」はオードリー・ヘップバーンの「ヘップバーン」だと昔英語の先生に習ったことを思い出しました。