Skip to content
KitploitKITPLOIT
ツールブログ
Log in
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2021-4034-CTF-writeup — CTF pwnチャレンジの書き起こしで、CVE-2021-4034 (pkexec) をヒープ操作で悪用し、Ghidraベースのリバースエンジニアリングとカスタムのshelly.soヘルパーバイナリを使用しています。 | Kitploit
ツール/GitHubGitHub/wechicken456/cve-2021-4034-ctf-writeup
エクスプロイトリバースエンジニアリングCTF学習と教育バイナリエクスプロイトラボと実践
GitHubwechicken456/cve-2021-4034-ctf-writeup

CVE-2021-4034-CTF-writeup

CTF pwnチャレンジの書き起こしで、CVE-2021-4034 (pkexec) をヒープ操作で悪用し、Ghidraベースのリバースエンジニアリングとカスタムのshelly.soヘルパーバイナリを使用しています。

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
リポジトリを見る
2112年前未レビュー
共有

CVE-2021-4034-CTF-writeup

これは、私がCで書いたCTFのpwnチャレンジであり、ユーザーがCVE-2021-4034の脆弱性を悪用する必要があります。プレイヤーには、このリポジトリのchallengeディレクトリにある2つのバイナリが与えられます。chalバイナリはCTFチャレンジを実装し、shelly.soは補助バイナリです。

このチャレンジのエミュレート方法

