HIGH padding oracleecho go

Padding Oracle in Echo Go

How Padding Oracle Manifests in Echo Go

Padding Oracle attacks exploit the behavior of padding validation in block cipher modes like CBC (Cipher Block Chaining). In Echo Go applications, this vulnerability typically manifests when error messages or response timing differ based on whether padding validation succeeds or fails during decryption.

Consider this common Echo Go pattern:

func decryptHandler(c echo.Context) error {
    encrypted := c.FormValue("data")
    ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
    if err != nil { return err }
    
    block, err := aes.NewCipher(key)
    if err != nil { return err }
    
    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]
    
    mode := cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(ciphertext, ciphertext)
    
    unpadded, err := pkcs7Unpad(ciphertext, aes.BlockSize)
    if err != nil {
        // THIS IS THE PROBLEM - different error handling
        return c.JSON(http.StatusBadRequest, map[string]string{
            "error": "Invalid padding",
        })
    }
    
    return c.JSON(http.StatusOK, map[string]string{
        "data": string(unpadded),
    })
}

func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
    if len(data) == 0 {
        return nil, errors.New("data is empty")
    }
    
    padLen := int(data[len(data)-1])
    if padLen == 0 || padLen > len(data) {
        return nil, errors.New("invalid padding")
    }
    
    for _, b := range data[len(data)-padLen:] {
        if int(b) != padLen {
            return nil, errors.New("invalid padding")
        }
    }
    
    return data[:len(data)-padLen], nil
}

The critical flaw is in the error handling. When an attacker sends modified ciphertext blocks, the server's response differs based on whether the padding is valid or the plaintext is recognizable. This timing or content difference leaks information about the plaintext.

In Echo Go applications, this often appears in:

  • Session token decryption in middleware
  • Encrypted API parameters
  • Database field encryption handling
  • Token-based authentication systems

The attack works by manipulating the ciphertext and observing server responses. For a block cipher with 8-byte blocks, an attacker can decrypt one byte in approximately 256 requests, then proceed to the next byte. This makes the attack practical even for large secrets.

Echo Go-Specific Detection

Detecting Padding Oracle vulnerabilities in Echo Go applications requires both static code analysis and dynamic testing. Here's how to identify this issue:

Static Analysis

Search your Echo Go codebase for these patterns:

// Look for these imports
import (
    "crypto/aes"
    "crypto/cipher"
    "encoding/base64"
    // and error handling that reveals padding status
)

// Search for these patterns
func decrypt(...) (..., error) {
    // ...
    if err != nil {
        return nil, errors.New("invalid padding") // or similar
    }
    // ...
}

// Check middleware for error responses
func decryptMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        // ...
        if paddingError {
            return echo.NewHTTPError(http.StatusBadRequest, "Invalid padding")
        }
        // ...
    }
}

Dynamic Testing with middleBrick

middleBrick's black-box scanning can detect Padding Oracle vulnerabilities without source code access. The scanner tests for timing differences and error message variations when sending modified ciphertext.

Install and use middleBrick CLI:

npm install -g middlebrick
middlebrick scan https://yourechoapp.com/api/protected

The scanner specifically tests for:

  • Differential error responses ("invalid padding" vs "invalid data")
  • Timing variations between padding errors and other errors
  • HTTP status code differences (400 vs 500)
  • Response size variations

Manual Testing

You can also manually test your Echo Go endpoints:

// 1. Capture a valid encrypted request
// 2. Modify the last byte of the second-to-last block
// 3. Send the modified request
// 4. Observe if the error changes
// 5. Repeat with different modifications

// Example using curl:
curl -X POST https://yourechoapp.com/api/decrypt \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "data=modified_ciphertext_here"

Look for any indication that the server processed the padding differently, such as different error messages, HTTP status codes, or response times.

Echo Go-Specific Remediation

Fixing Padding Oracle vulnerabilities in Echo Go requires eliminating timing and error message differences. Here are Echo Go-specific solutions:

1. Use Constant-Time Decryption

Always perform decryption and padding validation, regardless of input validity. Return the same error for all failures:

