📖

IPFactory WelcomeCTF2024 Writeup(お引越し版)

2025/04/15に公開

この記事は?

この記事は、IPFactory WelcomeCTF2024の問題作成者自身が解き方を解説したものです。

つたない文章ですが、何かの参考になれば幸いです!

開催概要

  • 2024/06/29 (土) 14:00JST - 06/30 (日) 14:00 (JST)(24時間)
  • Jeopardy方式
  • 問題文は日本語
  • 個人戦
  • 点数は100~500点
  • 正解数による増減はなし
  • Welcome/OSINT/Web/Misc/PPC/Crypto/Rev の7種類
  • PWNはサーバーが間に合わず、次回に持ち越しとなった
  • 参加者は15人(+数人)

このCTFは、IPFactoryが情報科学専門学校の生徒向けに行っているイベントです。
4月から6月の間に開催され、セキュリティ分野に興味を持ってもらうきっかけ作りとして毎年行っています。

Repository

https://github.com/IPFactory/WelcomeCTF2024

Welcome

Welcome - 100Point 15Solves

IPFCTFへようこそ!

Flagは下の入力欄に入力してください。

その後Submitボタンを押すとき、Correctであれば正解です。

InCorrectの場合は答えが違います。
flagはipfctf{welcome_to_the_ipfctf2024}です。

フラグは問題文中にあります。
とりあえずこの問題を解いてみましょう!!!

ipfctf{welcome_to_the_ipfctf2024}

OSINT

Favorite_food - 100Point 9Solves

お昼によく食べるワッフルのお店の名前を忘れてしまった!!!
Flagフォーマットはipfctf{お店の正式名称}です。

Author:Chisenon

Favorite_food

< 想定解 >

写真から、作問者の "Favorite_food" が "Waffle" である事が分かる。

今回のCTFを主催している IPFactory は 横浜駅 の近くにあるので、
"横浜駅周辺のワッフルを売っている店" を調べると...?

GoogleMap_1

いくつかヒットする。
そこから、テイクアウトの有無や包み紙などから"ミスターワッフル"っぽいことが分かるかもしれない。
https://mr-waffle.com/

そのお店の正式名称がフラグになる!

< 想定外 >

どうやら作問者の Twitter X から特定した人もいるみたい...

https://x.com/search?q=Chisenon+ワッフル&src=typed_query

コワイネェ~

ipfctf{MR.waffle}

P.S.
みんなもワッフル食べよう!!!


Where_is_here_?_01 - 100Point 13Solves

写真に写っているのはいったいどこだろう???

Flagフォーマットはipfctf{施設の名前}です。

場所の名前は日本語で答えてください。

17:46更新 正式名称とフラグが同一でなかったので、今までのフラグとwikipediaにある本来の正解のフラグの2つを正解とします

Author:Ogin0pan
Where_is_here_1

特徴的な建物があるときは、とりあえずGoogle レンズに入れてみる。

すると...

HANA

ipfctf{花の美術館}
ipfctf{千葉市花の美術館}
↑ 表記ゆれ対策でフラグが2つある

Objet - 200Point 10Solves

オブジェには何の意味があるんだろう… このオブジェのある施設を利用している人からはあだ名で呼ばれてるみたい

Flagフォーマットはipfctf{オブジェのあだ名} です。

オブジェのあだ名に ,.<>!?_|・'"#$%()+-*/= 等の記号は使用しません。
もし正解だと思うものに含まれていた場合はそれを除いて回答してください

Author:hatomato

Objet

写真をGoogle Lensで調べると東京電機大学の"愛"だとわかる。
その後 (東京電機大学 || 電大) && オブジェ で検索すると、"デススター" と呼ばれていることがわかる。

ipfctf{デススター}

P.S.
表記ゆれのためにデス・スターの点を無くさせたが、2つ正解を用意しておくでもよかった気がする。

作問の経緯:電大の知り合いが謎のオブジェをTwitter X に上げているのを見たから


Where_is_here_?_02 - 200Point 11Solves

さて、今回はあるアニメに登場するモデルとなった中学校だ。

どこか分かるかな??

Flagフォーマットはipfctf{中学校の名前}です。

Author:Ogin0pan
Where_is_here_2

こちらもとりあえずGoogle lensに入れて、
範囲を校舎にし絞ってあげると...

YURU

どうやら"ゆるキャン△"というアニメの聖地らしい
https://yurucamp.jp/

ちなみに、 "アニメ 聖地 中学校 山" で検索しても一応ヒットする。

ipfctf{身延町立下部中学校}

P.S.
最初答えがipfctf{身延町立身延中学校}になっており、
これは間違いだったので2つとも正解とした。(出題ミス)

Reflection - 200Point 1Solves

このお店の最寄り駅の正式名称を教えてほしい!

Flagフォーマットは ipfctf{駅名} です。

例:東京駅の場合、ipfctf{東京}

