cowrieのセットアップメモ

cowrieはkippoを改良したSSHハニーポットである.
ログをMySQL(mariaDB)に保存するまでのメモ.
基本的にINSTALL.mdを読んでいけばいい.

cowrieはroot権限を持たないユーザーで動かすことを推奨されている.
まずはユーザーの作成から.

$ sudo adduser --disabled-password cowrie
$ sudo su - cowrie
$ git clone http://github.com/micheloosterhof/cowrie
$ cd cowrie
$ cp cowrie.cfg.dist cowrie.cfg

とりあえずこれでstart.shを実行すれば2222/tcpでcowrieが待ち受ける.
待ち受けるポートを変更したい場合はcowrie.cfgの該当箇所を編集すればいい.
また, 22/tcpで待ち受けたい場合root権限が必要とされるが, root権限でハニーポットを動かすことは推奨されていない行為である.
そのため, ipatblesなどでポートフォワーディングしてあげるといい.

$ sudo iptables -t nat -A PREROUTING -p tcp --dport 22 -j REDIRECT --to-port 2222

続いて, ログをMySQLに吐き出すように設定を行う.
まずは, ユーザーの権限などの設定.

$ mysql -u root -p
$ CREATE USER 'cowrie'@'localhost' IDENTIFIED BY 'PASSWORD';
$ GRANT ALL PRIVILEGES ON *.* TO cowrie@localhost IDENTIFIED BY 'PASSWORD';
$ create database cowrie;

続いて, cowrie.cfgの編集.

[database_mysql]
host = localhost
database = cowrie
username = cowrie
password = PASSWORD
port = 3306

最後にテーブルを作成する.

$ cd doc/sql/
$ cat mysql.sql | mysql -u cowrie  -p -D cowrie

また, cowrie.cfgのデフォルト設定, 特にhostnameあたりは変更しておいた方がいい.

ログなどはlog/以下に保存され, log/tty/以下に侵入者が入力したコマンドを再現できるファイルがある.
このあたりはkippoと同じなので, utils/playlog.py log/tty/hoge.log で再生できる.

アセンブリメモ -スタック-

関数呼び出し時のスタックの動きなどのメモ.

#include <stdio.h>

int func(int a, int b, int c, int d){
   int y = 20;
   char buf[10] = "ABCD";
   return a+b+c+d;
}

int main(){
   int x = 10;
   func(1, 2, 3, 4);
}

main()の逆アセンブル結果

   0x08048400 <+0>:  push   %ebp
   0x08048401 <+1>:  mov    %esp,%ebp
   0x08048403 <+3>:  sub    $0x10,%esp
   0x08048406 <+6>:  movl   $0xa,-0x4(%ebp)
   0x0804840d <+13>: push   $0x4
   0x0804840f <+15>: push   $0x3
   0x08048411 <+17>: push   $0x2
   0x08048413 <+19>: push   $0x1
   0x08048415 <+21>: call   0x80483cb <func>
   0x0804841a <+26>: add    $0x10,%esp
   0x0804841d <+29>: leave  
   0x0804841e <+30>: ret    

func()の逆アセンブル結果

   0x080483cb <+0>:  push   %ebp
   0x080483cc <+1>:  mov    %esp,%ebp
   0x080483ce <+3>:  sub    $0x10,%esp
   0x080483d1 <+6>:  movl   $0x14,-0x4(%ebp)
   0x080483d8 <+13>: movl   $0x44434241,-0xe(%ebp)
   0x080483df <+20>: movl   $0x0,-0xa(%ebp)
   0x080483e6 <+27>: movw   $0x0,-0x6(%ebp)
   0x080483ec <+33>: mov    0x8(%ebp),%edx
   0x080483ef <+36>: mov    0xc(%ebp),%eax
   0x080483f2 <+39>: add    %eax,%edx
   0x080483f4 <+41>: mov    0x10(%ebp),%eax
   0x080483f7 <+44>: add    %eax,%edx
   0x080483f9 <+46>: mov    0x14(%ebp),%eax
   0x080483fc <+49>: add    %edx,%eax
   0x080483fe <+51>: leave  

