[Discourse] RCE in AWS Simple Notification Service Webhook due to bad Certificate Validation Logic
Discourse exposes a webhook that takes a user-provided “subscribe URL” and passes it into open()
unsanitized. Due to Discourse being written in Ruby, it’s possible to get command execution via the subscribe URL by way of the pipeline operator. However, the payload containing this URL is supposed to be signed by AWS, and a valid and verified certificate URL must be provided for the request to succeed.
Various checks are performed on the certificate URL, including verifying it’s HTTPS, and ensuring the URL pattern matches with the sns.*.amazonaws.com
host. The path must also be a .pem
extension. It’s possible that an attacker can reflect a fake certificate in an AWS page to bypass these checks and provide a seemingly valid signature. Further, because Ruby’s X509 certificate parser does loose parsing, it won’t reject a certificate with unrecognized data before and after the cert. So by getting a fake cert reflected in a page’s contents, these checks can be bypassed and a malicious payload can be signed.
Initially, they tried abusing the error page’s reflection of an invalid action to fake a cert. This wasn’t sufficient though, because an error page would serve a 400 error code, and the certificate request needed to receive a 200 OK. The final attack was to use the GetEndpointAttributes
method, which allows you to pass a CustomUserData
parameter that gets reflected in the page. This allows an attacker to bypass all the necessary checks and exploit the open()
call to launch a reverse shell.