Author:0kq

Reflection

とりあえず画像内の文字をいろいろ検索してみると、
"EDIYA COFFEE"という店がヒットする。

https://www.ediya.com/

メニューの値段も韓国語で表記されているため、韓国内の店舗を調べてみる!

Map_2

どうやらチェーン店みたいなので、もう少し情報を追加したい...

そこで問題のタイトルである "Reflection(反射)"に注目してみると...
お店のガラスに"ハングル文字"が反射して写っている!

それを人力で反転して読み取り検索にかける。
(ちなみに、ハングル文字の入力はコレを使った)

https://hot-korea.com/tool/translate/keyboard.php

すると、書いてあるのが "광장시장(広蔵市場)" という商店街の名前だということがわかる。

"광장시장 EDIYA COFFEE"で調べると、
"Ediya Coffee 鐘路交差点店"が一番近い店としてヒットする。

GoogleMap_3

また、その店の後ろには頑張って調べた "광장시장" がある。

HIROBA

その最寄り駅の正式名称(ハングル文字)がフラグとなる!

ipfctf{종로5가}

P.S.
ハングルを入力させたかった問題です...
韓国行きたいなぁ~

Apartment - 300Point 7Solves

友達の家に行きたいんだけどこれしか手掛かりがない… せめて郵便番号だけでも特定しないと!

Flagフォーマットは ipfctf{000-0000} です。

Author:hatomato

apartment

電柱に書いてある"室ノ木"から調べると...

image

複数個所見つかる。

image

…が、ここは港南区ではなく金沢区室ノ木地区である。

また、アパートに移っているマークは神奈川県のマークなので神奈川県営の住宅だとわかる。

image

そこから"県営住宅 室の木"で検索すると"236-0037"が出てくるが、
写真の住宅はぎりぎり横須賀市なので"237-0068"になる。

ipfctf{237-0068}

Bus_stop - 300Point 10Solves

Chisenon君は、さっき乗ったバス停に忘れ物をしてしまいました。

しかし、どこで乗ったか思い出せません...

乗る直前に撮った写真からバス停を調べられますか?

Flagフォーマットは ipfctf{バス停に書かれてある日本語表記(例:自由が丘)} です。

Author:Chisenon

Bus_stop

とりあえずGoogleLensにぶち込んでみると...
大阪の"天王寺公園(てんしば)"にこの看板が置いてあることが分かる。

NYAN

そこから最寄りのバス停を探してみるが、結構ある...

neko_chang

写真の背景から、駅や街中ではなく公園に一番近い場所で絞っていく。

ipfctf{あべの橋}

P.S.
本来は、同名バス停内の識別用番号をフラグにしたかった...
(難易度調整)


Where_is_here_?_03 - 300Point 3Solves

今回は手掛かりが少ないぞ…

写真に写っているものはある施設の中にあるぞ!

その施設とはいったいどこだ???

Flagフォーマットはipfctf{施設の名前}です。

[OSINT_3.pdf]

Author:Ogin0pan

ここでは ".pdf" ファイルが渡されたので、
とりあえず開こうとするが...

ER

どうやら見れないらしい...
こんな時は、とりあえず中身のデータを見てみる!

CODE

見づらい!
けど、".xml"、"slideLayouts"、"presentation" という文字列が見つかる。

これらの情報から、"PowerPoint"などのファイルを無理やり変更したのではないかと考えられる。

拡張子をとりあえず".pptx"に変更して、開いてみると...

CNP_1

何か出てくる!

ここから画像の場所を探すのかと思いきや、
既に複数レイヤあることが分かっていたため、画像をずらしてみると...

CNP_2

https://fumotoppara.net/

ipfctf{ふもとっぱらキャンプ場}

Are you Ready? - 400Point 1Solve

このシーンで使われているロケ地が知りたい!!!

Author:Chisenon

Are_you_Ready

とりあえずコレが何なのか調べると、

"仮面ライダービルド" に登場する "仮面ライダークローズエボル" だということがわかる。

https://www.kamen-rider-official.com/zukan/kamen_rider_members/1

また、"ビルド NEW WORLD 仮面ライダークローズ" という映画に登場する事も分かるため、予告映像なども情報になってくる。

https://youtu.be/i1m9jlD7xKs

Googleで"仮面ライダービルド ロケ地"と検索すると、下記のサイトがヒットするため、これらのサイトと照合しながら答えを出していく!

https://vavava.hatenablog.com/entry/2017build

http://trackless-path.net/k/2017.html

まず、映画以外の本編でも同じロケ地を使って無いか調べていくと、似たような場所が度々使われることわかる。

有名なシーンだと、第38話「マッドな世界」があげられる。

38

https://www.tv-asahi.co.jp/build/story/38/

ここの情報から、先ほどのロケ地サイトで調べ端からヒットするものを見ていくと...

image

