Not: Bu, Def Con 30 Cloud Village Lightning konuşmamın “Metin Notları” versiyonudur. Konuşma kaydedilmedi, bu yüzden bu onun tek kamu versiyonu. AWS’ye kamuoyu tartışmasından önce sunumu incelediği için çok teşekkürler.
Sayısız uygulama, Amazon Web Services’in webhooks ve geri arama gibi uygulama-uygulama iletişimi için basit bildirim hizmetine güvenmektedir. Bu mesajların özgünlüğünü doğrulamak için, bu projeler imzalama certanlığı değerine dayalı sertifika tabanlı imza doğrulaması kullanır. Ne yazık ki, resmi AWS SDK’larındaki bir boşluk, saldırganların tüm SNS HTTP abonelerine mesaj oluşturmasına izin verdi.
Amazon Basit Bildirim Hizmeti (SNS), bugün sunucusuz ekosistemde neredeyse temel olacak kadar yaygınlaşan teknolojilerden biridir. Fikrin kendisi yeni değil: Publisher-aboner mesajlaşma yazılımı, Apache Kafka’dan Rabbitmq’e kadar uzun zamandır var. SNS, farklı uygulamaların aralarında mesajlar vererek uzaktan iletişim kurmasına izin verir. Diğer mesajlaşma yazılımı genellikle kuyrukları içerirken, SNS mümkün olduğunca basit tutar (AWS, Amazon Basit Kuyruk Hizmeti – evet…) olarak sıralı olarak sıralı işlevselliği satar). Aynı zamanda, basit bir HTTP Webhook’u kendiniz uygulamaktan çok daha kullanışlıdır – SNS, FIFO mesajlaşma, kolay ölçeklenebilirlik, geniş yayıncı/abone desteği, mesaj filtreleme ve daha fazlasını destekler.
Tipik bir Amazon SNS kullanım durumu, uygulama-uygulama fanout modelidir. Örneğin, bir görüntü yükleme ve dönüştürme iş akışı, bir SNS konusuna yayınlanan bir S3 Ingest/Yükleme olay bildirim mesajına soyutlanabilir. SNS konusu, bu mesajı ayrı S3 kovalarında saklamadan önce görüntüyü farklı formatlara ve boyutlara dönüştüren birkaç farklı AWS Lambda abonesine iletir.
Kısacası, SNS, çeşitli olay kaynakları ve hedefler arasındaki tutkal (AWS tutkalıyla karıştırılmamalıdır – evet…) gibi davranır. SNS tarafından desteklenen en popüler hedeflerden bazıları e -posta, SMS ve HTTP/S’dir – örneğin, bir kullanıcının uygulamalarından abone olmasını veya bir Webhook iş akışını tetiklediğini geliştiricilere bildirin.
Tabii ki, sahne arkasında SNS temelde daha özellik dolu bir HTTP Webhook hizmeti var. SNS’yi bir HTTP/S hedefine yayınlayacak şekilde yapılandırdığınızda, aşağıdaki isteği uç noktanıza gönderir:
POST / HTTP/1.1
x-amz-sns-message-type: Notification
x-amz-sns-message-id: 22b80b92-fdea-4c2c-8f9d-bdfb0c7bf324
x-amz-sns-topic-arn: arn:aws:sns:us-west-2:123456789012:MyTopic
x-amz-sns-subscription-arn: arn:aws:sns:us-west-2:123456789012:MyTopic:c9135db0-26c4-47ec-8998-413945fb5a96
Content-Length: 773
Content-Type: text/plain; charset=UTF-8
Host: example.com
Connection: Keep-Alive
User-Agent: Amazon Simple Notification Service Agent
{
"Type" : "Notification",
"MessageId" : "22b80b92-fdea-4c2c-8f9d-bdfb0c7bf324",
"TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
"Subject" : "My First Message",
"Message" : "Hello world!",
"Timestamp" : "2012-05-02T00:54:06.655Z",
"SignatureVersion" : "1",
"Signature" : "EXAMPLEw6JRN...",
"SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
"UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:123456789012:MyTopic:c9135db0-26c4-47ec-8998-413945fb5a96"
}
Olay kaynağınıza bağlı olarak, konu ve mesaj ihtiyacınız olan verileri içerir. Ancak bu şu soruyu akla getirir: Webhook uç noktanız herkese açıksa, aldığınız isteklerin Amazon SNS’den geldiğine nasıl güveniyorsunuz? Nihayet:
Neredeyse hiç kimse ilk denemede jwt jetonları ve web şövalyeleri almadı. Webhooks ile, insanlar neredeyse her zaman gelen istekleri doğrulamayı unuttular… “
Ken Kantzer, “5 yıllık teknoloji başlangıç kodu denetimlerinden öğrenme”
Burası burada Signature
Ve SigningCertURL
Değerler devreye girer. AWS belgeleri doğrulama algoritmasının basit bir açıklamasına sahiptir; ilgili isim-değeri çiftleri (örneğin Subject
Ve Message
) JSON gövdesinden çıkarılır ve kanonik bir formatta düzenlenir, daha sonra SHA1 kullanılarak karma türetilmiş karma değeri.
Sonra, Signature
Değer Base64 kodludur, daha sonra indirilen genel anahtar kullanılarak şifre çözülmüştür. SigningCertURL
yaratmak için iddia edilen karma değeri.
Son olarak, iddia edilen ve türetilmiş karma değerler, eşleştiklerinden emin olmak için karşılaştırılır. Bu, Computerfile’ın çok iyi açıkladığı standart bir imza doğrulama şemasıdır.
Bu noktada olası bir zayıflık görmüş olabilirsiniz: Sertifikayı şu adresten kullanmamız gerekiyor. SigningCertURL
sözde doğru karma değerini oluşturmak için, ancak nasıl güveniyoruz? SigningCertURL
? Ay, ovma var…
AWS Bilgi Merkezi şu cevabı verir:
Sahtekarlık saldırılarını önlemeye yardımcı olmak için Amazon SNS mesaj imzalarını doğrularken aşağıdakileri yaptığınızdan emin olun:
- Amazon SNS’den sertifika almak için her zaman HTTPS kullanın.
- Sertifikanın özgünlüğünü doğrulayın.
- Sertifikanın Amazon SNS’den gönderildiğini doğrulayın.
- (Mümkün olduğunda) Mesajları doğrulamak ve doğrulamak için Amazon SNS için desteklenen AWS SDK’larından birini kullanın.
Tamam, bu yüzden bize söyler Verify that the certificate was sent from Amazon SNS
Bize nasıl olduğunu söylemeden. Ayrıca, doğrulamadan imza doğrulamasını *gerçekleştiren örnek bir python komut dosyası sağlar. SigningCertURL
!
import base64
from M2Crypto import EVP, RSA, X509
import requests
cache = dict()
# Extract the name-value pairs from the JSON document in the body of the HTTP POST request that Amazon SNS sent to your endpoint.
def processMessage(messagePayload):
print ("Start!")
if (messagePayload["SignatureVersion"] != "1"):
print("Unexpected signature version. Unable to verify signature.")
return False
messagePayload["TopicArn"] = messagePayload["TopicArn"].replace(" ", "")
signatureFields = fieldsForSignature(messagePayload["Type"])
print(signatureFields)
strToSign = getSignatureFields(messagePayload, signatureFields)
print(strToSign)
certStr = getCert(messagePayload)
print("Printing the cert")
print(certStr.text)
print("Using M2Crypto")
# Get the X509 certificate that Amazon SNS used to sign the message.
certificateSNS = X509.load_cert_string(certStr.text)
#Extract the public key from the certificate.
public_keySNS = certificateSNS.get_pubkey()
public_keySNS.reset_context(md = "sha1")
# Generate the derived hash value of the Amazon SNS message.
# Generate the asserted hash value of the Amazon SNS message.
public_keySNS.verify_init()
public_keySNS.verify_update(strToSign.encode())
# Decode the Signature value
decoded_signature = base64.b64decode(messagePayload["Signature"])
# Verify the authenticity and integrity of the Amazon SNS message
verification_result = public_keySNS.verify_final(decoded_signature)
print("verification_result", verification_result)
if verification_result != 1:
print("Signature could not be verified")
return False
else:
return True
# Obtain the fields for signature based on message type.
def fieldsForSignature(type):
if (type == "SubscriptionConfirmation" or type == "UnsubscribeConfirmation"):
return ["Message", "MessageId", "SubscribeURL", "Timestamp", "Token", "TopicArn", "Type"]
elif (type == "Notification"):
return ["Message", "MessageId", "Subject", "Timestamp", "TopicArn", "Type"]
else:
return []
# Create the string to sign.
def getSignatureFields(messagePayload, signatureFields):
signatureStr = ""
for key in signatureFields:
if key in messagePayload:
signatureStr += (key + "\n" + messagePayload[key] + "\n")
return signatureStr
#**** Certificate Fetching ****
#Certificate caching
def get_cert_from_server(url):
print("Fetching cert from server...")
response = requests.get(url)
return response
def get_cert(url):
print("Getting cert...")
if url not in cache:
cache[url] = get_cert_from_server(url)
return cache[url]
def getCert(messagePayload):
certLoc = messagePayload["SigningCertURL"].replace(" ", "")
print("Cert location", certLoc)
responseCert = get_cert(certLoc)
return responseCert
. SigningCertURL
/url
Değer, herhangi bir doğrulama adımı olmadan doğrudan getirilir. Bu arada, StackOverflow’a dönersek, en iyi Google Sonuç, URL’nin formatı eşleştirdiğini doğrulamayı önerir. sns.${region}.amazonaws.com
. Yeterince mantıklı, ancak not alacak çeşitli incelikler var.
Örneğin, bir geliştirici URL’nin ile başladığını doğrulamak isteyebilir. https://
içerir sns
Alt alandaki ilk sümüklü olarak, ikinci sümüklü olarak geçerli bir bölge dizesi içerir, daha sonra amazonaws.com
ihtisas. Ayrıca, geliştirici yolun ile bitmesini sağlamak isteyebilir. .pem
eklenti.
Yüzeyde, bu URL’nin gerçekten varsayılan AWS sertifika konumlarından birine ait olduğundan emin olmak için yeterli görünür. Ancak, bir kritik boşluk var: Amazon S3.
Daha spesifik olarak, Amazon S3 kova kaynaklarına şu adresten erişilebilir. https://
yukarıda açıklanan kontrolleri kolayca geçirir! Kendi oluşturulan genel anahtarlarını yükleyerek https://mysns.s3-us-west-2.amazonaws.com/evil.pem
bir saldırgan kurbanın webhook uç noktasına imzalı bir SNS mesajı kolayca oluşturabilir.
Özel SNS’nin hızlı açık kaynaklı kod incelemesinde SigningCertURL
Doğrulama rutinleri, bu tür kırık algoritmaların yanı sıra, https?://sns\.(.+)\.amazonaws\.com
(tarafından atlandı http://sns.evil.s3.amazonaws.com/evil.pem
) Ve sns.[a-z0-9\-]+.amazonaws.com
(tarafından atlandı snsthisismyamazonaws.com
).
Tamam, bu yüzden URL doğrulaması zor. Ancak, AWS yararlı bir şekilde sağlar AWS SDKs for Amazon SNS to validate and verify messages
. Örneğin AWS, sns-validator
NPM’de paket. Paket kodu aşağıdaki Regex’i kullanır:
defaultHostPattern = /^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/,
// hostPattern defaults to defaultHostPattern
var validateUrl = function (urlToValidate, hostPattern) {
var parsed = url.parse(urlToValidate);
return parsed.protocol === 'https:'
&& parsed.path.substr(-4) === '.pem'
&& hostPattern.test(parsed.host);
};
^sns\.
ilk sümüklü böceklerin yerine geçtiğini kontrol eder sns
; Bu, gibi kova adlarını bloke eder: mysns
. Ancak, Regex’in geri kalanı hala .s3-us-west-2.amazonaws.com
alt alan sonek. Neyse ki, s3-us-west-2.amazonaws.com
çalışıyor (çalıştı mı?) Aynı s3.amazonaws.com
bu nedenle alan adındaki ikinci sümük için minimum 3 karakter gereksinimini geçer ([a-zA-Z0-9\-]{3,}
). Bu bırakıyor sns
S3 kova bu Regex için mümkün olan tek eşleşme.
Elbette böyle kritik bir kova adı ayrılacak, değil mi?
- Sorun 1:
sns
ayrılmamış. - Sorun 2:
sns
halka açık bir kovadır. - Sorun 3:
sns
halka açık bir kovadır.
Bu boşluk sayesinde, bir saldırgan herhangi bir resmi SDK SNS doğrulayıcı kullanıcısına mesajlar oluşturabilir. Etki, uygulamanın Webhook işleyicisine bağlıdır. Örneğin, kullanıcıların çevrimiçi veri ihlallerini izlemek için e-postalarını kaydettirmelerini sağlayan bir araç olan Firefox Monitor, https://monitor.firefox.com/ses/notification adresinde herkese açık olarak erişilebilir bir SNS Webhook uç noktasına sahiptir. sns-validator
Gelen yayın mesajlarını doğrulamak için:
'use strict'
const MessageValidator = require('sns-validator')
const DB = require('../db/DB')
const mozlog = require('../log')
const validator = new MessageValidator()
const log = mozlog('controllers.ses')
async function notification (req, res) {
const message = JSON.parse(req.body)
return new Promise((resolve, reject) => {
validator.validate(message, async (err, message) => {
if (err) {
log.error('notification', { err })
const body = 'Access denied. ' + err.message
res.status(401).send(body)
return reject(body)
}
await handleNotification(message)
res.status(200).json(
{ status: 'OK' }
)
return resolve('OK')
})
})
}
Mesajı doğruladıktan sonra, doğrulanmış mesajdan alınan değerleri kullanarak kullanıcıları veritabanından siler:
async function handleNotification (notification) {
log.info('received-SNS', { id: notification.MessageId })
const message = JSON.parse(notification.Message)
if (message.hasOwnProperty('eventType')) {
await handleSESMessage(message)
}
if (message.hasOwnProperty('event')) {
await handleFxAMessage(message)
}
}
async function handleFxAMessage (message) {
switch (message.event) {
case 'delete':
await handleDeleteMessage(message)
break
default:
log.info('unhandled-event', { event: message.event })
}
}
async function handleDeleteMessage (message) {
await DB.deleteSubscriberByFxAUID(message.uid)
}
async function handleSESMessage (message) {
switch (message.eventType) {
case 'Bounce':
await handleBounceMessage(message)
break
case 'Complaint':
await handleComplaintMessage(message)
break
default:
log.info('unhandled-eventType', { type: message.eventType })
}
}
async function handleBounceMessage (message) {
const bounce = message.bounce
if (bounce.bounceType === 'Permanent') {
return await removeSubscribersFromDB(bounce.bouncedRecipients)
}
}
async function handleComplaintMessage (message) {
const complaint = message.complaint
return await removeSubscribersFromDB(complaint.complainedRecipients)
}
async function removeSubscribersFromDB (recipients) {
for (const recipient of recipients) {
await DB.removeEmail(recipient.emailAddress)
}
}
Bu nedenle, bir saldırgan SNS mesajlarını oluşturarak keyfi kullanıcıları Firefox Monitör veritabanından silebilir.
Raporladıktan sonra AWS’nin yanıtından etkilendim. Güvenlik açığını altyapı ucundaki zarif bir çözüm yoluyla hızla çözdüler ve S3’ten gelecekteki alt alan adı ad alanı çatışmalarını önleyerek. AWS, Regex’i SDK tarafından yamalama ihtiyacından kaçınarak binlerce uyarıyı tetiklemeyi engelledi ve geriye doğru uyumluluğu sağladı. Bu tür S3 kova adları oluşturmak da artık mümkün değil. Bununla birlikte, bu çözüm sadece AWS SDK kullanıcıları için geçerlidir; Geliştiriciler kendilerine dönüyorsa SigningCertURL
Doğrulama algoritması (veya doğrulanamıyor), saldırganlar ilerleyebilir…
Genel olarak, bu, günümüzün sunucusuz bulutunun sıhhi tesisatında korkak kılavuzun okunması ve okunmasının verimli bir yolculuğuydu. Herhangi bir hizmet için AWS’nin mükemmel geliştirici belgelerine bir göz atın – sadece ilginç bir şey bulabilirsiniz.