func secureDecryptHandler(c echo.Context) error {
    encrypted := c.FormValue("data")
    ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
    if err != nil {
        return genericError(c)
    }
    
    block, err := aes.NewCipher(key)
    if err != nil {
        return genericError(c)
    }
    
    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]
    
    mode := cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(ciphertext, ciphertext)
    
    // Always attempt unpadding, never reveal padding status
    _, err = pkcs7Unpad(ciphertext, aes.BlockSize)
    if err != nil {
        return genericError(c)
    }
    
    return c.JSON(http.StatusOK, map[string]string{
        "data": "decryption successful",
    })
}

func genericError(c echo.Context) error {
    return echo.NewHTTPError(http.StatusBadRequest, "Invalid data")
}

func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
    if len(data) == 0 {
        return nil, errors.New("invalid data")
    }
    
    padLen := int(data[len(data)-1])
    if padLen == 0 || padLen > len(data) {
        return nil, errors.New("invalid data")
    }
    
    for _, b := range data[len(data)-padLen:] {
        if int(b) != padLen {
            return nil, errors.New("invalid data")
        }
    }
    
    return data[:len(data)-padLen], nil
}

2. Use HMAC for Authentication

Combine encryption with message authentication to prevent tampering:

func encryptWithHMAC(plaintext []byte) (string, error) {
    key := []byte("your-32-byte-key-here")
    
    // Generate random IV
    iv := make([]byte, 12)
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return "", err
    }
    
    // Encrypt
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }
    
    aead, err := cipher.NewGCM(block)
    if err != nil {
        return "", err
    }
    
    ciphertext := aead.Seal(nil, iv, plaintext, nil)
    
    // Combine IV + ciphertext
    combined := append(iv, ciphertext...)
    
    return base64.StdEncoding.EncodeToString(combined), nil
}

func decryptWithHMAC(encrypted string) ([]byte, error) {
    ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
    if err != nil {
        return nil, errors.New("invalid data")
    }
    
    if len(ciphertext) < 12+1 {
        return nil, errors.New("invalid data")
    }
    
    iv := ciphertext[:12]
    ciphertext = ciphertext[12:]
    
    key := []byte("your-32-byte-key-here")
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, errors.New("invalid data")
    }
    
    aead, err := cipher.NewGCM(block)
    if err != nil {
        return nil, errors.New("invalid data")
    }
    
    plaintext, err := aead.Open(nil, iv, ciphertext, nil)
    if err != nil {
        return nil, errors.New("invalid data")
    }
    
    return plaintext, nil
}

// Echo handler
func secureHandler(c echo.Context) error {
    encrypted := c.FormValue("data")
    
    _, err := decryptWithHMAC(encrypted)
    if err != nil {
        return genericError(c)
    }
    
    return c.JSON(http.StatusOK, map[string]string{
        "data": "decryption successful",
    })
}

3. Add to Echo Middleware

Create reusable middleware for encrypted parameters:

func EncryptionMiddleware(key []byte) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            encrypted := c.FormValue("data")
            if encrypted == "" {
                return genericError(c)
            }
            
            _, err := decryptWithHMAC(encrypted)
            if err != nil {
                return genericError(c)
            }
            
            return next(c)
        }
    }
}

// Usage in Echo app
e := echo.New()
e.Use(EncryptionMiddleware([]byte("your-32-byte-key")))
e.POST("/api/protected", protectedHandler)

4. Integrate with middleBrick in CI/CD

Add security scanning to your Echo Go pipeline:

# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Run middleBrick Scan
        run: |
          npm install -g middlebrick
          middlebrick scan https://staging.yourechoapp.com \
            --fail-below B \
            --output json > security-report.json
      - name: Upload Report
        uses: actions/upload-artifact@v2
        with:
          name: security-report
          path: security-report.json

This ensures Padding Oracle and other vulnerabilities are caught before deployment.

Frequently Asked Questions

Why doesn't middleBrick find all Padding Oracle vulnerabilities in my Echo Go app?
middleBrick performs black-box scanning and tests for timing differences and error message variations. It cannot detect vulnerabilities that only manifest with specific secret keys or in complex multi-step workflows. For comprehensive coverage, combine middleBrick scanning with manual code review and static analysis of your Echo Go source code.
Can I use middleBrick to scan my local Echo Go development server?
Yes, middleBrick can scan any accessible API endpoint. For local development, ensure your Echo Go server is accessible from the network where middleBrick runs (or use a service like ngrok to expose it temporarily). The scanner works with localhost URLs and will test all unauthenticated endpoints for Padding Oracle and other vulnerabilities.