SSRF

SSRF Remediation in Go (Gin) [June 2026] [CVE-2026-44116]

[Updated June 2026] Updated CVE-2026-44116

Overview

CVE-2026-44116 describes a server-side request forgery in OpenClaw's Zalo plugin's sendPhoto function, where the SSRF guard failed to validate outbound photo URLs. An attacker could supply malicious URLs to the Zalo Bot API, causing the server to fetch internal resources that would otherwise be inaccessible. This vulnerability aligns with CWE-918: Server-Side Request Forgery. The real-world impact includes access to internal services, cloud metadata endpoints, and other protected resources, enabling data leakage or further network pivoting. In practice, such flaws arise when a Go (Gin) service fetches or proxies remote content based on user-provided URLs without strict validation or egress controls, allowing attackers to trigger requests to internal or restricted networks via the server.

Affected Versions

OpenClaw before 2026.4.22

Code Fix Example

Go (Gin) API Security Remediation
package main

import (
  "io"
  "net/http"
  "net/url"
  "strings"
  "time"

  "github.com/gin-gonic/gin"
)

type PhotoRequest struct {
  PhotoURL string
}

func main() {
  r := gin.Default()
  // Vulnerable endpoint (demonstration)
  r.POST("/vuln/photo", vulnerableHandler)
  // Fixed endpoint (recommended)
  r.POST("/v1/photo", secureHandler)
  r.Run()
}

func vulnerableHandler(c *gin.Context) {
  var req PhotoRequest
  if err := c.BindJSON(&req); err != nil {
    c.JSON(400, gin.H{"error": "bad request"})
    return
  }
  // Vulnerable: directly fetch user-provided URL
  resp, err := http.Get(req.PhotoURL)
  if err != nil {
    c.JSON(502, gin.H{"error": "fetch failed"})
    return
  }
  defer resp.Body.Close()
  data, _ := io.ReadAll(resp.Body)
  c.Data(resp.StatusCode, resp.Header.Get("Content-Type"), data)
}

func isUnsafeURL(u *url.URL) bool {
  if u.Scheme != "http" && u.Scheme != "https" {
    return true
  }
  host := strings.ToLower(u.Hostname())
  if host == "localhost" {
    return true
  }
  if strings.HasPrefix(host, "127.0.0.") {
    return true
  }
  if strings.HasPrefix(host, "10.") {
    return true
  }
  if strings.HasPrefix(host, "172.") {
    return true
  }
  if strings.HasPrefix(host, "192.168.") {
    return true
  }
  return false
}

func secureHandler(c *gin.Context) {
  var req PhotoRequest
  if err := c.BindJSON(&req); err != nil {
    c.JSON(400, gin.H{"error": "bad request"})
    return
  }
  parsed, err := url.Parse(req.PhotoURL)
  if err != nil {
    c.JSON(400, gin.H{"error": "invalid url"})
    return
  }
  if isUnsafeURL(parsed) {
    c.JSON(400, gin.H{"error": "unsafe url"})
    return
  }
  transport := &http.Transport{}
  client := &http.Client{
    Transport: transport,
    Timeout:   10 * time.Second,
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
      return http.ErrUseLastResponse
    },
  }
  resp, err := client.Get(req.PhotoURL)
  if err != nil {
    c.JSON(502, gin.H{"error": "fetch failed"})
    return
  }
  defer resp.Body.Close()
  data, _ := io.ReadAll(resp.Body)
  c.Data(resp.StatusCode, resp.Header.Get("Content-Type"), data)
}

CVE References

Choose which optional cookies to allow. You can change this any time.