回答編集履歴

2

当初の回答の一部を修正、.cfi_restoreについて追記

2019/05/08 15:22

投稿

dodox86
dodox86

スコア9183

test CHANGED
@@ -1,3 +1,7 @@
1
+ **回答の一部を修正し、追記しました:**2019/05/09 00:22
2
+
3
+
4
+
1
5
  > mainの中でバッファオーバーフローをしようとすると、EIPを書き換える直前でESPの書き換わるような挙動
2
6
 
3
7
  ...
@@ -6,7 +10,7 @@
6
10
 
7
11
 
8
12
 
9
- セキュリティの問題ではなく、bof.c の`main`関数で実装しているコードに問題があり意図する動きを阻害しています。逆アセンブルした結果で、`ret`直前のコードが
13
+ ~~セキュリティの問題ではなく、~~bof.c の`main`関数で~~実装しているコードに問題があり~~意図する動きを阻害しています。逆アセンブルした結果で、`ret`直前のコードが
10
14
 
11
15
  ```
12
16
 
@@ -284,7 +288,7 @@
284
288
 
285
289
  0x08048414 9 }
286
290
 
287
- (gdb) stepidisassemble
291
+ (gdb) disassemble
288
292
 
289
293
  Dump of assembler code for function main:
290
294
 
@@ -431,3 +435,153 @@
431
435
  (gdb) quit
432
436
 
433
437
  ```