先頭のpush, mov, subといった一連の命令が共通していることが分かる.
これは"関数プロローグ"などと呼ばれ, ebpをスタック上に退避させ, その関数で使用する局所変数のメモリをスタック上に確保する処理である.

main()から順に処理を見ていく.
関数プロローグの処理はfunc()で詳しく追いかけるとし, 0x0804840dからのpush命令から見ていく.
callで関数を呼び出す際はスタックに引数をpushしていく.

   0x0804840d <+13>: push   $0x4
   0x0804840f <+15>: push   $0x3
   0x08048411 <+17>: push   $0x2
   0x08048413 <+19>: push   $0x1
   0x08048415 <+21>: call   0x80483cb <func>

func()呼び出し前に引数が逆順(FILO)でスタック上に確保pushされていることが確認できる.
続いてcallでfuncが呼ばれると, 処理は0x080483cbに移る.
この時, callは自分が呼ばれたアドレスを覚えておかないといけないため, 戻るべきアドレス(リターンアドレス)をスタックにプッシュする.
ここでのリターンアドレスは0x0804841aである.
ここまでのスタックを整理すると以下のようになる.

|      | 0xbffff444 --> 0x804841a (Return Addr)
|      | 0xbffff448 --> 0x1 
|      | 0xbffff44c --> 0x2 
|      | 0xbffff450 --> 0x3
|______| 0xbffff454 --> 0x4

続いてfunc()の処理を見ていく.

(gdb) b *0x80483cb
Breakpoint 1, 0x080483cb in func ()

(gdb) x/4i $pc
=> 0x80483cb <func>:    push   ebp
   0x80483cc <func+1>:  mov    ebp,esp
   0x80483ce <func+3>:  sub    esp,0x10
   0x80483d1 <func+6>:  mov    DWORD PTR [ebp-0x4],0x14

(gdb) i r ebp
ebp            0xbffff468  0xbffff468

func()の関数プロローグである.
ebpの値は0xbffff468で, スタックにpushされる.
これは退避されたフレームポインタ(SFP)と呼ばれ, main()に処理を戻す際にebpを元に戻すために使用される.
結果, スタックは以下のようになる.

|      | 0xbffff440 --> 0xbffff468 --> 0x0 (SFP)
|      | 0xbffff444 --> 0x804841a (Return Addr)
|      | 0xbffff448 --> 0x1 
|      | 0xbffff44c --> 0x2 
|      | 0xbffff450 --> 0x3
|______| 0xbffff454 --> 0x4

続くmov命令でebpの値がespに, つまり, スタックの底とスタックの頂点が同じになる.
さらにsub命令でespが0x10減算される.
これによって0x10バイト分のメモリが確保された.
ここまでが関数プロローグである.

続いてfunc()では局所変数をyとbufの2種類を確保している.
変数確保後(0x080483df)でのスタックは以下のようになる.

(gdb) x/12 $esp
0xbffff430: 0x4241f666  0x00004443  0x080496ac  0x00000014
0xbffff440: 0xbffff468  0x0804841a  0x00000001  0x00000002
0xbffff450: 0x00000003  0x00000004  0x0804842b  0xb7fb6000
(gdb) x/s 0xbffff432
0xbffff432: "ABCD"
(gdb) x/u 0xbffff43c
0xbffff43c: 20
|      | 0xbffff430 --> 0x4241f666 
|      | 0xbffff434 --> 0x4443 ('CD')
|      | 0xbffff43c --> 0x14
|      | 0xbffff440 --> 0xbffff468 --> 0x0 (SFP)
|      | 0xbffff444 --> 0x804841a (Return Addr)
|      | 0xbffff448 --> 0x1 
|      | 0xbffff44c --> 0x2 
|      | 0xbffff450 --> 0x3
|______| 0xbffff454 --> 0x4

このようにスタックはメモリの低位へ成長していく.
続くreturn a+b+c+dは続くmov, addの部分.

   0x080483ec <+33>: mov    edx,DWORD PTR [ebp+0x8]
   0x080483ef <+36>: mov    eax,DWORD PTR [ebp+0xc]
   0x080483f2 <+39>: add    edx,eax
   0x080483f4 <+41>: mov    eax,DWORD PTR [ebp+0x10]
   0x080483f7 <+44>: add    edx,eax
   0x080483f9 <+46>: mov    eax,DWORD PTR [ebp+0x14]
   0x080483fc <+49>: add    eax,edx

