HIGH dns rebindinggin

Dns Rebinding in Gin

How Dns Rebinding Manifests in Gin

Dns Rebinding attacks exploit the trust relationship between web applications and internal network services. In Gin applications, this vulnerability typically manifests when user-supplied URLs are used to make outbound HTTP requests without proper validation.

The attack works by registering a malicious domain that resolves to the attacker's IP initially. After the victim's browser connects, the DNS record changes to point to an internal IP address (like 192.168.1.1 or 10.0.0.1). Since browsers respect DNS TTL caching, the malicious site can bypass the same-origin policy and access internal services as if it were a legitimate internal request.

In Gin applications, this commonly appears in endpoint handlers that proxy requests or fetch external resources. Consider this vulnerable pattern:

func proxyHandler(c *gin.Context) {
    targetURL := c.Query("url")
    resp, err := http.Get(targetURL)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    defer resp.Body.Close()
    
    body, _ := io.ReadAll(resp.Body)
    c.Data(http.StatusOK, resp.Header.Get("Content-Type"), body)
}

An attacker could craft a request like:

GET /proxy?url=http://malicious-domain.com/api

After DNS rebinding, this request could reach internal APIs like:

GET /proxy?url=http://192.168.1.100:8080/internal-api

Gin's default middleware doesn't provide any protection against this, as it treats all outbound requests as legitimate. The vulnerability becomes particularly dangerous when the proxied service expects authentication from the originating client or when internal APIs lack proper authorization checks.

Another Gin-specific manifestation occurs with WebSocket upgrades. If a Gin handler accepts WebSocket connections to user-specified URLs:

func wsProxy(c *gin.Context) {
    target := c.Query("target")
    
    d := websocket.DefaultDialer
    conn, _, err := d.Dial(target, c.Request.Header)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    defer conn.Close()
    
    // Proxy WebSocket messages...
}

This allows attackers to establish WebSocket connections to internal services that might not be publicly accessible, potentially exposing sensitive real-time data or control interfaces.

Gin-Specific Detection

Detecting Dns Rebinding in Gin applications requires both static code analysis and runtime testing. Static analysis should focus on identifying patterns where user input is used to construct outbound requests.

Using middleBrick's scanning capabilities, you can detect Dns Rebinding vulnerabilities without modifying your code. middleBrick's black-box scanning tests for SSRF patterns that include Dns Rebinding by attempting requests to special domains and observing responses. The scanner uses techniques like:

http://127.0.0.1.internal/
http://169.254.169.254/
http://metadata.google.internal/
http://instance-data/latest/meta-data/

These requests help identify if the application is making outbound connections to internal addresses. middleBrick also tests with time-delayed DNS responses to detect rebinding vulnerabilities.

For manual detection in your Gin codebase, search for these patterns:

# Find handlers that use user input for HTTP requests
grep -r "http\.Get\|http\.Post\|http\.Client" . | grep -E "(Query|Param|Default)"

# Find WebSocket proxy patterns
grep -r "websocket\.DefaultDialer\|websocket\.Dial" . | grep -E "(Query|Param|Default)"

Code review should specifically examine handlers that accept URLs as parameters. Look for missing validation of the scheme, host, and port. A secure implementation should reject non-HTTP/HTTPS schemes and validate the hostname against an allowlist.

middleBrick's OpenAPI analysis can also detect this vulnerability by examining your API specification. If your Swagger/OpenAPI spec shows endpoints accepting URL parameters that are used for outbound requests, middleBrick flags this as a potential SSRF risk. The scanner correlates spec definitions with runtime behavior, providing comprehensive coverage.

Runtime testing involves attempting requests with known internal IP addresses and observing the application's behavior. If the application responds with internal service data or error messages that reveal internal infrastructure details, it indicates a vulnerability.

Gin-Specific Remediation

Remediating Dns Rebinding in Gin requires implementing strict input validation and using safe HTTP clients. The most effective approach is to use an allowlist of permitted domains and reject all others.

Here's a secure implementation using Gin middleware:

package main

import (
    "net/http"
    "strings"
    "github.com/gin-gonic/gin"
)

type urlValidator struct {
    allowedDomains map[string]bool
}

