exploit(続2)

id:MaD:20070209の続きです。
自作のバグありプログラムにexploitを仕掛けるということをしていたわけですが、アセンブリを見れば全て解決でした。

crackmeのmain関数の最初と最後で行われている処理のアセンブリを以下に載せます。
ここでは次のような処理が行われていました。

  1. %ecx = %esp + 4
  2. %ecxをスタックにpush
  3. 中略
  4. 2の値をスタックから%ecxにpop
  5. %esp = %ecx - 4

ようするに、一番最初にスタックポインタの値(+4)を保存しておいて、最後にその値を取り出してスタックポインタの値を元に戻しているわけです。
バッファオーバーフローが起こるとこのスタック上にある%ecxが書き換わってしまう為に%espを元に戻せなくなる為segmentation faultとなるのだと判明しました。

main:
	leal	4(%esp), %ecx   // %ecx = %esp + 4
	andl	$-16, %esp     // %espの下位4ビットを0に
	pushl	-4(%ecx)       // 本来の%esp(%ecx -4)の値をプッシュ
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%edi
	pushl	%esi
	pushl	%ecx
	subl	$140, %esp
	movl	%ecx, -128(%ebp)
.. 中略 ..
	movl	$0, %eax
	addl	$140, %esp
	popl	%ecx
	popl	%esi
	popl	%edi
	popl	%ebp
	//leal	-4(%ecx), %esp  // %esp = %ecx - 4
	ret

んで、本来このように%espの値をいじる必要性はないはずです。(どうなんでしょう?)
試しに一番最後のlealだけをコメントアウトしてみました。これでもプログラムはちゃんと動くはずです。

% gcc crackme.s -o crackme.s
% exploit 0
password is incorrect.
% echo $?
100

あっさり通りました。ということで、この行の存在の為にexploitが仕掛けられないということが判明。
ちょっとこれだとexploitを仕掛けるのは大変ですね。%ecxの存在するスタック上の位置とその値を正確に割り出して、そこだけ書換えないようにする必要性があります。(文字列上にこの4バイトを仕込む)
何の為にスタックポインタを操作しているんでしょうか??単にexploitを防ぐ為ではないような気がするのですがどうなんでしょう?

他のコンパイラだとどうなのか興味がありますね。BCCとか。

関数ポインタの書き換えによるexploit

こっちはめちゃめちゃ簡単。
こんなコードだとあっというまにexploitできちゃいます。やっぱりバッファオーバーフローは危険なので注意ですね。

#include <stdio.h>
#include <string.h>

void foo(const char* s){
  printf("%s\n", s);
  return;
}

int main(int argc, char *argv[])
{
  char buf[10];
  void (*func)(const char*);

  func = foo;
  strcpy(buf, argv[1]);
  foo(argv[1]);
}