はじめに
- CpawCTFを全問解いてみたのでまとめました。
- 基本的にはMac OSを使用し、必要に応じてKali Linuxを使うという形で解いてみました。
- 問題番号が飛んでいるので何問か抜けているかのように見えますが、CpawCTFの仕様によるものです。
Level 1
Q1.[Misc] Test Problem
この問題の答え(FLAG)は、cpaw{this_is_Cpaw_CTF} です。
下の入力欄にFLAGを入力してSubmitボタンを押して、答えを送信しましょう!
Enjoy CpawCTF!!!!
- そのままです。
Q6.[Crypto] Classical Cipher
暗号には大きく分けて、古典暗号と現代暗号の2種類があります。特に古典暗号では、古代ローマの軍事的指導者ガイウス・ユリウス・カエサル(英語読みでシーザー)が初めて使ったことから、名称がついたシーザー暗号が有名です。これは3文字分アルファベットをずらすという単一換字式暗号の一つです。次の暗号文は、このシーザー暗号を用いて暗号化しました。暗号文を解読してフラグを手にいれましょう。
暗号文: fsdz{Fdhvdu_flskhu_lv_fodvvlfdo_flskhu}
- 解読ツールは色々とありますが、cryptiiを使いました。
- 問題文にあるように3文字ずらせば簡単にフラグを得られます。
Q7.[Reversing] Can you execute ?
拡張子がないファイルを貰ってこのファイルを実行しろと言われたが、どうしたら実行出来るのだろうか。
この場合、UnixやLinuxのとあるコマンドを使ってファイルの種類を調べて、適切なOSで実行するのが一般的らしいが…問題ファイル: exec_me
- まずファイルを調査してみます。
$ file exec_me
exec_me: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=663a3e0e5a079fddd0de92474688cd6812d3b550, not stripped
- 'ELF 64-bit LSB executable'から実行可能であることがわかるので、実行してみます。
$ ./exec_me
zsh: permission denied: ./exec_me
- 権限で弾かれてしまったので、実行権限を与えて再度実行すればフラグが得られます。
$ chmod +x exec_me
$ ./exec_me
Q8.[Misc] Can you open this file ?
このファイルを開きたいが拡張子がないので、どのような種類のファイルで、どのアプリケーションで開けば良いかわからない。
どうにかして、この拡張子がないこのファイルの種類を特定し、どのアプリケーションで開くか調べてくれ。問題ファイル: open_me
- ファイルを調べる。
$ file open_me
open_me: Composite Document File V2 Document, Little Endian, Os: Windows, Version 10.0, Code page: 932, Author: �, Template: Normal.dotm, Last Saved By: �, Revision Number: 1, Name of Creating Application: Microsoft Office Word, Total Editing Time: 28:00, Create Time/Date: Mon Oct 12 05:27:00 2015, Last Saved Time/Date: Mon Oct 12 05:55:00 2015, Number of Pages: 1, Number of Words: 3, Number of Characters: 23, Security: 0
- Wordファイルであることが分かったので、拡張子を'.doc'に変更してWordで開いてみるとフラグが得られます。
Q9.[Web] HTML Page
HTML(Hyper Text Markup Language)は、Webサイトを記述するための言語です。
ページに表示されている部分以外にも、ページをより良くみせるためのデータが含まれています。
次のWebサイトからフラグを探して下さい。http://q9.ctf.cpaw.site
※この問題のサーバへの攻撃はお止めください。
- ページソースを開いてみます。
- 'cpaw'でページ検索をかければ、ヘッダにフラグが見つかります。
Q10.[Forensics] River
JPEGという画像ファイルのフォーマットでは、撮影時の日時、使われたカメラ、位置情報など様々な情報(Exif情報)が付加されることがあるらしい。
この情報から、写真に写っている川の名前を特定して欲しい。
FLAGの形式は、"cpaw{river_name}"
例:隅田川 → cpaw{sumidagawa}問題ファイル: river.jpg
- Exiftoolを使って、画像のメタデータ見てみます。
- インストールしていない場合、brewでインストールできます。
$ exiftool river.jpg
-
緯度経度を得られたので、少し整形してGoogle Mapで検索してみます。
- GPS Latitude: 31 deg 35' 2.76" N
- GPS Longitude: 130 deg 32' 51.73" E
- 31 35 2.76 N 130 32 51.73 E
-
出てきた川の名前を指定されている形式に沿って書けば、フラグを得られます。
Q11.[Network]pcap
ネットワークを流れているデータはパケットというデータの塊です。
それを保存したのがpcapファイルです。
pcapファイルを開いて、ネットワークにふれてみましょう!pcapファイル
- pcapファイルをWiresharkで開けば、右下のアスキー表示領域にすぐフラグを見つけることができます。
Q12.[Crypto]HashHashHash!
ハッシュ関数とは、値を入れたら絶対にもとに戻せないハッシュ値と呼ばれる値が返ってくる関数です。
ですが、レインボーテーブルなどでいくつかのハッシュ関数は元に戻せてしまう時代になってしまいました。
以下のSHA1というハッシュ関数で作られたハッシュ値を元に戻してみてください!(ヒント:googleで検索)e4c6bced9edff99746401bd077afa92860f83de3
フラグは
cpaw{ハッシュを戻した値}
です。
- SHA-1 Centerが便利です。入力するだけで簡単にフラグを入手できます。
Q14.[PPC]並べ替えろ!
下にある配列の中身を大きい順に並べ替えて、くっつけてcpaw{並べ替えた後の値}をフラグとして提出してください。
例:もし配列{1,5,3,2}っていう配列があったら、大きい順に並べ替えると{5,3,2,1}となります。
そして、フラグはcpaw{5321}となります。
同じようにやってみましょう(ただし量が多いので、ソートするプログラムを書いたほうがいいですよ!)[15,1,93,52,66,31,87,0,42,77,46,24,99,10,19,36,27,4,58,76,2,81,50,102,33,94,20,14,80,82,49,41,12,143,121,7,111,100,60,55,108,34,150,103,109,130,25,54,57,159,136,110,3,167,119,72,18,151,105,171,160,144,85,201,193,188,190,146,210,211,63,207]
- Pythonで処理を書きました。
n = [15,1,93,52,66,31,87,0,42,77,46,24,99,10,19,36,27,4,58,76,2,81,50,102,33,94,20,14,80,82,49,41,12,143,121,7,111,100,60,55,108,34,150,103,109,130,25,54,57,159,136,110,3,167,119,72,18,151,105,171,160,144,85,201,193,188,190,146,210,211,63,207]
# 大きい順に並び替え
n.sort(reverse=True)
# 文字列に変換して結合
print(''.join(map(str, n)))
Level 2
Q13.[Stego]隠されたフラグ
以下の画像には、実はフラグが隠されています。
目を凝らして画像を見てみると、すみっこに何かが…!!!!
フラグの形式はcpaw{***}です。フラグは小文字にしてください。stego100.jpg
- 画像の左上・右下にモールス信号が書かれています。
-.-. .--. .- .-- .... .. -.. -.. . -. ..--.- -- . ... ... .- --. . ---... -.--.-
- Morse Code Translatorを使って変換する。
CPAWHIDDEN_MESSAGE:)
- フラグは小文字という指定があるので、小文字にして形式に当てはめればフラグが得られます。
Q15.[Web] Redirect
このURLにアクセスすると、他のページにリダイレクトされてしまうらしい。
果たしてリダイレクトはどのようにされているのだろうか…http://q15.ctf.cpaw.site
※この問題のサーバへの攻撃はお止め下さい
- curlで通信の詳細を出力させます。
- -v: 通信詳細の出力
$ curl -v http://q15.ctf.cpaw.site
- X-Flagに、フラグを見つけることができます。
Q16.[Network+Forensic]HTTP Traffic
HTTPはWebページを閲覧する時に使われるネットワークプロトコルである。
ここに、とあるWebページを見た時のパケットキャプチャファイルがある。
このファイルから、見ていたページを復元して欲しい。http_traffic.pcap
-
Wiresharkでpcapファイルを開きます。
- HTTP通信を抽出するために、「File」-->「Export Objects」-->「HTTP...」-->「Save All」から、適当に作ったディレクトリにファイルを保存します。
-
fileコマンドで調べてみると、network100、network100(1)が HTMLファイルになっています。
- network100(1)がそれらしきページになっているためソースコードを見て、ソースコードに合わせてcss, img, jsのディレクトリ構造を作って、ページを再読み込みするとフラグが得られます。
Q17.[Recon]Who am I ?
僕(twitter:@porisuteru)はスペシャルフォース2をプレイしています。
とても面白いゲームです。
このゲームでは、僕は何と言う名前でプレイしているでしょう!
フラグはcpaw{僕のゲームアカウント名}です。
- 検索すれば過去のツイートから見つけることができます。
Q18.[Forensic]leaf in forest
このファイルの中にはフラグがあります。探してください。
フラグはすべて小文字です!file
- fileコマンドで調べるとpacpファイルですが、WIreshrkでは読み込めません。
- catコマンドで、内容を見てみると「lovelive!」という文字が大量に出てくるため、それ以外の文字を発見するため、「lovelive!」を削除してみます。
- stringsコマンドで文字列を抽出し、sedコマンドで「lovelive!」を削除すると、同じ文字が三つ連続したフラグが見えてきます。
$ strings misc100 | sed 's/lovelive!//g'
Q19.[Misc]Image!
Find the flag in this zip file.
file
- Zipを開こうとすると開けないためfileコマンドで調べると、OpenDocument Drawingのファイルであることが分かります。
- OpenDocument Drawingは、「.odg」拡張子ですが、Documentファイルなので拡張子を「doc」にしてみると、Wordで開けます。
Q20.[Crypto]Block Cipher
与えられたC言語のソースコードを読み解いて復号してフラグを手にれましょう。
暗号文:cpaw{ruoYced_ehpigniriks_i_llrg_stae}crypto100.c
- Cファイルをコンパイルします。
$ gcc crypto100.c
- プログラムは文字列を逆順に出力しますが、キーの値が与えられていないため、
- 第一引数に暗号文、第二引数にキーの値を指定して実行します。
- キーの値を1から順に試していくと、4の時に文章が出力されます。
$ ./a.out ruoYced_ehpigniriks_i_llrg_stae 4
Q21.[Reversing]reversing easy!
フラグを出す実行ファイルがあるのだが、プログラム(elfファイル)作成者が出力する関数を書き忘れてしまったらしい…
reverse100
- fileコマンドで調べると、実行ファイルであることが分かります。
- stringsコマンドと、grepコマンドを合わせて、フラグがありそうな場所を探してみます。
$ strings rev100 | grep cpaw
- cpawという文字列が見つかったため、その下30行ほど出力してみます。
- すると、縦にフラグが現れるのが分かります。
$ strings rev100 | grep -A 30 cpaw
Q22.[Web]Baby's SQLi - Stage 1-
困ったな……どうしよう…….
ぱろっく先生がキャッシュカードをなくしてしまったショックからデータベースに逃げ込んでしまったんだ.
うーん,君SQL書くのうまそうだね.ちょっと僕もWeb問作らなきゃいけないから,連れ戻すのを任せてもいいかな?
多分,ぱろっく先生はそこまで深いところまで逃げ込んで居ないと思うんだけども…….とりあえず,逃げ込んだ先は以下のURLだよ.
一応,報酬としてフラグを用意してるからよろしくね!
https://ctf.spica.bz/baby_sql/
Caution
・sandbox.spica.bzの80,443番ポート以外への攻撃は絶対にしないようにお願いします.
・あなたが利用しているネットワークへの配慮のためhttpsでの通信を推奨します.
- テーブルは指定されているため、普通にSQLコマンドを打てばフラグが得られます。
$ select * from palloc_home
Q28.[Network] Can you login?
古くから存在するネットワークプロトコルを使った通信では、セキュリティを意識していなかったこともあり、様々な情報が暗号化されていないことが多い。そのため、パケットキャプチャをすることでその情報が簡単に見られてしまう可能性がある。
次のパケットを読んで、FLAGを探せ!network100.pcap
(2021/04/11: 問題ファイルを更新しました。)
- pcapファイルをWiresharkで開きます。
- ftp通信が多いためフィルタをかけると、ログインを試した形跡がすぐに見つかります。
- FTPは通信が暗号化されていないため、ログインに成功した際のユーザー名とパスワードを確認できます。
- ftpコマンドで、サーバーにログインします。
- MacOSではftpコマンドが利用できませんが、inetutilsをインストールすることで利用できるようになります。
$ brew install inetutils
$ ftp 118.27.110.77
> Name (118.27.110.77:root): cpaw_user
> Password: 5f4dcc3b5aa765d61d8327deb882cf99
> passive
> ls -a
> get .hidden_flag_file
> bye
- ダウンロードしたファイルを確認すると、フラグを見つけることができます。
$ cat .hidden_flag_file
Q23.[Reversing]またやらかした!
またprintf()をし忘れたプログラムが見つかった。
とある暗号を解くプログラムらしい…reversing200
-
stringsコマンドでファイルを見てみると、ライブラリのリンクや関数名、コンパイラ情報、ソースファイル名からC言語で書かれたプログラムであることは推測できますが、この情報だけでは動作を理解することは難しいので、バイナリファイルの分析にGhidraを使いました。
-
以下はMacにおけるGhidraのダウンロード手順です。
- Java jdkをダウンロードして、インストール
- Ghidraをダウンロード
- Zipファイル内にあるghidraRunを実行
- ./ghidraRun
- Mac標準アプリのAutomatorを使って、以下を実行するアプリを作っとけば次回から開くのが楽です。
- cd ファイルディレクトリ
- ./ghidraRun
-
以下がGhidraにおける分析手順です。
- File - New Project
- Non-Shared Project を作成
- File - Import File
- rev200 をインポート
- ファイルをダブルクリックして開く。
- 「分析しますか?」というポップアップが出てくる。- Yes
- 次のポップアップで「Analyze」を選択
- 左側のサイドバーに表示される「Symbol Tree」から「Functions - main」を探し、クリックすると、右側のサイドバーにデコンパイル結果が表示されます。
- File - New Project
-
コード解説
- local_7c に一連の整数値を代入
- これらの整数値は16進数で表されており、ASCIIコードに対応する文字を表しています
- local_7c の値を 0x19 とのXOR演算を行い、結果を local_44 に格納
- local_44 の各要素を文字として表示 - (追記が必要なコード。)
- local_7c に一連の整数値を代入
// オリジナルコード
undefined4 main(void)
{
int iVar1;
uint *puVar2;
int local_84;
uint local_7c [14];
uint local_44 [14];
local_7c[0] = 0x7a;
local_7c[1] = 0x69;
local_7c[2] = 0x78;
local_7c[3] = 0x6e;
local_7c[4] = 0x62;
local_7c[5] = 0x6f;
local_7c[6] = 0x7c;
local_7c[7] = 0x6b;
local_7c[8] = 0x77;
local_7c[9] = 0x78;
local_7c[10] = 0x74;
local_7c[11] = 0x38;
local_7c[12] = 0x38;
local_7c[13] = 100;
puVar2 = local_44;
for (iVar1 = 0xe; iVar1 != 0; iVar1 = iVar1 + -1) {
*puVar2 = 0;
puVar2 = puVar2 + 1;
}
for (local_84 = 0; local_84 < 0xe; local_84 = local_84 + 1) {
local_44[local_84] = local_7c[local_84] ^ 0x19;
}
return 0;
}
// 修正後コード
#include <stdio.h>
// undefined4 -> int main (整形)
int main(void)
{
// uint -> unsigned int (整形)
int iVar1;
unsigned int *puVar2;
int local_84;
unsigned int local_7c[14];
unsigned int local_44[14];
local_7c[0] = 0x7a;
local_7c[1] = 0x69;
local_7c[2] = 0x78;
local_7c[3] = 0x6e;
local_7c[4] = 0x62;
local_7c[5] = 0x6f;
local_7c[6] = 0x7c;
local_7c[7] = 0x6b;
local_7c[8] = 0x77;
local_7c[9] = 0x78;
local_7c[10] = 0x74;
local_7c[11] = 0x38;
local_7c[12] = 0x38;
local_7c[13] = 100;
puVar2 = local_44;
for (iVar1 = 0xe; iVar1 != 0; iVar1 = iVar1 - 1) {
*puVar2 = 0;
puVar2 = puVar2 + 1;
}
for (local_84 = 0; local_84 < 0xe; local_84 = local_84 + 1) {
local_44[local_84] = local_7c[local_84] ^ 0x19;
}
// local_44 の各要素を文字として表示させる (追加箇所)
printf("Flag: ");
for (local_84 = 0; local_84 < 0xe; local_84 = local_84 + 1) {
printf("%c", local_44[local_84]);
}
return 0;
}
- 実行すればフラグが得られます。
- gcc main.c -o main
- ./main
Q24.[Web]Baby's SQLi - Stage 2-
うーん,ぱろっく先生深くまで逃げ込んでたか.
そこまで難しくは無いと思うんだけども…….
えっ?何の話か分からない?
さてはStage 1をクリアしてないな.
待っているから,先にStage 1をクリアしてからもう一度来てね.Caution: sandbox.spica.bzの80,443番ポート以外への攻撃は絶対にしないようにお願いします.
- まず「Q22.[Web]Baby's SQLi - Stage 1-」をもう一度解き、得られた結果にURLがあります。
- ユーザー名は分かっているため、パスワードの検証を回避して情報を得る必要があるため、以下コマンドを使用します。
- 通常、ログイン処理ではユーザー名とパスワードが入力され、データベースクエリによってユーザー名とパスワードが一致するかどうかが確認されますが、「' OR '1'='1'」という常に真となる条件式を注入することで、データベースクエリの条件部分は無視され、通常のログイン処理における正当なパスワード検証がスキップされ、ログインせずにシステムにアクセスすることが可能になります。
' or '1'='1'--
Q26.[PPC]Remainder theorem
x ≡ 32134 (mod 1584891)
x ≡ 193127 (mod 3438478)
x = ?フラグはcpaw{xの値}です!
- "≡" は合同を表す記号であり、"mod" は剰余を表す記号であるため、1584891で割った余りが32134であり、3438478で割った余りが193127であるxを探す必要があります。
- Pythonのsympyライブラリを使用して剰余定理を適用するやり方もありますが、以下はシンプルに総当たりで数値を出したPythonコードです。
for i in range(0, 100000):
# 割り算の解の数値を総当たりで試して、xの数値を探す。
x = i * 1584891 + 32134
if x % 3438478 == 193127:
print(x)
break
- sympyライブラリを使用したやり方。
from sympy.ntheory.modular import solve_congruence
# 合同式のリストを作成
congruences = [(32134, 1584891), (193127, 3438478)]
# 剰余定理を使って解を求める
solution = solve_congruence(*congruences)
# 解を出力
print(solution)
Q29.[Crypto] Common World
Cpaw君は,以下の公開鍵を用いて暗号化された暗号文Cを受け取りました.しかしCpaw君は秘密鍵を忘れてしまいました.Cpaw君のために暗号文を解読してあげましょう.
(e, N) = (11, 236934049743116267137999082243372631809789567482083918717832642810097363305512293474568071369055296264199854438630820352634325357252399203160052660683745421710174826323192475870497319105418435646820494864987787286941817224659073497212768480618387152477878449603008187097148599534206055318807657902493850180695091646575878916531742076951110529004783428260456713315007812112632429296257313525506207087475539303737022587194108436132757979273391594299137176227924904126161234005321583720836733205639052615538054399452669637400105028428545751844036229657412844469034970807562336527158965779903175305550570647732255961850364080642984562893392375273054434538280546913977098212083374336482279710348958536764229803743404325258229707314844255917497531735251105389366176228741806064378293682890877558325834873371615135474627913981994123692172918524625407966731238257519603614744577)
暗号文: 80265690974140286785447882525076768851800986505783169077080797677035805215248640465159446426193422263912423067392651719120282968933314718780685629466284745121303594495759721471318134122366715904
これは…?フラグは以下のシンタックスです.
cpaw{復号した値}
※復号した値はそのままで良いですが,実は意味があります,余力がある人は考えてみてください.
- 公開鍵が (e, N) のペアであること、
- 非常に大きな数値であること、暗号文が数値の形式で与えられていることから推測するに、「RSA暗号化」であると考えられます。
- RSA暗号に対する攻撃方法として「Common Modulus Attack」があるのは分かったのですが、実装方法が分からなかったのでChatGPTにCommon Modulus Attackを実行可能なPythonコードを聞いて、数値を調整して実行すればフラグが得られました。
- 拡張ユークリッドの互除法(Extended Euclidean Algorithm)は、2つの整数の最大公約数を計算するアルゴリズムです。
- 共通のモジュラス(Common Modulus)とは、RSA暗号において複数の異なる公開指数を使用して暗号化されたメッセージを復号化する際に共有されるモジュラス(modulus)のことを指します。RSA暗号では、公開鍵は通常、公開指数とモジュラスのペア (e, N) で表されます。共通のモジュラスを使用する場合、異なる公開指数で暗号化されたメッセージを同じモジュラスで復号化することができます。これにより、共通のモジュラス攻撃(Common Modulus Attack)と呼ばれる攻撃手法が可能になります。
import math
# 拡張ユークリッドの互除法による最大公約数と拡張ユークリッドの互除法の結果を返す関数
def extended_gcd(a, b):
if b == 0:
return a, 1, 0
gcd, x, y = extended_gcd(b, a % b)
return gcd, y, x - (a // b) * y
# Common Modulus Attackを実行する関数
def common_modulus_attack(e1, e2, c1, c2, N):
# e1とe2が互いに素でない場合はエラーを発生させる
gcd, a, b = extended_gcd(e1, e2)
if gcd != 1:
raise ValueError("The given public exponents are not coprime.")
# c1とc2を復号化して平文m1とm2を得る
m1 = pow(c1, a, N)
m2 = pow(c2, b, N)
# 共通のモジュラスに対する平文を計算
m = (m1 * m2) % N
return m
# 公開鍵と暗号文の設定
e1 = 11
e2 = 13
# 公共のモジュラス
N = 236934049743116267137999082243372631809789567482083918717832642810097363305512293474568071369055296264199854438630820352634325357252399203160052660683745421710174826323192475870497319105418435646820494864987787286941817224659073497212768480618387152477878449603008187097148599534206055318807657902493850180695091646575878916531742076951110529004783428260456713315007812112632429296257313525506207087475539303737022587194108436132757979273391594299137176227924904126161234005321583720836733205639052615538054399452669637400105028428545751844036229657412844469034970807562336527158965779903175305550570647732255961850364080642984562893392375273054434538280546913977098212083374336482279710348958536764229803743404325258229707314844255917497531735251105389366176228741806064378293682890877558325834873371615135474627913981994123692172918524625407966731238257519603614744577
# 暗号文1
c1 = 80265690974140286785447882525076768851800986505783169077080797677035805215248640465159446426193422263912423067392651719120282968933314718780685629466284745121303594495759721471318134122366715904
# 暗号文2
c2 = 14451037575679461333658489727928902053807202350950440400755535465672646289383249206721118279217195146247129636809289035793102103248547070620691905918862697416910065303500798664102685376006097589955370023822867897020714767877873664
# Common Modulus Attackの実行
m = common_modulus_attack(e1, e2, c1, c2, N)
print("Decrypted message:", m)