Skip to content
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

Closed
dunderhay opened this issue Jul 24, 2020 · 4 comments · Fixed by #1940
Closed

Comments

@dunderhay
Copy link

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:

  1. Lack of Authentication on endpoints (/import/site , /import/email and /import/group).
  2. Server Side Request Forgery (SSRF)

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:

It is possible to access the "Import Site" endpoint of the "Create Landing Page" area without cookie/API key, AKA without authentication. As such unauthenticated users who can access the admin portal can use it to perform server side request forgery attacks. Note: this is only really an issue if the admin interface is listening on anything other than 127.0.0.1, or if it is running on 127.0.0.1 on a multiuser machine accessed by non gophish admins.

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:

  • Attacks against the server itself (target other services running on localhost)
  • Internal port scanning
  • Attacks against other back-end systems e.g:
    • Abusing trust relationship between gophish and mail server
    • Querying AWS metadata service

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.

ssrf-req
ssrf-resp

@jordan-wright
Copy link
Collaborator

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!

@jordan-wright
Copy link
Collaborator

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.

@dunderhay
Copy link
Author

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?).

jordan-wright added a commit that referenced this issue Aug 19, 2020
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.
jordan-wright added a commit that referenced this issue Aug 20, 2020
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.
@jordan-wright
Copy link
Collaborator

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 169.254.169.254 used for instance metadata), and then made a configuration option that can be set to restrict access to certain address ranges, which I'll ensure is listed in the documentation.

I'll be releasing a new version of Gophish soon that includes this fix.

willl03 pushed a commit to willl03/gophish that referenced this issue Oct 20, 2020
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants