回答編集履歴
3
コメントに対して回答追加
test
CHANGED
@@ -72,4 +72,90 @@
|
|
72
72
|
|
73
73
|
|
74
74
|
|
75
|
+
---
|
76
|
+
|
77
|
+
> "Hello World!"を出力するプログラムを作ったとして
|
78
|
+
|
79
|
+
> コンパイルした機械語をアレンジする必要があるのですか?
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
「〜を出力するプログラム」は、自分自身の、通常のメモリ配置で動作するが、
|
84
|
+
|
85
|
+
シェルコードは他人(攻撃対象)が動作するメモリアドレスで動作しなければならない。プログラムコード領域(コードセグメント?)で動作する想定でコンパイルされるコードが、攻撃対象のスタック領域(スタックセグメント?)で動作することになる(のだよね?)。
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
つまり動作環境(メモリアドレス)がガラっと変わる事を忘れてはいけない。コードの配置場所が違ってしまうので、関数呼出し等ができなくなったり、文字列のようなデータの在り処(メモリアドレス)が変わってしまうという事。だからアレンジが必要になる。
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
結論的に言えそうな事は、**シェルコード中の命令で、オペランドにメモリアドレスが現れる命令が要注意**だと思う。私が示したコードでは、次の4行。
|
94
|
+
|
95
|
+
- movl $shcode-8, %esp
|
96
|
+
|
97
|
+
- movl $msg,(%esp)
|
98
|
+
|
99
|
+
- call _printf
|
100
|
+
|
101
|
+
- .long shcode
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
一方、[シェルコード書いてみた](https://qiita.com/slowsingle/items/59c139b747edec9157cc)のアセンブリコード connect.s には要注意な命令は無い。connect.s をアセンブルして得られた機械語はそのまま利用可能と思われる。その違いをまとめると次のようになる。
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
- スタックポインタを変更していない("movl $shcode-8,%esp" 相当の操作無し)
|
110
|
+
|
111
|
+
- 文字列 "/bin//sh" をそのままpushしている("movl $msg,(%esp)" に相当)
|
112
|
+
|
113
|
+
- syscall 命令を使う("call _printf" ではなく)
|
114
|
+
|
115
|
+
- リターンアドレスを上書きする値を含んでいない(".long shcode" が無い)
|
116
|
+
|
117
|
+
|
118
|
+
|
75
|
-
|
119
|
+
文字列をpushする部分はこう。
|
120
|
+
|
121
|
+
|
122
|
+
|
123
|
+
```gas
|
124
|
+
|
125
|
+
xor rcx, rcx /* rcx := 0 */
|
126
|
+
|
127
|
+
push rcx /* push した 0 が '\0' の代り */
|
128
|
+
|
129
|
+
mov rax, 0x68732f2f6e69622f /* == "/bin//sh" */
|
130
|
+
|
131
|
+
push rax /* スタックに 8 文字を push */
|
132
|
+
|
133
|
+
mov rdi, rsp /* その時の rsp が文字列先頭アドレス */
|
134
|
+
|
135
|
+
```
|
136
|
+
|
137
|
+
スタックに文字列の先頭アドレスを push するのではなく、8文字の文字列(8文字==64bit)そのものを直接 push するのがミソで、**push 直後のスタックポインタ rsp の値が文字列先頭アドレスになる**。このようなコードはスタック領域が何番地でも問題無く動くからアレンジは不要。
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
printf() を使わず、syscall を使うことは大きい。
|
142
|
+
|
143
|
+
まず、printf() 関数のアドレスを調べる必要が無い。そして、syscall で出力するなら、スタックポインタの変更(movl $shcode-8,%esp)をしなくても良い可能性が高い。
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
printf() は関数だから、その実行にはそれなりのスタック領域が必要だが、何バイト必要なのか簡単にはわからない。そこで私のコードは安全策をとって、スタックポインタを変更した。
|
148
|
+
|
149
|
+
一方、syscall 命令は、その時点でOS内部のコードへ処理が遷移すると同時に、スタックポインタがOS用のスタック領域に切り替わる。そのため、こちらのコードに必要なスタックサイズは少量で済み、かつ見積りが可能になる(=push するバイト数を数えれば良い)。
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
文字列をpushする事とsyscall命令を使う事は、メモリアドレスが現れる命令をなるべく使わないための、標準的な工夫と言えるだろう。
|
154
|
+
|
155
|
+
|
156
|
+
|
157
|
+
いずれにしても、(攻撃対象プロセスの)スタック領域を、どう使おうとしているか、目に見えていないことには、何が問題かも理解できないと思う。一命令ごとに動作をトレースできないようでは(略)。
|
158
|
+
|
159
|
+
|
160
|
+
|
161
|
+
ところで、リンクのページでは 167 バイトのシェルコードを得ている。しかし、リターンアドレスを上書きする値も、バッファを溢れさせる nop 命令列も無いので、**そのままでは攻撃できない**事に気づいているだろうか。結局、その2つを付け足すアレンジは最低限要るはずだ。
|
2
オーバーフローをオーバーランに変更
test
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
アレンジ可能かどうか具体的なアドバイスは可能だと思う、と書いた手前、回答してみる。
|
2
2
|
|
3
|
-
- 攻撃対象のプログラムは scanf() 等で、スタック上の文字配列(ローカル変数)にシェルコードを読み込み、バッファオーバー
|
3
|
+
- 攻撃対象のプログラムは scanf() 等で、スタック上の文字配列(ローカル変数)にシェルコードを読み込み、バッファオーバーランを起こす
|
4
4
|
|
5
5
|
- 攻撃対象プログラムの文字配列の先頭アドレスがわかる
|
6
6
|
|
1
leal 命令を movl 命令に修正
test
CHANGED
@@ -24,7 +24,7 @@
|
|
24
24
|
|
25
25
|
shcode:
|
26
26
|
|
27
|
-
l
|
27
|
+
movl $shcode-8, %esp # スタックポインタを調整
|
28
28
|
|
29
29
|
movl $msg,(%esp)
|
30
30
|
|