爆破撮影ができそうで石とか砂っぽい場所が見つかり、コレを調べると...

image

ipfctf{千葉石産株式会社}

P.S.
正直解ける人がいるとは思ってなかった
仮面ライダービルドはいいぞ!!!

Web

webcome! - 100Point 11Solves

IPFactory Welcome LT 2024にご参加いただきありがとうございます。

初のCTFで簡単なのはOSINTですが、webは技術を初めに学ぶのに最適です。

ぜひ楽しみながら勉強していってください!

Author:hatomato

本当に初心者に向けた問題。

image

白背景に白文字でisThisFlag?{xxxxxx}という偽文字列がある。
(意外と引っかかっていた)

ソースコードにコメントとしてフラグが書いてある。

ipfctf{1et's_1earn_w3b}

I_Love_Purple - 200Point 9Solves

暗い色は嫌い… でも真っ白なのはつまらないよね!

Author:hatomato

RED・GREEN・BLUEと書かれたフォームがある。

image

そこにRGBの値を入れると背景が変わる。
明るい紫にするとフラグが出てくる。

if($colorRed >= 128 && $colorBlue >= 128 && $colorGreen <= 60){
        echo "<div class=flag><p>Wow! Purple!</p><p>ipfctf{w0w_purp1e!}</p></div>";
    }

ipfctf{w0w_purp1e!}

My_website - 200Point 9Solves

まず技術を勉強するにあたってwebサイトを作りました。 ぜひ見ていってね!

Author:hatomato

コレもソースコードを見る問題。

image

HTML,CSS,JavaScriptでフラグが三分割されているので、全てのソースコードを見てつなぎ合わせる。

ipfctf{P0w3r_W1sd0m_C0ur4g3!}

フラグはゼルダの伝説のトライフォース(力、知恵、勇気)から
虹色に輝くタイトルがお気に入り

Are_you_admin? 300Point 4Solves

友達のサイトに招待されたのに管理者じゃないと入れないみたい… どうにかログインできないかな?

Author:hatomato

user / passが入力できるページに飛ばされる。

image

ソースコードを見ると
<input type="hidden" name="admin" value="False">
という要素が隠れているので、適当なuser / passを入力したうえでここをTrueにして送る。

ipfctf{w1th_great_p0wer_c0mes_great_resp0ns1b1l1ty}

ちなみにデータベースは使用していない。

Welcome_to_my_CTF - 200Point 5Solves

IPF主催CTFへようこそ

Author:parsley

サイト内から自力でフラグを探す問題

image

怪しげなURLやフォーム、リンクなどが散りばめられている。

本物のフラグはページにアクセスする前に、一瞬だけリダイレクトされる場所にある。

フェイクフラグのcookieを持っているとリダイレクトされないので、初回のみ表示されるという仕組みになっている。

image

curlなど、何でもいいので redirect.html の中身を見てみる。

curl http://123.456.78.9/redirect.html

image

怪しいaタグの先を見てみると...

ipfctf{b4r3t35h1m4tt4}

F12のデバッグツールを使い、
通信経路を確認してリダイレクトページの存在に気付けると見つかるはず!

Misc

Let's_buy_a_flag!_Easy - 100Point 11Solves

やぁ。僕は、xx-チくんだよ。今日は、WelcomeCTFにようこそ。

初心者の人も経験者の人も、楽しんでくれたら、うれしいな。

さて、これから僕はフラッグを買いに行こうと思うよ。 君、僕が買うのの手伝いをしてくれないかな。

もし買えたら君が持っていっていいよ。

Author:save

pythonのファイルを実行した後、表示される動画内でEnterの入力が求められるのでそこでEnterをクリックするだけ。

sc

[Enter]ぽち

sc

なお、コマンドプロンプトやメモ帳などでソースコードを見ると一瞬で終わります。

ipfctf{B4N4N45_4R3_D311C10U5!}

Let's_buy_a_flag!_Normal - 200Point 10Solves

やぁ、僕は???だよ!なんかxx-チくんがふらっぐ?、 とかいうのを買ってきてほしいみたい。

お金は持ってるから一緒に買いに行ってくれる?

Author:save

まずは、Easyと同様にpythonプログラムを実行してEnterを押す、とお金が足りずフラッグが不完全な形で出てきます。

sc

なので、コマンドプロンプトやメモ帳などで直接ソースコードを見てフラッグを手に入れます。

sc

ちなみにお小遣いのディレクトリ・ファイルを作って特定の場所に置いてあげると動画が変化し、再生後に普通にフラッグが手に入ります。

ipfctf{xx_1_10v3_y0u_50_much!}


Max_prime - 300Point 8Solves

6143590134451345688932123456721854310を素因数分解したとき、もっとも大きな値を求めなさい。

flag形式はipfctf{求めた素数}とします。

2が正解である場合、ipfctf{2}と入力してください。

Author:seiya1122_404

factordbで素因数分解すると答えが出てくる。

http://factordb.com/index.php?query=6143590134451345688932123456721854310

ipfctf{164432230050389377}

ChatGPT...? - 300Point 3Solves

ここでは、gpt-4を搭載したチャットボットと対話していただきます。

対話の相手はぱせりが普段使いしているプロンプトをもとにしてキャラ付けしています。

CTFのフラグを守るべき秘密としてあらかじめ入力しているので、対話によって聞き出してください。

Author:parsley

サイトにChatBotが組み込まれている問題。

image

アプローチとしては、

  • 守るべき秘密が秘密である必要がないと説得する
  • 元の指示を無効にするように指示する

などの方法でプロンプトを破壊し、情報を聞き出すことができる。

作成初期のChatBotは相当頑固だったので今回は割と緩めの設定にしてある。

< 解答例 >

image

ipfctf{ud1d1tg00dj0b}

ふっかつのじゅもん - 500Point 2Solves

魔王を倒してフラグを入手しよう!

ふっかつのじゅもんで何度でも蘇れるぞ!

セーブ方法

  1. Tabキーを押してメニューを開く
  2. セーブ を押す
  3. じゅもんをコピー
  4. ゲームを終了を押してタイトルに戻る
  5. じゅもんを入力する

フラグフォーマットはipfctf{xxxxxx}です。

Author:しまきぃ(ゲスト)

ちょっとしたブラウザゲーム

image

仕様としては、

  • セーブを行うと、FC版ドラクエのようにステータスを変換した文字列が表示される
  • 1レベル上がるごとにメモを取り、ステータスがどのように変化したかを記録する
  • 25面まではこの方法でレベルを上げて進める
  • ラスボス前の25面でヒントが手に入り、それをもとに99レベル、ポーションMAX、最大装備にしてクリアできる
  • bitの関係上、127レベルまでありそうだが、100レベル以上にはならないようにしている
  • JSはJSf〇ckにより難読化されているため、ソースコードから読み解くことはできない

とりあえず、セーブデータをいじりながらステージ25まで行きドラゴンを倒す。

image

すると「魔法の紙片」がドロップする。
どうやら"ふっかつのじゅもん"の内部構造が書かれているみたい?

image

まず、レベルの上限が99らしい。
10進数の"99" は 2進数で"0110 0011"になるため、7桁の入る緑が怪しい...

確認のために、始めたばかりのLv.1のセーブデータを見てみる。

|m{|ししくしりししししちに

先頭の "|m{|" はユーザー名と同じ文字数で変換される。

次の11文字の平仮名と、ヒントの44マスが、1文字 = 4bitでかみ合い会うため、ししく をがレベルのセーブに使うのではないか???

と言う切り口で、とりあえずレベルを上げてデータをとってみる。

レベルアップごとに先頭3文字を見る

Lv1 ししく~
xxx|x xxxx x1|xx

Lv2 ししち~
xxx|x xxxx 10|xx

Lv3 ししね~
xxx|x xxxx 11|xx

Lv4 しにし~
xxx|x 0001 00|xx

Lv5 しにく~
000|0 0001 01|xx

Lv6 しにち~
000|0 0001 10|xx

Lv7 しにね~
000|0 0001 11|xx

Lv8 してし~
000|0 0010 00|xx

という風に、中央の4bitは4レベル上がるごとに動く事がわかる。
地道にレベリングすると、下のようなテーブルができあがる...

//じゅもん処理
var bitcipher = ["0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"];
var stringcipher = ["し", "に", "て", "は", "く", "ま", "い", "す", "ち", "と", "り", "ぬ", "ね", "ふ", "む", "あ"];

このテーブルをもとに、セーブの構造を調べていくと...

add

とこのような構造になっている事が分かる。

ちなみに、各パラメータの詳細は

レベル上限:99
経験値:制限なし
お金:制限なし
ポーション:5個
装備:3段階
ステージ数:26

となっている。
そのため、最強のセーブデータは
000|1 1000 11|11 1111 1111| 1111 1111 1111 11|10 1|11|1 1010

あちああああああむあり
となる。

実際に入れて、経験値とお金を2進数に変換すると正しいことが証明される。

XP:11 1111 1111 ⇒ 1023
GOLD:1111 1111 1111 11 ⇒ 16383

image

魔王討伐

image

結構ギリギリの戦いになりそう...

無事、フラグをゲットし無事終了

image

ipfctf{Th3_gr3at_3vi1_has_b33n_banish3d}

< 想定外 >
image

どうやらブレークポイントを設定するとクリック時に元のコードが見られるらしい…

完全に想定外だった...

<!-- 唯一のIPFactory外の人による問題でhatomatoが作成を依頼。
正直こんなゲーム良く作れるなと思った。
誰か彼を雇ってあげてください。
https://github.com/simakixi -->

FC版ドラクエ1のふっかつのじゅもん解説動画から着想を得た。

参考元のYouTube

PPC

Guess_the_number - 200Point 11Solves

2024の6乗に30乗したときの一の位を求めなさい。

解答形式はipfctf{求めた数}とします。

1が正解の場合ipfctf{1}と入力してください。

Author:seiya1122_404

実際に求めることは不可能に近いため2024の一の位に着目する。

4 × 4 % 10 = 6

6 × 4 % 10 = 4

したがって偶数回かけ合わせれば一の位は必ず6、

奇数回かけ合わせれば一の位は必ず4であることがわかる。

ipfctf{6}

実際に求めようとすると約2763垓(2763 × 10^20)桁ぐらいになりそうなので、求めるの不可能なのでは?

All_Numbers - 300Point 7Solves

1~12のダイスが12つある。12つのサイコロの値がそれぞれ違う値になる確率を求めよ。

ただし、seiya1122_404によるさいころの細工は考慮しないものとする。

解答形式はipfctf{分子_分母}とします。

1/2が答えの場合、ipfctf{1_2}と回答してください。

尚、もっとも小さな値に約分した値のみ正解とします。

Author:seiya1122_404

まず、全通りを求めると12^12通りである。

その中で12つのサイコロの値がそれぞれ違う値になるとき

以下の求め方が楽だと思われる。

1つ目のサイコロ → 12通り

2つ目のサイコロ → 11通り

3つ目のサイコロ → 10通り

...

12つ目のサイコロ → 1通り

したがって、12!/12^12を計算すればflagを得ることができる。

ipfctf{1925_35831808}

Crypto

Let's_buy_a_flag!_Difficult - 300Point 5Solves

皆様ごきげんよう! ????でございます。

どうやらxx-チのやつがフラッグというものを買わせたいらしく、

面倒ではあるのですが、今から私とともに買いに行っていただけませんか?

それでは向かいましょう。

Author:save

pythonプログラムを実行すると、Normalで壊れた自販機の動画が再生されます。

sc

Enterを押してみますが正常に動作せず、フラッグも出てきません。コマンドプロンプトやメモ帳で中身を見てみると自販機のプログラムはスクラップになっていることがわかります。

sc

そして、よく見るとこのプログラムはBase64でデコードし実行されているのがわかるため、cyberchefなどを使ってdecodeしたり、exec()をprint()に変更してデコード結果を出力することでフラッグを入手することができます。

sc

ipfctf{600d_1uck!_????_kun!}


Ancient_cipher - 300Point 1Solve

15世紀に遺された暗号だそうだ...

Author:seiya1122_404

ヴィジュネル暗号の問題。解くために必要な鍵がないが
Vigenère Solverを使うと解くことができる。

sc

ipfctf{Vigenere_1s_c0mplex_cipher}

ちなみに鍵はkeyisnone


Famous_RSA - 400Point 1Solve

RSA暗号だからといって過信は禁物だよ

え?スウェーデンの皆さん解読早くないですか?

Author:seiya1122_404

スウェーデンの皆さんの解読が早かったのは、スウェーデンにBESKというコンピュータがあり、
そのコンピュータが発見したメルセンヌ素数(2^3217 - 1)が今回の秘密鍵になっていたから。

import sympy
import gmpy2
n = 638763712382923216059106991508678361926241300260491635444155072138797654644420376834980020628402237483183050231723091099889242548861893992519673725690498683290925618378219154699628671253025366236125653875797981084627703992151210262823556152854587889660128183786590389107924650303462088524017965158670038212429052686856199069337714609730598528710567231286023062488837632001759674494990908205767939998334415171542949212941227037479893872694618273143903190087350009336995237701151951298893596886809827978858975366370945795353343737870571847332865096110031837016699001866848569735703123594467954423347391797989003720529222967409042795462413595968837859344602451140123847750206423132616123374510519134155878677275318352094026485830602587184893941344518411895423972811395966154770067044404253907305119879567378014942077864608421117041317469689004410567371414070669929627585709759053436192100154056069524832159763912953734996562487437313571958242244788672338954430715235839390715133047981823279857295687765690133381050760387006975127881047741833457753869406523145467675345816700942437988590114303396258551662145593784121319211036594981310582202639861091037767384556577013029645379608593283943265071036597069153411000589962068862298199940078647970297198237530727720114792955367418928119343512224184352959753385381340717332165730761079703151029675928778845515782638770637925235116365180975040069608202894453244071250874808794923511639012857066134085464485913951982358054077428350296954920402017575543091662880474835072483164535054722523727529357579548521138776305884874915092929918304581486044364764503916866170083095323756800946233341562613275818070731966959415865838279351595889753634465004053194104233162241046849349664509844601810217064486539000367106950430645609233136138265214449569833300140883502026581730821158074819776603419475029918282313031726869306800751118186144427460357194528797587050855399703403972842986092779956265648479955330450625426777933052671694013966911
p = 2 ** 3217 - 1
q = n // p
e = 65537
l = (p-1)*(q-1)
d = gmpy2.invert(e,l)
c = 260369703995570293320673614384121222979049514937723856658266813755140319395586671234576034991481356796528898492940674663320398168996169184064650360035001035763108375456002218223069481985900726538125070793314613948061966053754989471639612593649499979409739126358382648739881096053481962171050571910316150686722862055384409194626273019448557495215005113298098835634452453448266437372383690260504376680960488378846417067354975430512238315159814507245029550861778453614175149331883259180649788830637947075459489109523256421469196369241863862655618211042369600521394535509199051140721765090222615294405183766754937432336803289399485838811004435756608825754466771408718492760735779283412117084523685604150828876845286813619389404715760577287489237014121512525178553980251497218493643160501251624150114756023861825310173978370338479983345349503384582080330423235668134334380726598110403708699465240412445702612120102396893068813072391639138432413372315291070801601354877884943452786354788127896480948299784053308850428928030458856960787264253316991604253500799828923034062359182908224799045882567813586391468975567044405271124129403871784116620378419600802998306050852249549530924361420253571486588560554000846257953021059771156116084261513894827059580117202479592475047525994694474035184637953420539473339136631993953666325596405116631549739119407840480761884069846937439380616635780851365476579510986168709738843202949032612802148321293826324507311771286125878387975432896454300905560582020036310149641946891917372196407139047658514757092093532331943446849125309897372078047638457850219767040811263844025570160700327497220177657123002786726157012077733359404553648763127590275210541023087094836376878182521498828864420874670914502638714001001593046352019517211269783755771825030153015283219812646429991346994011193547537991476754024159215297447505986920893959633247187309103759312149531917976156763011690044200287554982162287023608044149878937673273049725912678227287605035
m = pow(c, d, n)
decrypted_message = int(m).to_bytes((int(m).bit_length() + 7) // 8, "little").decode("utf-8")
print("Decrypted message:", decrypted_message)

ipfctf{H0w_m4ny_pr1m3s_d0_u_k17ow?}

想定外解

nが十分に小さいため素因数分解できるらしい・・・・・・

from sympy import factorint

# 素因数分解したい数を指定します
n = 638763712382923216059106991508678361926241300260491635444155072138797654644420376834980020628402237483183050231723091099889242548861893992519673725690498683290925618378219154699628671253025366236125653875797981084627703992151210262823556152854587889660128183786590389107924650303462088524017965158670038212429052686856199069337714609730598528710567231286023062488837632001759674494990908205767939998334415171542949212941227037479893872694618273143903190087350009336995237701151951298893596886809827978858975366370945795353343737870571847332865096110031837016699001866848569735703123594467954423347391797989003720529222967409042795462413595968837859344602451140123847750206423132616123374510519134155878677275318352094026485830602587184893941344518411895423972811395966154770067044404253907305119879567378014942077864608421117041317469689004410567371414070669929627585709759053436192100154056069524832159763912953734996562487437313571958242244788672338954430715235839390715133047981823279857295687765690133381050760387006975127881047741833457753869406523145467675345816700942437988590114303396258551662145593784121319211036594981310582202639861091037767384556577013029645379608593283943265071036597069153411000589962068862298199940078647970297198237530727720114792955367418928119343512224184352959753385381340717332165730761079703151029675928778845515782638770637925235116365180975040069608202894453244071250874808794923511639012857066134085464485913951982358054077428350296954920402017575543091662880474835072483164535054722523727529357579548521138776305884874915092929918304581486044364764503916866170083095323756800946233341562613275818070731966959415865838279351595889753634465004053194104233162241046849349664509844601810217064486539000367106950430645609233136138265214449569833300140883502026581730821158074819776603419475029918282313031726869306800751118186144427460357194528797587050855399703403972842986092779956265648479955330450625426777933052671694013966911

# 素因数分解を行います
factors = factorint(n)

print(factors)

Base2024 - 400Point 1Solve

今年は2024年...

よし、Base2024という新しい暗号を作るか。

新しい暗号だし誰も見破れないでしょ(フラグ)

Author:seiya1122_404

ChatGPTにencrypt.pyの逆の手順を聞くと解読用のコードを書いてくれる。

def decode_base2024(encoded_string):
    base = 2024
    decoded_value = 0

    for char in encoded_string:
        decoded_value = decoded_value * base + ord(char)

    decoded_string = ""
    while decoded_value > 0:
        decoded_string = chr(decoded_value % 256) + decoded_string
        decoded_value //= 256

    return decoded_string

# ファイルからBase2024でエンコードされた文字列を読み込む
with open('encoded.txt', 'r', encoding='utf-8') as file:
    encoded_str = file.read().strip()

# デコードする
decoded_str = decode_base2024(encoded_str)
print(decoded_str)

ipfctf{N0w_y0u_can_d3cryp7_4ny_numb3rs_of_base}


Scattered_RSA - 400Point 1Solve

以下から暗号化された文字列を解読し、flagを見つけてください。
少し難しいと思うので、予めヒントを出します。

  1. 初め2行は公開鍵。以降は一行一文字暗号化された文字が記されています

  2. 各文字はord,chrでUnicode コードポイントを用いて数値化されています

file:ctf_rsa.txt
 
Author:parsley

ファイル(ctf_rsa.txt)の中身はこのようになっています。

image

まず、n を素因数分解し p と q を求めます。

n = 121932633334857493

p = 123456791
q = 987654323

試し割法などで素因数分解すると数十分かかるため、ポラード・ロー法や、適当な素因数分解サイトを使用するのをお勧めします。

http://www.factordb.com/index.php?query=121932633334857493

次に、オイラーのトーシェント関数ϕ(N)を計算し、秘密鍵dを求めます。

phi = (p-1) * (q-1)
d = inverse(e, phi)
print("d:", d)

( inverse は モジュラ逆数の計算 )

d = 84471144059424953

最後に、これらの情報を元に暗号文リストをループし、各暗号文を復号して平文を出力します。

for i in cypherlist:
    m = pow(i, d, N)
    
    print(''.join(chr(byte) for byte in long_to_bytes(m) if chr(byte).isprintable()), end='')

出力を見ると、フラグらしき文字列が見つかります。

image

ipfctf{c0ngr4tu14t10n5}

別作者の問題と違い、実際にプログラムでRSAを解く問題となっています。


Can_you_devide_it? - 500Point 1Solve

nが素因数分解されなきゃ安全でしょ?

Author:seiya1122_404

eが小さいのでRSA暗号の脆弱性であるLow_Public_Componentを利用する。

import gmpy2

def int_to_bytes(x: int) -> bytes:
    return x.to_bytes((x.bit_length()+7)//8, 'little')


def LPA(c, e, n):
    while(True):
        m, b = gmpy2.iroot(c, e)
        if b:
            break
        else:
            c += n
    return int(m)


def main():
    n = 4156538470158208367161179083228780362243539447751009766810923696950893447239988855874643666714592747214463886162935208577164939629713938094898124101627521879439401119003080249866596265735090500856954456833363862773722533180461366520681396592230158310814718376584123509611311853839453503295278634591075215559931395068251523000058782170910129234782190434402746703996608756618386102385931588971753728646688198223439142172904384667627253126510324246928868417884724983977711686147127850654670341067461603154906254693342973352604801294055020056749737352040509329721412291673075972952263208836392086774850684267883511050611766436172546242176145106472673689300688814185539210319556512904843318481039398399945128560961638426665520084544088127094287760600793961824465257963061883828337537708340755409316426869021840501293469739641961579259607780771373413584557277475888975933931582977814605871391693249189674676720442809939023227814425676954514004496168047785439649076172215479925957765464799276684050770908520010821816474770692886822153420523342171764035605219163043131822096816824630632005537724966251177030592654147545566440363045481407989526634796045151972707660308545811728469789524309930819896992929208949795739044476343456176643530805691274630755211705709680116493284982502879432933339060260526917626246652887800977052197719950378748520080340590681912995343934808476937736095873348415627113194295674270923775650024487172838047711292772469870472668014635092218489125846984302508423745658018616554447560138345912344715007363158266133128268063791996977970378484665702867934783023055485761173723399706018882177899787603436223583991175703542804491437450916189122997482076186851628813654514455602976094329854427272187706271101440312415721674866611741552793529329654584592436569367131300981663557715750000747114469317091021412707642957744380148077980446706053014187109352212584605627449876151095813673796804917363291087809517390944571379062355515871566047245440449334755816321564189326139782911402235706344312734278646789431546786725900809028144999798629974164341185619472366152398546206834232980789878702746251069098020597222319649155820853999963341638599035109577544645865100294317003504147238336418460237915212897662810683723515608589470282677096373489066477599678238317206454843048146543378112332402974409508584741292038780792045840358769577951298858975466605616539306765725252177458635724910187179306680286916484541462108919770384119026957339096774326266424748278997534787093669008459053417768140727449795509595110679987806682610621018941210105610822212141623254737412603630327802009512673311164243780702614748514823891302767198053774254041276452977537144825152365880294786220368999926410244410635689337116204619694521646459657642411226570434648857616287150806448327785704344391480273029817167770859415038200353642752648751504626203435393076666285862767208482969406740742499207000239776203806530401376181815105860883647345588217262874173744785103899536787888374798455932188747818718620253870792178054956043829745505317810147141288300089249966319171981594417622443504260681156035542608844060658353491809598360536377292877872957008312421510120360202602023131069497606490051261364546777052578970824185727009392834514185356175969705676414413453651367749018178678142175925163276265511008001985928367554053477174803727163346433908345029276116488990992988567932769911721684035944921142233296865063996074264438271729069587696378471853210415718364373660909290409469216375286993411455617945609579185057036193054023352368724100060989257082724450674428990090347350428624666834888327218202421280879523707527407647434733393238300972181742659812780923093356040409268420477368166754820436019969582570345857104522816975238284237989192734821264069381592331260938160391542212422518294648996414118303214064639018250660254045445675916725380805603283565771448210003909768228874947823304839847316133497545875884770257869836855161121609163214419087539944017185241553663372133082449425800355994103466753564771194334172146375069319
    e = 29
    c = 977852386005290704777145966734721414473506131875571539260613965223849872713949361510905098998624594834079301638971994117958029896997299364648465702922523285324335264120342255784831908793924203417395853171349545282985757756133316608210771430649251749410346659432944572013115808260683929030860312931285170111789924807763535484053688838548103560056732224725334133737695118743494134989712424529259130833508736469452919756463659361090208125595143222731379360671878049924071036254362488383998534078579682733765394912894436829324162247395010782669747241527251023734466463328488805352029755355835940924921832693386867917038198898476816187353084036750497143109346566313487348679688660710617901897247451998704604923559335049910633213766521891679831668203773023635652636864876084330388299300537892099892187349524869402454463197358117152561348315765530136228973249715271522255890603164322746223872852557117657291677024970704652943877758519316651690477492987250982174857613715474361987518988265876828865493008368287754815141925743245778341205655363828781762271934915340309681870363214056337532887815120984039313590589512955036578507609127179933082061564659161521510738078080111842393348997913225459509385939246186490689186858063822237659606109062703994986955573533581087350335039294281417695609140610993710487073979056156986444002247858180041452421910090128966308031381987696825186794868269877066999189487647317643957807870355192005716268457130392355772786174389000595494146993556828326804007241546239074386997096893416996896056463078199903538416240527410969441412121893170142364366648883763585406143919851650737140011655901654523515622867579367902938519238770742201534171909510252245536129121746350297551547598572704651223523480631411075592041015625
    m = LPA(c, e, n)
    
    flag = int_to_bytes(m).decode().strip()
    print(flag)

if __name__ == '__main__':
    main()

ipfctf{Sma1l_e_1s_d4ng3r}

解いた人、RSA暗号の脆弱性についてよく知っているのだろうか?

Rev

Let's_buy_flag_END - 400Point 1Solve

...。

Author:save

問題文はありません。pythonプログラムを実行したすると再生される動画からすべてを察するという謎解き問題です。

動画を実際に見てみると、右下の手が特定の画像の上で特殊な文字列に変化して消えた後に一本下線の<u>終</u>が表示されて動画が終了します。

sc

この特殊な文字列はバイナリデータを表しており、これまでの問題の動画ファイルのバイナリデータを確認すると結びつくかと思います。
そして実際にBzエディタなどのようなバイナリエディタで中身を確認してみると、動画ファイルの"_1(下線1)"となっている動画の最終行にフラッグのかけらが置いてあります。

sc

これをE(asy)、N(ormal)、D(ifficult)、のそれぞれから抽出し、合体させることでENDのふらっぐを取得することができます。

ipfctf{H1_1m_734ch__70d4y_137'5__134rn_m4ny__7h1n65_70637h3r}

リート変換知ってる人はこの問題群(Let's_buy_a_flag!)の元ネタわかっちゃうかも。

感想

CTF運営はとても面白かったです。特に始まって数時間は解答も多く、フラグ自体の間違いや分かりづらさの修正などがあって、忙しくも楽しかったです。

解答一覧を眺めながら、「こんなに間違いが多いのか」、「これを解けたのか」、「これは問題が悪かったな」など、いろいろと勉強になりました。

また、この機会にAWSを触れたことは大きかったです。

目的がないと動くのは難しいので、どんどんこういったイベントを開催すると、サークルとしても盛り上がりますし、個人も成長できるので、win-winだと思います。

改善点

  • OSINT問題では、表記揺れによる誤答が多く見られました。これを解消するためには、Google Mapsに準拠することを明記する、座標や郵便番号での回答を求める、または複数の正解を設定するなどの対策が有効だと思われます

  • 今回は締切直前まで準備が続き、多くの点でギリギリの状態でした。次回は 締切駆動 を避け、より余裕を持った準備を心掛けたいです

  • 点数設定については、一人の判断で行われたため、問題の難易度との乖離が生じました。今後はより公平性を確保するために、点数設定に複数人の意見を取り入れることを検討したいです

最後に

IPFactory WelcomeCTF2024 にご参加いただいた皆様、ありがとうございました。
多少のドタバタがありましたが、次回の機会があれば、さらに面白い問題をご用意したいと考えております!

今後ともIPFactoryをどうぞよろしくお願いいたします。

Discussion