teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

3

コメントに対して回答追加

2019/11/08 15:06

投稿

rubato6809
rubato6809

スコア1382

answer CHANGED
@@ -35,4 +35,47 @@
35
35
 
36
36
  ただし、これを**アセンブルしただけでは、目的のシェルコードは得られない**。アセンブルさせてみた上で、バイナリエディタなどでファイル化するのが、愚直だが確実な方法だと思う。アセンブラ・リンカなど、正規の使い方で作れるものではないのだから工夫が要る。
37
37
 
38
+ ---
39
+ > "Hello World!"を出力するプログラムを作ったとして
40
+ > コンパイルした機械語をアレンジする必要があるのですか?
41
+
42
+ 「〜を出力するプログラム」は、自分自身の、通常のメモリ配置で動作するが、
43
+ シェルコードは他人(攻撃対象)が動作するメモリアドレスで動作しなければならない。プログラムコード領域(コードセグメント?)で動作する想定でコンパイルされるコードが、攻撃対象のスタック領域(スタックセグメント?)で動作することになる(のだよね?)。
44
+
45
+ つまり動作環境(メモリアドレス)がガラっと変わる事を忘れてはいけない。コードの配置場所が違ってしまうので、関数呼出し等ができなくなったり、文字列のようなデータの在り処(メモリアドレス)が変わってしまうという事。だからアレンジが必要になる。
46
+
47
+ 結論的に言えそうな事は、**シェルコード中の命令で、オペランドにメモリアドレスが現れる命令が要注意**だと思う。私が示したコードでは、次の4行。
48
+ - movl $shcode-8, %esp
49
+ - movl $msg,(%esp)
50
+ - call _printf
51
+ - .long shcode
52
+
53
+ 一方、[シェルコード書いてみた](https://qiita.com/slowsingle/items/59c139b747edec9157cc)のアセンブリコード connect.s には要注意な命令は無い。connect.s をアセンブルして得られた機械語はそのまま利用可能と思われる。その違いをまとめると次のようになる。
54
+
55
+ - スタックポインタを変更していない("movl $shcode-8,%esp" 相当の操作無し)
56
+ - 文字列 "/bin//sh" をそのままpushしている("movl $msg,(%esp)" に相当)
57
+ - syscall 命令を使う("call _printf" ではなく)
58
+ - リターンアドレスを上書きする値を含んでいない(".long shcode" が無い)
59
+
38
- 以上検討した上で、ご感想をど
60
+ 文字列pushする部分はこう。
61
+
62
+ ```gas
63
+ xor rcx, rcx /* rcx := 0 */
64
+ push rcx /* push した 0 が '\0' の代り */
65
+ mov rax, 0x68732f2f6e69622f /* == "/bin//sh" */
66
+ push rax /* スタックに 8 文字を push */
67
+ mov rdi, rsp /* その時の rsp が文字列先頭アドレス */
68
+ ```
69
+ スタックに文字列の先頭アドレスを push するのではなく、8文字の文字列(8文字==64bit)そのものを直接 push するのがミソで、**push 直後のスタックポインタ rsp の値が文字列先頭アドレスになる**。このようなコードはスタック領域が何番地でも問題無く動くからアレンジは不要。
70
+
71
+ printf() を使わず、syscall を使うことは大きい。
72
+ まず、printf() 関数のアドレスを調べる必要が無い。そして、syscall で出力するなら、スタックポインタの変更(movl $shcode-8,%esp)をしなくても良い可能性が高い。
73
+
74
+ printf() は関数だから、その実行にはそれなりのスタック領域が必要だが、何バイト必要なのか簡単にはわからない。そこで私のコードは安全策をとって、スタックポインタを変更した。
75
+ 一方、syscall 命令は、その時点でOS内部のコードへ処理が遷移すると同時に、スタックポインタがOS用のスタック領域に切り替わる。そのため、こちらのコードに必要なスタックサイズは少量で済み、かつ見積りが可能になる(=push するバイト数を数えれば良い)。
76
+
77
+ 文字列をpushする事とsyscall命令を使う事は、メモリアドレスが現れる命令をなるべく使わないための、標準的な工夫と言えるだろう。
78
+
79
+ いずれにしても、(攻撃対象プロセスの)スタック領域を、どう使おうとしているか、目に見えていないことには、何が問題かも理解できないと思う。一命令ごとに動作をトレースできないようでは(略)。
80
+
81
+ ところで、リンクのページでは 167 バイトのシェルコードを得ている。しかし、リターンアドレスを上書きする値も、バッファを溢れさせる nop 命令列も無いので、**そのままでは攻撃できない**事に気づいているだろうか。結局、その2つを付け足すアレンジは最低限要るはずだ。

2

オーバーフローをオーバーランに変更

2019/11/08 15:06

投稿

rubato6809
rubato6809

スコア1382

answer CHANGED
@@ -1,5 +1,5 @@
1
1
  アレンジ可能かどうか具体的なアドバイスは可能だと思う、と書いた手前、回答してみる。
2
- - 攻撃対象のプログラムは scanf() 等で、スタック上の文字配列(ローカル変数)にシェルコードを読み込み、バッファオーバーフローを起こす
2
+ - 攻撃対象のプログラムは scanf() 等で、スタック上の文字配列(ローカル変数)にシェルコードを読み込み、バッファオーバーランを起こす
3
3
  - 攻撃対象プログラムの文字配列の先頭アドレスがわかる
4
4
  - 攻撃対象プログラムの_printf のアドレスがわかる
5
5
  - 上記の printf() を呼ぶパターン

1

leal 命令を movl 命令に修正

2019/10/27 09:56

投稿

rubato6809
rubato6809

スコア1382

answer CHANGED
@@ -11,7 +11,7 @@
11
11
  さらに切り詰めれば、おおよそ次のようなコードがあれば良いのではないか。
12
12
  ```gas
13
13
  shcode:
14
- leal $shcode-8, %esp # スタックポインタを調整
14
+ movl $shcode-8, %esp # スタックポインタを調整
15
15
  movl $msg,(%esp)
16
16
  call _printf # printf("Hello World!\n") ;
17
17
  ret # 表示後、どうすれば良い?