edxにebp+0x8(0xbffff448)の値である0x1を, eaxにebp+0xc(0xbffff44c)の値である0x2を代入し, edxにeaxを加算.
さらにeaxに0x3を代入しedxに加算...のようになり, 最終的にleave命令でeaxが戻り値として返る.
最後のret命令でリターンアドレスであるmain()の0x804841aに処理が戻る.

アセンブリメモ -ループ-

前回に続いてループ処理を見ていく.

#include <stdio.h>

int main(){
   int i;
   for (i=0; i<10; i++){
      printf("%d\n", i);
   }
   return 0;
}
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x0,-0xc(%ebp)
   0x08048413 <+24>: jmp    0x804842c <main+49>
   0x08048415 <+26>: sub    $0x8,%esp
   0x08048418 <+29>: pushl  -0xc(%ebp)
   0x0804841b <+32>: push   $0x80484d0
   0x08048420 <+37>: call   0x80482d0 <printf@plt>
   0x08048425 <+42>: add    $0x10,%esp
   0x08048428 <+45>: addl   $0x1,-0xc(%ebp)
   0x0804842c <+49>: cmpl   $0x9,-0xc(%ebp)
   0x08048430 <+53>: jle    0x8048415 <main+26>
   0x08048432 <+55>: mov    $0x0,%eax
   0x08048437 <+60>: mov    -0x4(%ebp),%ecx
   0x0804843a <+63>: leave  
   0x0804843b <+64>: lea    -0x4(%ecx),%esp
   0x0804843e <+67>: ret    

f:id:yagamikou:20150924021650p:plain

0x08048413にてjmp命令で0x804842cに飛ばされる.
0x804842cではcmp命令で0x9と変数iを比較され, jle命令で9より小さい場合, printfの処理へと移る.

ではbreakが入った場合どうなるか.

#include <stdio.h>

int main(){
   int i;
   for (i=0; i<10; i++){
      if (i == 5){
         break;
      }
      printf("%d\n", i);
   }
   return 0;
}
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x0,-0xc(%ebp)
   0x08048413 <+24>: jmp    0x8048434 <main+57>
   0x08048415 <+26>: cmpl   $0x5,-0xc(%ebp)
   0x08048419 <+30>: jne    0x804841d <main+34>
   0x0804841b <+32>: jmp    0x804843a <main+63>
   0x0804841d <+34>: sub    $0x8,%esp
   0x08048420 <+37>: pushl  -0xc(%ebp)
   0x08048423 <+40>: push   $0x80484e0
   0x08048428 <+45>: call   0x80482d0 <printf@plt>
   0x0804842d <+50>: add    $0x10,%esp
   0x08048430 <+53>: addl   $0x1,-0xc(%ebp)
   0x08048434 <+57>: cmpl   $0x9,-0xc(%ebp)
   0x08048438 <+61>: jle    0x8048415 <main+26>
   0x0804843a <+63>: mov    $0x0,%eax
   0x0804843f <+68>: mov    -0x4(%ebp),%ecx
   0x08048442 <+71>: leave  
   0x08048443 <+72>: lea    -0x4(%ecx),%esp
   0x08048446 <+75>: ret    

f:id:yagamikou:20150924021705p:plain

breakのif文が0x08048415にあることが分かる.
条件式を満たさなければjne命令で0x804841dへと飛び, 満たしていればjmpで0x804843aへと飛ぶ.

ちなみにwhile文もfor文と同じようなアセンブラコードになる.

#include <stdio.h>

