New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Server Side Request Forgery via Import Site feature (related to closed issue #1026) #1908
Comments
Hi there! I'll get back to this as soon as I can, but in the future I'd ask that you please send any future security issues to hi@getgophish.com rather than creating a public issue. Thanks for getting in touch! |
Ok, thanks for the patience. I'll say that this was also reported a couple of weeks ago via email and since then I've been thinking about how to handle this. Normally, SSRF issues are straightforward since you don't expect internal URLs or services to be hit. Just restrict those and problem (largely) solved. But in Gophish, that's simply not the case. There have been multiple folks in the issues that are trying to import landing pages from localhost. And while that is ideally not the most common thing, it would definitely be expected that people may want to import landing pages that are on the internal network. Any place where we allow network connections to be established from Gophish, we expect internal services to be candidates for that. That's makes it difficult to implement good SSRF protection. I think there's an argument that importing sites is the most dangerous here since we actual render the returned HTML, vs. other places where maybe we only try to establish a connection and give a 👍 or 👎 . So at this point, we pretty much know that a blocklist is probably the route we need to go. But we also know that internal services, localhost, etc. are all valid to be requested. So we could go the route of blocking very, very few things (like metadata URLs) but it just feels like it's extra effort and it'll always be a cat/mouse kind of scenario. Alternatively, we could take the route of blocking access to internal services by default, and allowing an override via an environment variable or something. It's more effort, but it'd be possible. Curious to get your thoughts on this. |
Hey Jordan, Yeah agree with you that in this use case, the SSRF is really tricky to try prevent. Also valid input that folks might want to clone their landing page from an internal resource (never done this myself personally). Like you said, the blocklist approach is very cat/mouse and ends up being a lot of work. I do like the second suggestion you mentioned (environment variable or setting to allow fetching resources from dangerous endpoints - or something like that?). |
This fixes #1908 by creating a *net.Dialer which restricts outbound connections to only allowed IP ranges. This implementation is based on the blog post at https://www.agwa.name/blog/post/preventing_server_side_request_forgery_in_golang To keep things backwards compatible, by default we'll only block connections to 169.254.169.254, the link-local IP address commonly used in cloud environments to retrieve metadata about the running instance. For other internal addresses (e.g. localhost or RFC 1918 addresses), it's assumed that those are available to Gophish. To support more secure environments, we introduce the `allowed_internal_hosts` configuration option where an admin can set one or more IP ranges in CIDR format. If addresses are specified here, then all internal connections will be blocked except to these hosts. I've done various testing, but could use a second set of eyes here to make sure I didn't break anything. There's various bits about this approach I don't really like. For example, since various packages all need this functionality, I had to make the RestrictedDialer a global singleton rather than a dependency off of, say, the admin server. Additionally, since webhooks are implemented via a singleton, I had to introduce a new function, `SetTransport`. Finally, I had to make an update in the gomail package to support a custom net.Dialer.
Initial commit of SSRF mitigations. This fixes #1908 by creating a *net.Dialer which restricts outbound connections to only allowed IP ranges. This implementation is based on the blog post at https://www.agwa.name/blog/post/preventing_server_side_request_forgery_in_golang To keep things backwards compatible, by default we'll only block connections to 169.254.169.254, the link-local IP address commonly used in cloud environments to retrieve metadata about the running instance. For other internal addresses (e.g. localhost or RFC 1918 addresses), it's assumed that those are available to Gophish. To support more secure environments, we introduce the `allowed_internal_hosts` configuration option where an admin can set one or more IP ranges in CIDR format. If addresses are specified here, then all internal connections will be blocked except to these hosts. There are various bits about this approach I don't really like. For example, since various packages all need this functionality, I had to make the RestrictedDialer a global singleton rather than a dependency off of, say, the admin server. Additionally, since webhooks are implemented via a singleton, I had to introduce a new function, `SetTransport`. Finally, I had to make an update in the gomail package to support a custom net.Dialer.
Hey @dunderhay, I've opted for a slightly different approach. Since it's very expected behavior for Gophish to contact internal hosts, I've opted to have a very small denylist by default (e.g. only the common link-local IP address I'll be releasing a new version of Gophish soon that includes this fix. |
Initial commit of SSRF mitigations. This fixes gophish#1908 by creating a *net.Dialer which restricts outbound connections to only allowed IP ranges. This implementation is based on the blog post at https://www.agwa.name/blog/post/preventing_server_side_request_forgery_in_golang To keep things backwards compatible, by default we'll only block connections to 169.254.169.254, the link-local IP address commonly used in cloud environments to retrieve metadata about the running instance. For other internal addresses (e.g. localhost or RFC 1918 addresses), it's assumed that those are available to Gophish. To support more secure environments, we introduce the `allowed_internal_hosts` configuration option where an admin can set one or more IP ranges in CIDR format. If addresses are specified here, then all internal connections will be blocked except to these hosts. There are various bits about this approach I don't really like. For example, since various packages all need this functionality, I had to make the RestrictedDialer a global singleton rather than a dependency off of, say, the admin server. Additionally, since webhooks are implemented via a singleton, I had to introduce a new function, `SetTransport`. Finally, I had to make an update in the gomail package to support a custom net.Dialer.
What version of Gophish are you using?:
Gophish v0.10.1
Brief description of the issue:
This issue has previously been raised here #1026. It appears there were actually two seperate issues reported originally:
The fix for the authentication was implemented but the SSRF is still exploitable and appears that there is no fix for this (it is functionality acting as intended). By nature, the import site functionality needs to be able to make requests to any external IP address or domain name in order to clone the site. So an allow listing approach is not really feasible. Perhaps a regex to block requests to localhost, internal IP address ranges and AWS endpoints, but this just seems like a never ending battle? 🤷♂️
The account for the person who originally reported the issue appears to have been deleted but as per their issue:
I have struck out the text which is no longer relevant to reflect the authentication updates which have been made. The endpoints now require authentication but it is still possible to abuse the SSRF vulnerability to achieve the following:
Note: AWS has added support for IMDSv2 to mitigate SSRF attacks against AWS metadata service. Also I have checked if various protocols and URL schemas (ssh, smb, file, gopher, dict, etc) and had no luck with any of these being supported.
What are you expecting to see happen?
Expecting to not be able to port scan localhost, internal IP address ranges, and access AWS metadata service.
What are you seeing happen?
It is possible to query the AWS metadata service and port scan localhost / internal IP address ranges. For example, it is possible to fetch the EC2 instance identity document (
http://169.254.169.254/latest/dynamic/instance-identity/document
) by inserting this as the endpoint to attempt to clone in the "Import Site" feature.The text was updated successfully, but these errors were encountered: