回答編集履歴

3

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

2019/11/08 15:06

投稿

rubato6809
rubato6809

スコア1382

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

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

2019/11/08 15:06

投稿

rubato6809
rubato6809

スコア1382

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

1

leal 命令を movl 命令に修正

2019/10/27 09:56

投稿

rubato6809
rubato6809

スコア1382

test CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  shcode:
26
26
 
27
- leal $shcode-8, %esp # スタックポインタを調整
27
+ movl $shcode-8, %esp # スタックポインタを調整
28
28
 
29
29
  movl $msg,(%esp)
30
30