Broken Authentication

Broken Authentication in Go Gin: CVE-2026-33715 guide [CVE-2026-33715]

[Updated 2026-04] Updated CVE-2026-33715

Overview

CVE-2026-33715 describes a broken-authentication and access-control flaw in Chamilo LMS where an unauthenticated AJAX endpoint (public/main/inc/ajax/install.ajax.php) could be reached on a fully installed instance. This endpoint accepts an arbitrary SMTP DSN from POST data and connects to an attacker-specified SMTP server, enabling Server-Side Request Forgery (SSRF) into internal networks via the SMTP protocol. The issue could also weaponize the Chamilo server as an open email relay for phishing and spam campaigns, with emails potentially appearing to originate from the server’s IP. Error responses from failed SMTP connections could reveal internal topology or service details. The root problem is poor authentication checks and improper segregation of sensitive administrative paths. It was fixed in version 2.0.0-RC.3. This pattern maps to CWE-306 (Missing Authentication for Critical Function) and CWE-918 (Server-Side CSRF/SSRF-like exposure via improperly secured endpoints). In Go with Gin, analogous vulnerabilities arise when endpoints performing sensitive actions are left unauthenticated or when clients can supply critical configuration (like an SMTP DSN) that the server uses to contact internal resources. The remediation is to enforce strong authentication on sensitive routes, avoid using client-provided DSNs for outbound connections, and validate all inputs with strict allowlists and server-side configuration.

Code Fix Example

Go (Gin) API Security Remediation
package main

import (
  "net/http"
  "net/smtp"
  "os"

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

func main() {
  r := gin.Default()

  // Vulnerable pattern: unauthenticated endpoint that uses a client-provided DSN to dial SMTP
  r.POST("/install/ajax/test_mailer", func(c *gin.Context) {
    dsn := c.PostForm("dsn")
    if dsn == "" {
      c.JSON(http.StatusBadRequest, gin.H{"error": "dsn is required"})
      return
    }
    // Untrusted input: DSN used directly to connect to an SMTP server
    client, err := smtp.Dial(dsn)
    if err != nil {
      c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
      return
    }
    // Minimal operation to demonstrate usage; not a production mail flow
    _ = client.Close()
    c.JSON(http.StatusOK, gin.H{"status": "connected to SMTP", "dsn": dsn})
  })

  // Fixed pattern: require authentication and do not use client-provided DSN
  // All outbound SMTP connections must use a server-configured DSN
  authorize := func(c *gin.Context) {
    if c.GetHeader("X-API-Key") != "secret-key" {
      c.AbortWithStatus(http.StatusUnauthorized)
      return
    }
    c.Next()
  }

  r.POST("/install/ajax/test_mailer_fix", authorize, func(c *gin.Context) {
    smtpDSN := os.Getenv("SMTP_DSN") // configured on the server, not from the client
    if smtpDSN == "" {
      c.JSON(http.StatusInternalServerError, gin.H{"error": "SMTP DSN not configured on server"})
      return
    }
    client, err := smtp.Dial(smtpDSN)
    if err != nil {
      c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
      return
    }
    _ = client.Close()
    c.JSON(http.StatusOK, gin.H{"status": "connected to server SMTP"})
  })

  r.Run(":8080")
}

CVE References

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