Discourse is a popular, open-source discussion platform used by thousands of online communities and companies. In September 2022, a critical security vulnerability was published that could let a malicious administrator abuse the platform to scan internal or external network ports. This issue—CVE-2022-39241—affected many self-hosted sites and cloud installs. Let’s break down what this vulnerability is, why it’s dangerous, and how you can protect your Discourse install.
What is CVE-2022-39241?
At its core, CVE-2022-39241 is a Server-Side Request Forgery (SSRF) vulnerability in the way Discourse handled outgoing webhooks.
Webhooks let Discourse send HTTP requests to external services when certain events happen (like a new post or user). But the webhook system didn’t properly restrict who or what it could talk to. A user with Admin rights could craft a webhook to call any IP address and port—including services running on your private network, localhost, or even other sites on the internet.
Trigger unwanted actions or leaks in internal services
- In some cases (not in this one), SSRF can be used to get sensitive data or compromise other parts of infrastructure
In Discourse’s case, a malicious admin could use the webhook feature to probe your infrastructure.
How the Exploit Worked
A Discourse admin could go to the “webhooks” settings area and add a webhook that points to http://127...1:80 (or any other local/internal IP and port). The webhook could be triggered (for example, by creating a new post), forcing Discourse to send a request to that internal address.
Discourse would try to connect, and based on the response (success, timeout, error), the admin could guess which ports are open.
Suppose you want to enumerate services on your local network. You could
1. Create a webhook pointing to http://192.168.1.10:3306 (MySQL port).
Trigger the webhook by performing an action matching the webhook filter.
3. Check the response in the Discourse admin interface—if the request fails instantly, the port isn’t open. If there is a connection delay or a different error, it may be open.
Let’s simulate how an admin can create a webhook via the Discourse API
curl -X POST "https://your-discourse-site.example.com/admin/api/web_hooks"; \
-H "Content-Type: application/json" \
-H "Api-Username: admin_user" \
-H "Api-Key: YOUR_API_KEY" \
-d '{
"web_hook": {
"name": "SSRF Test",
"payload_url": "http://127...1:800",
"content_type": "application/json",
"active": true
}
}'
You could swap 127...1 and 800 for any IP and port you want to check. If your server is set to log outgoing errors, you’ll see what happened for each attempt.
(test-passed)
If you run a self-hosted Discourse, update to the latest version *right away*
> Official Patch Commit
> Security Announcement
Workarounds
If you absolutely can’t patch yet, block webhook access to private IP ranges with the environment variable:
Add to app.yml under env:
DISCOURSE_BLOCKED_IP_BLOCKS: 127...1/8,10.../8,192.168../16,172.16../12
This prevents webhooks from calling IPs on those ranges. The setting overrides the blocked_ip_blocks admin option.
More details
- Discourse Meta: Webhook SSRF security issue
Summary
- CVE-2022-39241 lets a malicious Discourse admin scan and probe internal/external networks via SSRF in webhooks
Set the blocked IP blocks variable as a defense in depth
Discourse moved quickly to patch and disclose this issue. But it’s a reminder: *even trusted admins can make mistakes or become malicious—lock down what your apps can do!*
References
- NVD Entry: CVE-2022-39241
- Discourse Security Announcement
- Patch Commit on GitHub
If you found this post useful, bookmark for your next Discourse upgrade!
Timeline
Published on: 11/02/2022 17:15:00 UTC
Last modified on: 11/04/2022 15:37:00 UTC