このwriteupを作成した時点では、Dockerfileはまだ完成していません。ライブCTF中にこのチャレンジをデプロイするにはDockerfileが必要ですが、ローカルでは不要です。以下のようにユーザー権限を設定し、脆弱なパッケージをインストールすることで、ローカルでこのチャレンジをエミュレートできます。

  1. この脆弱性を正常に悪用するには、Linuxマシンを使用していないプレイヤーはまずLinux VMをインストールする必要があります。次に、脆弱なカーネルをインストールする必要があります。その方法についてはこちら: [https://askubuntu.com/a/700221]
  2. パッケージ libpolkit-gobject-1-0=0.105-26ubuntu1 libpolkit-agent-1-0=0.105-26ubuntu1 policykit-1=0.105-26ubuntu1 をインストールします。
  3. カレントディレクトリに root:root が所有する flag.txt ファイルを作成します。
  4. 権限のないユーザーを作成します。このユーザーに切り替えます。
  5. challenge フォルダ内のファイルをカレントディレクトリにダウンロードします。
  6. chal を権限のないユーザーとして実行します。

ブラインド分析

chal バイナリを実行すると、このバイナリが何をするのかが漠然とわかります。``` WELCOME TO THE HUB CTRL+ALT+DELICIOUS We're not just a sandwich hub. We are the beacon of flavors, serving a symphony in every byte

  1. ENTER THE HUB
  2. QUIT 1 Order number: 0x7ffde93681f0 Enter your name: tin
  3. ADD NEW ORDER
  4. EDIT ORDER
  5. SHOW ORDER
  6. CANCEL ORDER
  7. CHECKOUT
  8. DONE 1 Pick your bread: aaaa Select your spread: bbbb Choose your veg: cccc Slam your meat & egg: dddc Any side notes for the cook? 0000
  9. ADD NEW ORDER
  10. EDIT ORDER
  11. SHOW ORDER
  12. CANCEL ORDER
  13. CHECKOUT
  14. DONE 1 Pick your bread: AAAA Select your spread: BBBB Choose your veg: CCCC Slam your meat & egg: DDDD Any side notes for the cook? 1111
  15. ADD NEW ORDER
  16. EDIT ORDER
  17. SHOW ORDER
  18. CANCEL ORDER
  19. CHECKOUT
  20. DONE 3 Enter order index: 0 aaaa, bbbb, cccc, dddc, 0000
  21. ADD NEW ORDER
  22. EDIT ORDER
  23. SHOW ORDER
  24. CANCEL ORDER
  25. CHECKOUT
  26. DONE 3 Enter order index: 1 AAAA, BBBB, CCCC, DDDD, 1111
  27. ADD NEW ORDER
  28. EDIT ORDER
  29. SHOW ORDER
  30. CANCEL ORDER
  31. CHECKOUT
  32. DONE 4 Enter order index: 1
  33. ADD NEW ORDER
  34. EDIT ORDER
  35. SHOW ORDER
  36. CANCEL ORDER
  37. CHECKOUT
  38. DONE 3 Enter order index: 1 Invalid index!
  39. ADD NEW ORDER
  40. EDIT ORDER
  41. SHOW ORDER
  42. CANCEL ORDER
  43. CHECKOUT
  44. DONE 5 /notes ./tin/notes
  45. ADD NEW ORDER
  46. EDIT ORDER
  47. SHOW ORDER
  48. CANCEL ORDER
  49. CHECKOUT
  50. DONE 6
  51. ENTER THE HUB
  52. QUIT 2 Come again :)
`Add` 関数の入力サイズや `Cancel` 関数のインデックスをいろいろ試しても、特別なことは何も起きません(オーバーフローもセグメンテーションフォールトも発生しません)。ただし、いくつか興味深い点があります:
  - 注文はリスト(おそらく連結リスト)に格納されているように見え、0から始まるインデックスが付いている?
  - `Order number: 0x7ffde93681f0` という出力があり、これはスタック上のどこかのアドレスを表示しているように見える?
  - また、プログラムは入力された名前のディレクトリを作成し、その中に3つの実行ファイルを生成します。そのうちの1つが、私たちに与えられたヘルパーバイナリファイルです:```sh
peasant@Tin-VM:~/Desktop$ ls
chal  chal.c  Dockerfile shelly.so  solve.py  tin
peasant@Tin-VM:~/Desktop$ ls -l tin/
total 28
-rwxrwx--- 1 peasant vboxsf     5 Feb  4 14:33 notes
-rwxrwx--- 1 peasant vboxsf    20 Feb  4 14:33 recipe
-rwxr-x--- 1 peasant vboxsf 16488 Feb  4 14:33 shelly.so
peasant@Tin-VM:~/Desktop$ cat tin/notes 
0000
peasant@Tin-VM:~/Desktop$ cat tin/recipe 
aaaa
bbbb
cccc
dddc

実行可能ファイルには入力が含まれています。


Ghidra解析

他の関数をいじってバグに遭遇することを期待することもできますが(可能性は高いですが)、本題に入り、Ghidraでプログラムを開きます。

プログラムの実行中に表示される文字列とGhidraに存在する文字列を比較することで、一部のFUN_*関数を馴染みのある名前に変更できます:```C undefined8 main(void)

{ int iVar1; size_t sVar2; undefined2 *puVar3; long in_FS_OFFSET; int opt; int local_1c; char *local_18; long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28); local_18 = "/recipe"; print("WELCOME TO THE HUB CTRL+ALT+DELICIOUS\n"); print( "We're not just a sandwich hub. We are the beacon of flavors, serving a symphony in every by te\n\n" ); while( true ) { print("1. ENTER THE HUB\n"); print("2. QUIT\n"); __isoc99_scanf(&DAT_001030c5,&opt); getc(stdin); if (opt != 1) break; printf("Order number: %p\n",&local_18); print("Enter your name: "); __isoc99_scanf(&DAT_0010334f,&DAT_00105120); sVar2 = strlen(&DAT_00105120); puVar3 = (undefined2 *)malloc(sVar2 + 2); DAT_00105100 = puVar3; *puVar3 = 0x2f2e; *(undefined *)(puVar3 + 1) = 0; strcpy((char *)(DAT_00105100 + 1),&DAT_00105120); iVar1 = FUN_001022f0(DAT_00105100,&DAT_00105060); if (iVar1 == -1) { mkdir((char *)DAT_00105100,0x1c0); } DAT_00105140 = 0; order_cnt = 0; for (local_1c = 0; local_1c < 10; local_1c = local_1c + 1) { *(undefined8 *)(&ptr_array + (long)local_1c * 8) = 0; } main_menu(); } print("Come again :)\n");

`printf("Order number: %p\n",&local_18);` **はスタック上のローカル変数の位置を出力します。**

gdbでの簡単な確認により、漏洩したアドレスが定数文字列 `/recipe` へのポインタのアドレスであることがわかります。```gdb
...
Order number: 0x7fffffffdfc0
...
gef➤  x/gx 0x7fffffffdfc0
0x7fffffffdfc0:	0x0000555555559020
gef➤  x/s 0x0000555555559020
0x555555559020:	"/recipe"

mkdir の呼び出しが見られます。これは、現在のディレクトリに入力した名前のディレクトリを作成します。

これらはプログラムを実行したときの観測と一致しています。次に、いくつかの変数を初期化してから main_menu 関数を呼び出しています。これは次のようになります:```C while( true ) { while( true ) { while( true ) { while( true ) { while( true ) { while( true ) { print("1. ADD NEW ORDER\n"); print("2. EDIT ORDER\n"); print("3. SHOW ORDER\n"); print("4. CANCEL ORDER\n"); print("5. CHECKOUT\n"); print("6. DONE\n"); __isoc99_scanf(&DAT_001030c5,&local_40); getc(stdin); if (local_40 != 1) break; add_order(); } if (local_40 != 2) break; edit_order(); } if (local_40 != 3) break; show_order(); } if (local_40 != 4) break; cancel_order(); } if (local_40 != 5) break; checkout(); } if (local_40 == 6) break; if (local_40 == 0x539) { print( "\nGORDON RAMSAY: Finally, a worthy opponent, our battle will be legendary! I BET YOU CAN 'T GUESS THE SECRET RECIPE.\n" ); fgets(inp,0x20,stdin); getrandom(random-bytes,0x10,0); for (local_3c = 0; local_3c < 0x10; local_3c = local_3c + 1) { if (inp[local_3c] != random-bytes[local_3c]) { print("...Nuh Uh!...\n"); /* WARNING: Subroutine does not return */ exit(0); } print("...Ooh Yes.. sCruMpTioUs..."); } print("Fine... I'll give you a taste.\n"); FUN_00101504(); }

REVに詳しくない場合、これはC言語の`switch`文の逆コンパイル結果です。興味深いオプションとして `0x539` があります。これによりプレイヤーは `0x10` のランダムなバイトを推測できます。すべてのバイトが一致すると、`FUN_00101504();` が呼び出され、`system("cat flag.txt");` が実行されます。それ以外の場合はプログラムは終了します。

しかし、16個のランダムなバイトをブルートフォースするには、すべての 256^16 = 340282366920938463463374607431768211456 通りの可能性を試す必要があります。これを突破するのはお疲れ様です(笑)。
ツールをダウンロード