13 KiB
SSRF Exploitation Evidence
Executive Summary
This report documents the comprehensive exploitation testing of one Server-Side Request Forgery (SSRF) vulnerability candidate identified during the analysis phase. After exhaustive testing and verification, the finding has been reclassified as a FALSE POSITIVE for SSRF vulnerability.
Key Finding: The OAuth Dynamic Client Registration vulnerability (SSRF-VULN-01) was initially classified as SSRF but is actually an Open Redirect vulnerability. The server does not make outbound HTTP requests to attacker-controlled destinations; instead, it redirects the client's browser using HTTP 303 status codes. This does not meet the definition of Server-Side Request Forgery.
Exploitation Status:
- Successfully Exploited: 0
- Potential Vulnerabilities: 0
- False Positives: 1
Successfully Exploited Vulnerabilities
No SSRF vulnerabilities were successfully exploited.
Potential Vulnerabilities (Validation Blocked)
No potential SSRF vulnerabilities requiring further investigation.
False Positive Analysis
SSRF-VULN-01: OAuth Dynamic Client Registration - Open Redirect (NOT SSRF)
Summary:
- Vulnerable location: POST /register (unauthenticated), GET /oauth/authorize (authenticated)
- Initial Classification: Server-Side Request Forgery (SSRF) with internal service access capability
- Corrected Classification: Open Redirect vulnerability
- Reason for Reclassification: The server uses
http.Redirect()which sends an HTTP 303 response to the client's browser, not a server-side HTTP request to the target URL - SSRF Criteria: ❌ FAILED - Server does not make outbound HTTP requests to attacker-controlled destinations
Detailed Analysis:
The vulnerability allows unauthenticated attackers to register OAuth clients with arbitrary redirect URIs, including internal network addresses. However, the exploitation mechanism does not match the definition of SSRF:
What Actually Happens:
- Attacker registers OAuth client with malicious redirect_uri (e.g.,
http://169.254.169.254/latest/meta-data/) - Attacker (or victim) initiates OAuth authorization flow
- Server generates authorization code
- Server sends HTTP 303 redirect response to the CLIENT'S BROWSER
- Client's browser attempts to navigate to the internal URL (and fails from external network)
What Does NOT Happen (SSRF behavior):
- ❌ Server does not make HTTP requests to the redirect_uri
- ❌ Server does not establish connections to internal services
- ❌ Server does not access internal network resources
- ❌ Server does not leak internal service responses back to the attacker
Code Evidence:
File: /repos/inou-portal/portal/oauth.go:139
http.Redirect(w, r, redirectURL.String(), http.StatusSeeOther)
This function sends an HTTP 303 status code with a Location header - it does NOT make an outbound request. The Go documentation confirms:
"Redirect replies to the request with a redirect to url, which may be a path relative to the request path. The provided code should be in the 3xx range and is usually StatusMovedPermanently, StatusFound or StatusSeeOther."
Exploitation Attempts:
Attempt 1: Register OAuth Client with AWS Metadata Endpoint
Objective: Force server to access AWS EC2 instance metadata service
Method:
POST /register HTTP/1.1
Host: inou.com
Content-Type: application/json
{
"client_name": "Security Research Client - AWS Metadata",
"redirect_uris": [
"http://169.254.169.254/latest/meta-data/iam/security-credentials/"
]
}
Result:
HTTP/1.1 201 Created
Content-Type: application/json
{
"client_id": "243f06b1598fb12d49c862ba047b9d1e",
"client_secret": "b3cec86e5fdf34ddde72a35d945eddb57ec6a985d2dc808e55c42420032cee3f",
"redirect_uris": [
"http://169.254.169.254/latest/meta-data/iam/security-credentials/"
],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_post"
}
Outcome: ✅ Client registration successful - No validation of redirect_uri against internal IP ranges
Attempt 2: Trigger OAuth Authorization Flow
Objective: Force server to make HTTP request to AWS metadata endpoint
Prerequisites:
- Valid user session (obtained via passwordless authentication with backdoor code 250365)
- Registered malicious OAuth client from Attempt 1
Method:
GET /oauth/authorize?client_id=243f06b1598fb12d49c862ba047b9d1e&redirect_uri=http%3A%2F%2F169.254.169.254%2Flatest%2Fmeta-data%2Fiam%2Fsecurity-credentials%2F&response_type=code&state=test HTTP/1.1
Host: inou.com
Cookie: login=6a484f3f85735a03
Server Response:
HTTP/1.1 303 See Other
Location: http://169.254.169.254/latest/meta-data/iam/security-credentials/?code=4f4f3e67fcf804ce05487c18fdaee6f5b354ecb467f834d9747285369a4b31d7&state=test
Content-Type: text/html; charset=utf-8
Content-Length: 177
Analysis:
- Server returned HTTP 303 redirect (client-side redirect)
- Server did NOT make an HTTP request to 169.254.169.254
- Server did NOT receive or return data from the metadata service
- Browser attempted to navigate to the internal URL (network error from client side)
Outcome: ❌ NOT SSRF - This is an Open Redirect vulnerability, not server-side request forgery
Attempt 3: Register Additional Clients for Internal Services
Objective: Test if different internal targets yield server-side requests
Targets Tested:
-
Internal API Server:
http://127.0.0.1:8082/api/access- Client ID: 9863b518919c5c11532b2775e81a0b82
- Registration: ✅ Successful
-
Signal RPC Service:
http://192.168.1.16:8080/api/v1/rpc- Client ID: 96478c7a51bc4447f2a54cf6c30ae970
- Registration: ✅ Successful
-
DICOM Viewer:
http://localhost:8765/viewer- Client ID: afb78106315871695217d878f6c8b6b9
- Registration: ✅ Successful
Result: All clients successfully registered with internal network targets, confirming complete lack of redirect URI validation. However, all subsequent authorization flows resulted in the same behavior: HTTP 303 client-side redirects, not server-side HTTP requests.
Outcome: ❌ NOT SSRF - Consistent client-side redirect behavior across all internal targets
Attempt 4: Search for Alternative SSRF Sinks
Objective: Identify any code paths where the server actually makes outbound HTTP requests with user-controlled URLs
Code Review Findings:
-
Google Gemini API Integration (
/repos/inou-portal/lib/llm.go:109)- URL construction:
fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models/%s:generateContent?key=%s", *config.Model, GeminiKey) - User control: ❌ NONE - Model parameter never populated from user input
- Verdict: Not exploitable
- URL construction:
-
Signal Messaging RPC (
/repos/inou-portal/lib/signal.go:10)- Endpoint:
const signalAPI = "http://192.168.1.16:8080/api/v1/rpc" - User control: ❌ NONE - Hardcoded destination
- Verdict: Not exploitable
- Endpoint:
-
Internal API Proxy (
/repos/inou-portal/portal/api_proxy.go:22)- Endpoint:
const apiBackend = "http://127.0.0.1:8082" - User control: ❌ NONE - Hardcoded destination
- Verdict: Not exploitable
- Endpoint:
-
SMTP Email Delivery (
/repos/inou-portal/lib/email.go)- Configuration: Loaded from environment file
- User control: ❌ NONE - Administrative configuration only
- Verdict: Not exploitable
Outcome: ❌ No true SSRF sinks identified - All outbound HTTP requests use hardcoded or configuration-based destinations
SSRF vs Open Redirect - Technical Distinction:
| Characteristic | SSRF | Open Redirect (This Vulnerability) |
|---|---|---|
| Server Behavior | Server makes HTTP request to attacker-controlled URL | Server sends HTTP 3xx redirect to client |
| Request Origin | Server's network context | Client's network context |
| Access to Internal Services | Yes - server can reach internal networks | No - client cannot reach internal networks from external position |
| Data Exfiltration | Yes - server returns internal service responses | No - server never sees internal service data |
| Network Boundary Bypass | Yes - server is inside the network | No - redirect fails from external client |
| HTTP Status Codes | 200 OK (or error from internal service) | 303 See Other (redirect to client) |
Why This Matters for Classification:
The analysis phase correctly identified that the application allows arbitrary redirect URIs to be registered without validation. However, the exploitation mechanism does not meet the SSRF threat model:
-
SSRF Definition: Server-Side Request Forgery occurs when an attacker can cause the server to make HTTP requests to arbitrary destinations, leveraging the server's network position and credentials.
-
This Vulnerability: The server generates a redirect response (HTTP 303) that instructs the client's browser to navigate to an arbitrary URL. The server itself never makes the request.
Attempted Bypass Techniques:
To ensure thorough testing, the following bypass attempts were made to see if the redirect could be leveraged into true SSRF:
- ❌ Multiple Protocol Tests: Tried
file://,gopher://,ftp://- All accepted in registration but still result in client-side redirects - ❌ Redirect Chaining: Attempted to chain redirects to see if server would follow - No server-side request initiated
- ❌ DNS Rebinding Simulation: Cannot be tested externally, but would not matter since redirect is client-side
- ❌ SSRF via Request Smuggling: OAuth flow doesn't involve request parsing that could be smuggled
Actual Security Impact:
While not SSRF, this vulnerability still has security implications:
- Open Redirect Risk: Attackers can redirect users to phishing sites via trusted inou.com domain
- OAuth Code Leakage: If an attacker can register a client with their own domain, they receive the authorization code when users authorize
- Network Topology Disclosure: Error messages and timing differences may reveal which internal IPs/ports exist
- Authorization Code in URL: Codes appear in URL parameters of the redirect, violating OAuth security best practices (should use POST-based flows for sensitive redirects)
Recommended Fix:
While this is not SSRF, the underlying vulnerability (unrestricted redirect URIs) should still be fixed:
// In /repos/inou-portal/portal/mcp_http.go, before line 131:
func validateRedirectURI(uri string) error {
parsed, err := url.ParseRequestURI(uri)
if err != nil {
return fmt.Errorf("invalid URI format: %w", err)
}
// Require HTTPS for all redirect URIs
if parsed.Scheme != "https" {
return fmt.Errorf("only HTTPS redirect URIs allowed")
}
// Block private IP ranges
if ip := net.ParseIP(parsed.Hostname()); ip != nil {
if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() {
return fmt.Errorf("private/internal IP addresses not allowed")
}
}
// Domain allowlist for production
allowedDomains := []string{"claude.ai", "anthropic.com"}
hostname := strings.ToLower(parsed.Hostname())
allowed := false
for _, domain := range allowedDomains {
if hostname == domain || strings.HasSuffix(hostname, "."+domain) {
allowed = true
break
}
}
if !allowed {
return fmt.Errorf("redirect domain not in allowlist")
}
return nil
}
Conclusion:
After exhaustive exploitation attempts across multiple attack vectors and internal targets, I have determined with high confidence that SSRF-VULN-01 is a FALSE POSITIVE for SSRF classification. The vulnerability is correctly identified as a lack of redirect URI validation, but the exploitation mechanism (HTTP 303 client-side redirect) does not allow server-side network boundary bypass or internal service access.
The vulnerability should be reclassified as:
- Type: Open Redirect + OAuth Security Misconfiguration
- Severity: Medium (was incorrectly rated High for SSRF impact)
- Exploitability: High (unauthenticated registration, no validation)
- Impact: Phishing, OAuth code theft, but NOT internal network access
Exploitation Classification Decision Framework Applied:
"Is this preventing factor a security implementation designed to stop this attack, or an external operational constraint?"
Answer: Neither. The "prevention" is the fundamental design of HTTP redirects - they are client-side by definition in the HTTP specification (RFC 7231). This is not a security control that could be bypassed; it's the architectural reality of how http.Redirect() functions.
The correct classification is: FALSE POSITIVE - The vulnerability does not enable Server-Side Request Forgery.