438
+
439
+
440
+
441
+ ---
442
+
443
+ **以下、追記部分です:**2019/05/09 00:22
444
+
445
+
446
+
447
+ > mainの中でバッファオーバーフローをしようとすると、EIPを書き換える直前でESPの値が書き換わるような挙動を起こしてしまうためEIPを書き換えられません。
448
+
449
+
450
+
451
+ 追って調べたところ、言葉のとおりでした。`main`関数に限って、`ret`直前に `lea esp, [ecx-4]`のコードが入る場合と入らない場合があります。質問者であるkonataroさんのC言語コードでは入ってしまい、拙作の例示用C言語コードでは入りません。その為、動きに差が出ています。
452
+
453
+
454
+
455
+ bof.c を"-S"オプション付きでコンパイルしてアセンブリ言語ソースで出力すると、`main`関数の`ret`命令付近のエピローグコードは、以下のようになります。注目すべきコードである`leal -4(%ecx), %esp`が入っています。(質問者のkonataroさんが既に提示されたものとほぼ等価なものですが、説明の為に掲げています)
456
+
457
+ ```
458
+
459
+ call strcpy
460
+
461
+ addl $16, %esp
462
+
463
+ .loc 1 13 0
464
+
465
+ movl $0, %eax
466
+
467
+ .loc 1 14 0
468
+
469
+ movl -4(%ebp), %ecx
470
+
471
+ .cfi_def_cfa 1, 0
472
+
473
+ leave
474
+
475
+ .cfi_restore 5
476
+
477
+ leal -4(%ecx), %esp
478
+
479
+ .cfi_def_cfa 4, 4
480
+
481
+ ret
482
+
483
+ .cfi_endproc
484
+
485
+ ```
486
+
487
+ これをkonataroさんが既に指摘されているように、`main`ではなく、普通の関数`func`に変えてコンパイルすると、
488
+
489
+ ```C
490
+
491
+ //bof.c
492
+
493
+ #include<stdio.h>
494
+
495
+ #include<string.h>
496
+
497
+
498
+
499
+ char buffer[32];
500
+
501
+
502
+
503
+ int func(int argc, char *argv[])
504
+
505
+ {
506
+
507
+ char local[32];
508
+
509
+ printf("buffer 0x%x\n", &buffer);
510
+
511
+ fgets(local, 128, stdin);
512
+
513
+ strcpy(buffer,local);
514
+
515
+ return 0;
516
+
517
+ }
518
+
519
+
520
+
521
+ int main(int argc, char *argv[])
522
+
523
+ {
524
+
525
+ return func(argc, argv);
526
+
527
+ }
528
+
529
+ ```
530
+
531
+ アセンブリ言語のソースは、以下のように変わります。`leal -4(%ecx), %esp`は消滅しています。
532
+
533
+ ```
534
+
535
+ call strcpy
536
+
537
+ addl $16, %esp
538
+
539
+ .loc 1 13 0
540
+
541
+ movl $0, %eax
542
+
543
+ .loc 1 14 0
544
+
545
+ leave
546
+
547
+ .cfi_restore 5
548
+
549
+ .cfi_def_cfa 4, 4
550
+
551
+ ret
552
+
553
+ .cfi_endproc
554
+
555
+ ```
556
+
557
+
558
+
559
+ `.cfi_restore`とは何か?をWEBで検索してみると以下のas(gas)のドキュメントがヒットし、
560
+
561
+ [7.10 CFI directives](https://sourceware.org/binutils/docs/as/CFI-directives.html)
562
+
563
+
564
+
565
+ 内容として下の説明がありました。
566
+
567
+ > 7.10.17 .cfi_restore register
568
+
569
+ > .cfi_restore says that the rule for register is now the same as it was at the beginning of the function, after all initial instruction added by .cfi_startproc were executed.
570
+
571
+
572
+
573
+ どうも、このディレクティブで関数先頭でのレジスターの値と同じようにすることを指示するもののようで、`main`内で関数を呼び出すコードを書くと`leal -4(%ecx), %esp`のコードが挟み込まれます。拙作の`main`の例示コードでは関数を呼び出さない為、目的を達成できるコードが生成できたようです。あくまで勝手な推測ですが、SSPなどの仕組みの一部で、`main`を呼び出したCランタイムのスタートアップルーチンに正しく戻れるよう保証する動作の一端でしょうか。少し検索した限りでは、なぜそうなっているかを説明しているgccやbinutilのドキュメントは見つけることはできませんでした。この辺り、gcc周りの事情にお詳しい方にフォローや突っ込みをお願いしたいところです。
574
+
575
+
576
+
577
+ ですので、少なくとも本質問の内容の限りにおいては、gccに"-fno-stack-protector"オプションを追加した上で、
578
+
579
+ - main関数以外の関数内に目的のコードを書く。
580
+
581
+ - main関数内に書くときは、標準関数を含む他の関数を呼び出さない。
582
+
583
+ ようにすると所望の動作をするコードになるので、それを踏まえてコードを書く必要があると考えます。
584
+
585
+
586
+
587
+ 私自身の参考として - [IPA - 第10章 著名な脆弱性対策 - バッファオーバーフロー: #5 運用環境における防御](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c905.html)

1

記述を一部修正、ソースコード中にコメント追加

2019/05/08 15:22

投稿

dodox86
dodox86

スコア9183

test CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
 
8
8
 
9
- セキュリティの問題ではなく、bof.c の`main`関数で実装しているコードに問題があり意図する動きを阻害しているだけです。逆アセンブルした結果で、`ret`直前のコードが
9
+ セキュリティの問題ではなく、bof.c の`main`関数で実装しているコードに問題があり意図する動きを阻害していす。逆アセンブルした結果で、`ret`直前のコードが
10
10
 
11
11
  ```
12
12
 
@@ -26,7 +26,7 @@
26
26
 
27
27
  /* t7.c simple bof */
28
28
 
29
- int i;
29
+ int i; /* ※ループ実行時に破壊されないよう、static領域に配置*/
30
30
 
31
31
  int main(int argc, char *argv[]) {
32
32
 
@@ -114,7 +114,7 @@
114
114
 
115
115
  ```
116
116
 
117
- breakした時点でスタックが`0x41`で上書きされているので、`argc`や`argv`が0x41...に書き換わっています。
117
+ ブレイクした時点でスタックが`0x41`で上書きされているので、`argc`や`argv`が0x41...に書き換わっていることも分かります。
118
118
 
119
119
  ```
120
120