int main(){
   int i=0;
   while(i<=10){
      printf("%d\n", i);
      i++;
   }
   return 0;
}
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x0,-0xc(%ebp)
   0x08048413 <+24>: jmp    0x804842c <main+49>
   0x08048415 <+26>: sub    $0x8,%esp
   0x08048418 <+29>: pushl  -0xc(%ebp)
   0x0804841b <+32>: push   $0x80484d0
   0x08048420 <+37>: call   0x80482d0 <printf@plt>
   0x08048425 <+42>: add    $0x10,%esp
   0x08048428 <+45>: addl   $0x1,-0xc(%ebp)
   0x0804842c <+49>: cmpl   $0xa,-0xc(%ebp)
   0x08048430 <+53>: jle    0x8048415 <main+26>
   0x08048432 <+55>: mov    $0x0,%eax
   0x08048437 <+60>: mov    -0x4(%ebp),%ecx
   0x0804843a <+63>: leave  
   0x0804843b <+64>: lea    -0x4(%ecx),%esp
   0x0804843e <+67>: ret    

アセンブリメモ -if文-

Cを逆アセンブルした結果どのようになるかのメモ.
まずはif文

if文

   #include <stdio.h>

   int main(){
      int x = 1;
      if(x > 5){
         printf("x > 5\n");
      }
      return 0;
   }
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x1,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x5,-0xc(%ebp)
   0x08048417 <+28>: jle    0x8048429 <main+46>
   0x08048419 <+30>: sub    $0xc,%esp
   0x0804841c <+33>: push   $0x80484d0
   0x08048421 <+38>: call   0x80482d0 <puts@plt>
   0x08048426 <+43>: add    $0x10,%esp
   0x08048429 <+46>: mov    $0x0,%eax
   0x0804842e <+51>: mov    -0x4(%ebp),%ecx
   0x08048431 <+54>: leave  
   0x08048432 <+55>: lea    -0x4(%ecx),%esp
   0x08048435 <+58>: ret    

f:id:yagamikou:20150924012430p:plain

0x0804840cにて変数x(%ebp-0xc)に1を代入している.
0x08048413にて変数xと0x5を比較している.
0x08048417にてjle命令で0x8048429へと処理が移る.
jle命令は"より小さいか等しい"という条件である.
つまり, 変数xが5より小さい場合に0x8048429へと処理が移るという意味である.
そのため, if文の条件によってcmpの次の命令が変わる.

if文の条件をx < 5とすると次のようになる.

   0x0804840c <+17>: movl   $0x1,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x4,-0xc(%ebp)
   0x08048417 <+28>: jg     0x8048429 <main+46>

jg命令は"より大きい"である.
つまり, xが4より大きいならば0x8048429へ処理が移る.

また, x == 5とすると次のようになる.

   0x0804840c <+17>: movl   $0x1,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x5,-0xc(%ebp)
   0x08048417 <+28>: jne    0x8048429 <main+46>

jneは"等しくない時に"という意味である.
つまりxと5が等しくない時に0x8048429に処理が移る.

次にif-elseの場合.

   #include <stdio.h>

   int main(){
      int x = 1;
      if(x == 5){
         printf("x == 5\n");
      } else {
         printf("x != 5\n");
      }
      return 0;
   }
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x1,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x5,-0xc(%ebp)
   0x08048417 <+28>: jne    0x804842b <main+48>
   0x08048419 <+30>: sub    $0xc,%esp
   0x0804841c <+33>: push   $0x80484e0
   0x08048421 <+38>: call   0x80482d0 <puts@plt>
   0x08048426 <+43>: add    $0x10,%esp
   0x08048429 <+46>: jmp    0x804843b <main+64>
   0x0804842b <+48>: sub    $0xc,%esp
   0x0804842e <+51>: push   $0x80484e7
   0x08048433 <+56>: call   0x80482d0 <puts@plt>
   0x08048438 <+61>: add    $0x10,%esp
   0x0804843b <+64>: mov    $0x0,%eax
   0x08048440 <+69>: mov    -0x4(%ebp),%ecx
   0x08048443 <+72>: leave  
   0x08048444 <+73>: lea    -0x4(%ecx),%esp
   0x08048447 <+76>: ret    

f:id:yagamikou:20150924012512p:plain