func (v *urlValidator) isValid(rawURL string) bool {
    if !strings.HasPrefix(rawURL, "http://") && !strings.HasPrefix(rawURL, "https://") {
        return false
    }
    
    u, err := http.Parse(rawURL)
    if err != nil {
        return false
    }
    
    // Reject private IP ranges
    if isPrivateIP(u.Hostname()) {
        return false
    }
    
    // Check against allowlist
    domain := getDomain(u.Hostname())
    if !v.allowedDomains[domain] {
        return false
    }
    
    return true
}

func isPrivateIP(host string) bool {
    // Check for private IP ranges: 10.x, 172.16-31.x, 192.168.x, 127.x
    return strings.HasPrefix(host, "10.") ||
           strings.HasPrefix(host, "127.") ||
           strings.HasPrefix(host, "192.168.") ||
           strings.HasPrefix(host, "172.")
}

func getDomain(host string) string {
    parts := strings.Split(host, ".")
    if len(parts) <= 2 {
        return host
    }
    return strings.Join(parts[len(parts)-2:], ".")
}

func validateURLMiddleware(validator *urlValidator) gin.HandlerFunc {
    return func(c *gin.Context) {
        urlParam := c.Query("url")
        if urlParam != "" && !validator.isValid(urlParam) {
            c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid or unauthorized URL"})
            c.Abort()
            return
        }
        c.Next()
    }
}

func main() {
    validator := &urlValidator{
        allowedDomains: map[string]bool{
            "example.com": true,
            "api.example.org": true,
        },
    }
    
    r := gin.Default()
    r.Use(validateURLMiddleware(validator))
    
    r.GET("/proxy", func(c *gin.Context) {
        targetURL := c.Query("url")
        
        client := &http.Client{
            Timeout: 10 * time.Second,
            CheckRedirect: func(req *http.Request, via []*http.Request) error {
                return http.ErrUseLastResponse // Prevent redirect loops
            },
        }
        
        resp, err := client.Get(targetURL)
        if err != nil {
            c.JSON(500, gin.H{"error": err.Error()})
            return
        }
        defer resp.Body.Close()
        
        body, _ := io.ReadAll(resp.Body)
        c.Data(http.StatusOK, resp.Header.Get("Content-Type"), body)
    })
    
    r.Run(":8080")
}

For WebSocket endpoints, implement similar validation:

func validateWSHandler(validator *urlValidator) gin.HandlerFunc {
    return func(c *gin.Context) {
        target := c.Query("target")
        if target != "" && !validator.isValid(target) {
            c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid WebSocket target"})
            c.Abort()
            return
        }
        c.Next()
    }
}

r.GET("/ws-proxy", validateWSHandler(validator), func(c *gin.Context) {
    target := c.Query("target")
    
    d := websocket.DefaultDialer
    conn, _, err := d.Dial(target, c.Request.Header)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    defer conn.Close()
    
    // WebSocket proxying logic...
})

Additional protections include implementing DNS pinning by setting a minimum TTL for DNS resolutions, using HTTP clients that respect DNS caching properly, and implementing rate limiting on endpoints that accept URLs to slow down automated rebinding attempts.

middleBrick's continuous monitoring can verify these remediations by regularly scanning your API endpoints and ensuring the Dns Rebinding vulnerability remains resolved. The platform provides detailed reports showing which specific endpoints were tested and the results of each security check.

Frequently Asked Questions

How does Dns Rebinding differ from regular SSRF in Gin applications?
Dns Rebinding is a specific type of SSRF that exploits DNS caching to bypass IP-based allowlists. While regular SSRF might be prevented by blocking private IP addresses, Dns Rebinding works by initially resolving to a legitimate IP, then changing to an internal IP after the browser has cached the DNS record. In Gin applications, this means that simple IP allowlisting is insufficient - you need hostname validation and DNS pinning to prevent rebinding attacks.
Can middleBrick detect Dns Rebinding in my Gin API without access to the source code?
Yes, middleBrick performs black-box scanning that tests for Dns Rebinding by making requests to special domains and observing the application's behavior. The scanner uses techniques like time-delayed DNS responses and requests to known internal service endpoints to detect if your Gin application is vulnerable to rebinding attacks. No source code access or credentials are required - just the API endpoint URL.