続いて, else ifを追加した場合.

   #include <stdio.h>

   int main(){
      int x = 5;
      if(x > 5){
         printf("x > 5\n");
      } else if( x < 5){
         printf("x < 5\n");
      } else {
         printf("x == 5\n");
      }
      return 0;
   }
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x5,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x5,-0xc(%ebp)
   0x08048417 <+28>: jle    0x804842b <main+48>
   0x08048419 <+30>: sub    $0xc,%esp
   0x0804841c <+33>: push   $0x80484f0
   0x08048421 <+38>: call   0x80482d0 <puts@plt>
   0x08048426 <+43>: add    $0x10,%esp
   0x08048429 <+46>: jmp    0x8048453 <main+88>
   0x0804842b <+48>: cmpl   $0x4,-0xc(%ebp)
   0x0804842f <+52>: jg     0x8048443 <main+72>
   0x08048431 <+54>: sub    $0xc,%esp
   0x08048434 <+57>: push   $0x80484f6
   0x08048439 <+62>: call   0x80482d0 <puts@plt>
   0x0804843e <+67>: add    $0x10,%esp
   0x08048441 <+70>: jmp    0x8048453 <main+88>
   0x08048443 <+72>: sub    $0xc,%esp
   0x08048446 <+75>: push   $0x80484fc
   0x0804844b <+80>: call   0x80482d0 <puts@plt>
   0x08048450 <+85>: add    $0x10,%esp
   0x08048453 <+88>: mov    $0x0,%eax
   0x08048458 <+93>: mov    -0x4(%ebp),%ecx
   0x0804845b <+96>: leave  
   0x0804845c <+97>: lea    -0x4(%ecx),%esp
   0x0804845f <+100>:   ret    

f:id:yagamikou:20150924012524p:plain

次にAND, OR演算子を使用した場合にどういったアセンブリコードになるかを確認する.
まずはAND演算子を用いたコードから.

   #include <stdio.h>

   int main(){
      int x = 1;
      if(x > 0 && x < 10){
         printf("x > 0 && x < 10\n");
      } else {
         printf("x > 10 || x < 0\n");
      }
      return 0;
   }
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x1,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x0,-0xc(%ebp)
   0x08048417 <+28>: jle    0x8048431 <main+54>
   0x08048419 <+30>: cmpl   $0x9,-0xc(%ebp)
   0x0804841d <+34>: jg     0x8048431 <main+54>
   0x0804841f <+36>: sub    $0xc,%esp
   0x08048422 <+39>: push   $0x80484e0
   0x08048427 <+44>: call   0x80482d0 <puts@plt>
   0x0804842c <+49>: add    $0x10,%esp
   0x0804842f <+52>: jmp    0x8048441 <main+70>
   0x08048431 <+54>: sub    $0xc,%esp
   0x08048434 <+57>: push   $0x80484f0
   0x08048439 <+62>: call   0x80482d0 <puts@plt>
   0x0804843e <+67>: add    $0x10,%esp
   0x08048441 <+70>: mov    $0x0,%eax
   0x08048446 <+75>: mov    -0x4(%ebp),%ecx
   0x08048449 <+78>: leave  
   0x0804844a <+79>: lea    -0x4(%ecx),%esp
   0x0804844d <+82>: ret

f:id:yagamikou:20150924012544p:plain

注目すべき点はcmp命令の部分.

   0x08048413 <+24>: cmpl   $0x0,-0xc(%ebp)
   0x08048417 <+28>: jle    0x8048431 <main+54>
   0x08048419 <+30>: cmpl   $0x9,-0xc(%ebp)
   0x0804841d <+34>: jg     0x8048431 <main+54>

条件が1つだったときは

   cmp X, X
   jle 0xXXXXXXX
   call <puts>
   ...
   cmp X, X
   jle 0xXXXXXXX
   cal <puts>
   ...

のように条件分岐後にifステートメント中の処理コード(printfなど)が来ていた.
しかし, AND演算子を用いた結果,

   cmp X, X
   jle 0xXXXXXXXX
   cmp X, X
   jle 0xXXXXXXXX

のようにcmp+jxxのコードが連続して登場するようになる.
OR演算子を使った場合も同様である.

   0x08048413 <+24>: cmpl   $0xa,-0xc(%ebp)
   0x08048417 <+28>: jg     0x804841f <main+36>
   0x08048419 <+30>: cmpl   $0x0,-0xc(%ebp)
   0x0804841d <+34>: jns    0x8048431 <main+54>

f:id:yagamikou:20150924012601p:plain

参考