Όταν το Node2Shell (CVE-2026-111497) ξέφυγε από τη Node.js και έγινεHTTP2Shell
Ένα out-of-bounds write σε έναν Brotli decoder γραμμένο σε C — δεμένο μέσα στα runtimes που θεωρούσαμε «ασφαλή υποδομή». Ξεκίνησε ως bug της Node.js. Τελείωσε ως κλάση τρωτότητας που αγγίζει κάθε HTTP client, agent, CLI και CI runner που αποσυμπιέζει bytes που δεν ελέγχει. Αυτό το άρθρο είναι ο πλήρης χάρτης: ανατομία, μετάλλαξη, blast radius, kill chains, ανίχνευση και πλάνο μετριασμού.
$ cve-inspect --id CVE-2026-111497 alias : Node2Shell → HTTP2Shell type : out-of-bounds write (native C) component : bundled Brotli decoder carriers : Node.js · Bun · Python (brotli / brotlicffi) entry : HTTP απόκριση με Content-Encoding: br impact : heap corruption → RCE patched : Node 22.23.4 / 24.22.1 / 26.12.1 · Bun latest status : public PoCs · όχι (ακόμη) στο CISA KEV
Σε ενενήντα δευτερόλεπτα
Αν δεν διαβάσεις τίποτα άλλο, διάβασε τον παρακάτω πίνακα. Κάθε γραμμή είναι ένα δεδομένο που χρειάζεται για να αποφασίσεις πόσο σε αφορά — και με ποια σειρά θα κάνεις actions.
Το σενάριο που να μείνει: ένα service κάνει server-side fetch σε URL που επηρεάζεται από input χρήστη — ακόμα κι έμμεσα, μέσω redirects, previews ή webhooks. Ο attacker απαντά με Content-Encoding: br και crafted brotli stream. Ο decoder διαβρώνει τη μνήμη της διεργασίας μέσα στο εσωτερικό σου δίκτυο. Από εκεί: metadata endpoints, tokens, lateral movement.
Χρονολόγιο: εννέα μέρες που άλλαξαν το bug
Το HTTP2Shell δεν γεννήθηκε έτοιμο. Είναι αποτέλεσμα αλυσιδωτών αποκαλύψεων — και του πώς η ερευνητική κοινότητα «αναδιαβάζει» ένα advisory μετά τη δημοσίευσή του. Η σειρά έχει σημασία: δείχνει πώς ένα ήδη patchαρισμένο bug μετατρέπεται σε κλάση τρωτότητας.
Το προοίμιο: PHP2Shell
Δύο κρίσιμα RCE στην PHP (CVE-2026-109618, CVE-2026-109619) δημοσιοποιούνται και patchάρονται βιαστικά. Θέμα: native parsing & compression primitives ως επιθετική επιφάνεια. Η αγορά security «ζεσταίνεται» πάνω στην ιδέα.
Node2Shell — το advisory
Ανακοινώνεται το CVE-2026-111497: out-of-bounds write στον bundled Brotli decoder της Node.js και του Bun. Κυκλοφορούν οι διορθωμένες εκδόσεις 22.23.4, 24.22.1, 26.12.1. Η αρχική ανάγνωση: «Node servers — patchάρετε».
Πρώτα PoCs
Εντός 24ώρου εμφανίζονται δημόσια PoC repositories. Στη γειτονική οικογένεια, το Exploit-DB καταγράφει το #52704 (PHP2Shell, 22/10). Το bug παύει να είναι θεωρητικό.
Η επέκταση σε Python
Έρευνα της Aikido τεκμηριώνει ότι τα Python packages brotli και brotlicffi είναι οχήματα της ίδιας λογικής ευπάθειας — και μέσω αυτών, κάθε Python HTTP stack που κάνει transparent decompression. Το blast radius βγαίνει από τη JavaScript.
Η μετονομασία: HTTP2Shell
Ο κλάδος υιοθετεί τον όρο HTTP2Shell (HTTP-to-Shell): όχι πια «bug της Node.js», αλλά κλάση τρωτότητας. Την ίδια μέρα το CISA KEV προσθέτει το CVE-2026-114625 (Arista VeloCloud Orchestrator SQLi) και το CVE-2026-110447 (Collabora Online) — τεχνικά άσχετα, αλλά με κοινό θέμα: επιφάνειες που «απλώς δέχονται και επεξεργάζονται» δεδομένα.
Το παρόν
Καμία τεκμηριωμένη μαζική in-the-wild εκμετάλλευση που να οδηγεί σε KEV — αλλά τα PoCs είναι δημόσια και το παράθυρο ανοίγει. Στόχος του άρθρου: μετατροπή του «advisory fatigue» σε εκτελέσιμο πλάνο.
Ανατομία: από byte σε shell
3.1 · Τι είναι ο Brotli — και γιατί είναι παντού context
Ο Brotli είναι ο αλγόριθμος συμπίεσης που έφερε η Google για το web: μικρότερα payloads από gzip σε σχεδόν κάθε περιεχόμενο, και μια σχεδόν απόλυτη υιοθέτηση από τα σύγχρονα HTTP stacks. Ο server δηλώνει Content-Encoding: br, ο client το καταλαβαίνει, όλοι χαίρονται. Το κόστος αυτής της ευκολίας κρύβεται σε μια λεπτομέρεια αρχιτεκτονικής: η reference υλοποίηση του Brotli είναι γραμμένη σε C — γλώσσα χωρίς αυτόματο έλεγχο ορίων μνήμης.
Τα runtimes δεν ξαναγράφουν τον decoder από το μηδέν. Τον ενσωματώνουν: η Node.js τον κουβαλάει χρόνια για να υποστηρίζει brotli αποκρίσεων, το Bun έκανε το ίδιο. Αυτό σημαίνει ότι ο C κώδικας τρέχει μέσα στη διεργασία της εφαρμογής σου — ίδιος heap, ίδιες permissions, κανένα sandbox ανάμεσα. Όταν εκεί μέσα υπάρχει out-of-bounds write, δεν μιλάμε για «λάθος λογικής» που επιστρέφει λάθος αποτέλεσμα. Μιλάμε για εγγραφή σε μνήμη που δεν της αναλογεί — με όλες τις συνέπειες που έχει αυτό σε επίπεδο μηχανής.
3.2 · Out-of-bounds write: γιατί είναι χειρότερο από read primitive
Η κοινότητα έχει ζήσει ξανά το «native decoder τρώει untrusted bytes»: το Heartbleed ήταν out-of-bounds read — διάβαζε μνήμη που δεν έπρεπε και τη διέρρεε. Το read είναι σοβαρό. Το write όμως είναι άλλη κατηγορία: δεν σεναριάζει μόνο «τι έμαθε ο attacker», αλλά «τι μπορεί να κάνει ο attacker».
Μια εγγραφή εκτός ορίων διαβρώνει ό,τι βρεθεί δίπλα: metadata του heap, αντικείμενα γειτονικά, pointers συναρτήσεων. Με σωστή προετοιμασία (heap grooming — δηλαδή στοίχειωση του heap ώστε το «δίπλα» να είναι ακριβώς ό,τι σε συμφέρει), η διαβρώση μετατρέπεται σε έλεγχο ροής εκτέλεσης — και από εκεί σε εκτέλεση κώδικα επιλογής του attacker. Είναι δύσκολο, context-sensitive, και εξαρτάται από build: allocators, ASLR, hardening flags. Αλλά τα δημόσια PoCs που εμφανίστηκαν αμέσως μετά το advisory αποδεικνύουν το προφανές: εφικτό.
Η βασική εξίσωση: decompression σημαίνει parsing δεδομένων που έρχονται από άλλον. Αν τα bytes της απόκρισης τα ελέγχει ο attacker, τότε εκείνος τρέχει —κατόπιν αιτήματός του— έναν C parser μέσα στη διεργασία σου. Το write σπανίζει περισσότερο από το read, κοστίζει περισσότερο, και γι' αυτό αξίζει περισσότερο στην αγορά των exploits.
3.3 · Το μοτίβο του θύματος victim pattern
Ποιος είναι ο κοινός παρονομαστής όλων των πιθανών θυμάτων; Ο κώδικας που κάνει HTTP fetch χωρίς να γνωρίζει ότι συμμετέχει σε decompression. Δες πόσο αθώο μοιάζει:
// ΚΑΘΕ service που κάνει κάτι τέτοιο είναι ύποπτο: const res = await fetch(userInfluencedUrl, { headers: { 'accept-encoding': 'gzip, deflate, br' } // ← το br στο προεπιλεγμένο σετ }); // Η αποσυμπίεση είναι ΑΟΡΑΤΗ στον developer: const body = await res.text(); // ← εδώ, μέσα στη διεργασία, τρέχει ο C decoder // Ο developer δεν είδε ποτέ το "br". Δεν το ζήτησε ρητά. // Απλώς συμβαίνει — γιατί έτσι συμπεριφέρονται όλοι οι HTTP clients.
Η κεφαλίδα Content-Encoding: br απαντάται σε δύο γραμμές από οποιονδήποτε HTTP server-παγίδα. Δεν χρειάζεται ιδιαίτερη ρύθμιση από τον attacker: η διαπραγμάτευση έχει γίνει από τον ίδιο σου τον client. Η ευκολία αυτή είναι που κάνει το primitive τόσο ευρύ.
3.4 · Η αλυσίδα εκμετάλλευσης kill path
- Αξιοπιστία ανά build: τα native heap exploits είναι context-sensitive. Σε παραγωγικά συστήματα με custom allocators, hardening ή ασυνήθιστες εκδόσεις, η αξιοπιστία πέφτει — αλλά το DoS (crash) ως ελάχιστο αποτέλεσμα μένει.
- Τι μπορεί μετά ο attacker: ό,τι κάνει η διεργασία: ανάγνωση αρχείων, credentials από environment, tokens στη μνήμη, εξερχόμενες κλήσεις δικτύου. Σε containers, απόπειρες escape ανάλογα με τη διαμόρφωση.
- Ο ήσσων κακός: ακόμη και αποτυχημένη εκμετάλλευση αφήνει ίχνη: segfaults μέσα σε brotli symbols — δείτε την ενότητα ανίχνευσης.
Η μετάλλαξη: γιατί «Node2Shell» ήταν λάθος όνομα
Τα ονόματα ευπαθειών δεν είναι διαφημίσεις — είναι μοντέλα σκέψης. Όταν η αρχική ονοματολογία έδειχνε «Node», οι ομάδες που δεν έτρεχαν Node.js καρφίτσωσαν το advisory στον πίνακα «later». Αυτό ήταν το ακριβέστερο λάθος της εβδομάδας: το bug δεν ζει στην Node.js ως πλατφόρμα, αλλά σε έναν C decoder που η Node.js τυχαίνει να κουβαλάει — και τον οποίο κουβαλάνε και άλλοι.
Με αυτόν τον φακό, η «επέκταση» σε Python δεν είναι καμία έκπληξη. Η έρευνα της Aikido τεκμηρίωσε ότι τα ευρέως διαδεδομένα πακέτα brotli και brotlicffi λειτουργούν ως οχήματα: μόλις υπάρχουν στο environment, αρκετοί HTTP clients της Python ενεργοποιούν αυτόματα τη διαπραγμάτευση και αποσυμπίεση του brotli. Ο developer δεν έγραψε ούτε μία γραμμή γι' αυτό. Η ύπαρξη ενός πακέτου σε ένα requirements.txt άλλαξε συμπεριφορά ασφαλείας ολόκληρης της εφαρμογής. Αυτό είναι το οριστικό supply-chain μάθημα του HTTP2Shell.
Ο κανόνας του HTTP2Shell, μια φορά: «Αν η διεργασία σου αποσυμπιέζει brotli bytes που μπορεί να επηρεάσει τρίτος, και ο decoder είναι ο ευάλωτος, τότε είσαι επιφάνεια επίθεσης — ανεξαρτήτως γλώσσας, runtime ή οικοσυστήματος». Το HTTP2Shell δεν είναι ευπάθεια· είναι ο κάτοχος χάρτης της ευπάθειας.
4.1 · Ρητός κώδικας vs αόρατος κώδικας surface
🎯 Ρητός κώδικας
Άμεσες κλήσεις σε decompression APIs, custom decoders, ρητές ρυθμίσεις encodings. Εδώ τουλάχιστον ξέρεις ότι υπάρχει η επιφάνεια: τη βλέπεις σε code review, τη βάζεις σε SBOM, τη φυλάς.
👻 Αόρατος κώδικας
Το Content-Encoding handling μέσα στα HTTP clients. Κανείς δεν το ζητά ρητά, κανείς δεν το βλέπει. Είναι η «υδάτινη στήριξη» του εκμεταλλεύσιμου surface — και υπάρχει παντού: clients, agents, SDKs, CLIs, monitoring.
Η σωστή ερώτηση δεν είναι «ποιος καλεί τον decoder» αλλά «ποιος τερματίζει bytes που μπορεί να είναι br-encoded από πηγή που δεν ελέγχεις πλήρως». Με αυτό το κριτήριο ο χάρτης των δυνητικών θυμάτων απλώνεται σε επίπεδα — και αυτό μας οδηγεί στο blast radius.
Blast radius: ο πλήρης χάρτης εκθέσεων
5.1 · Τα πέντε επίπεδα εκθέσεων tier map
| Tier | Τι είναι | Παραδείγματα | Έκθεση | Γιατί |
|---|---|---|---|---|
| TIER 0 | Runtimes με bundled decoder | Node.js (παλαιότερες εκδόσεις), Bun | ΑΜΕΣΗ | Ο ευάλωτος C κώδικας τρέχει in-process σε κάθε decompression. Κάθε fetch με br-encoded απόκριση είναι υποψήφιο trigger. |
| TIER 0.5 | Python decode packages | brotli, brotlicffi (C bindings) | ΑΜΕΣΗ, ΥΠΟ ΟΡΟΥΣ | Το binding φορτώνει C decoder στη διεργασία. Η έκθεση ενεργοποιείται από την παρουσία του πακέτου και τη χρήση του από HTTP stack. |
| TIER 2 | HTTP clients & SDKs | fetch/undici/axios/got/superagent στο Node · requests/httpx/aiohttp στην Python όταν υπάρχει brotli | ΕΜΜΕΣΗ, ΔΙΑΔΕΔΟΜΕΝΗ | Ο client διαπραγματεύεται br και αποσυμπιέζει αυτόματα. Ο developer δεν βλέπει ποτέ το encoding. |
| TIER 3 | Agents, CLIs, CI/CD | Telemetry/backup agents, package managers, artifact fetchers, monitoring daemons, CI runners | ΕΜΜΕΣΗ, ΥΠΟΤΙΜΗΜΕΝΗ | Τρέχουν unattended, κάνουν fetch από πηγές που σπάνια ελέγχονται και συχνά κρατούν τα υψηλότερα privileges του οικοσυστήματος. |
| TIER 4 | Native static consumers | Rust/Go/C++ bindings προς την upstream C library, embedded, appliances | ΑΣΑΦΗΣ | Δεν υπάρχουν τυποποιημένα advisories ανά binding. Χρειάζεται δικός σου έλεγχος: ποια έκδοση του C decoder είναι statically συνδεδεμένη. |
5.2 · Γιατί οι browsers είναι (σχετικά) ασφαλέστεροι context
Εύλογη ερώτηση: «ο browser μου κάνει fetch και decompress brotli όλη μέρα — γιατί δεν μιλάμε γι' αυτό;». Τρεις λόγοι. Πρώτον, οι μεγάλοι browsers διατηρούν δικές τους, ενεργά fuzzed υλοποιήσεις decoders — δεν κληρονομούν τυφλά την upstream C library.
Δεύτερον, η αρχιτεκτονική τους είναι sandboxed: ακόμη κι ένα επιτυχές memory corruption στον renderer πρέπει να διασχίσει επιπλέον φράγματα για να γίνει ουσιαστικό RCE. Τρίτον, το threat model τους παρακολουθείται από τις καλύτερα χρηματοδοτούμενες security ομάδες του πλανήτη. Αυτό δεν σημαίνει μηδενικό ρίσκο — σημαίνει ότι το ρεαλιστικό κέντρο βάρους του HTTP2Shell είναι το server-side & infrastructure-side HTTP: τα δικά σου services, agents και runners.
5.3 · Το γλυκό σημείο: το cloud threat model
- Πυκνότητα HTTP: τα microservices μιλούν συνεχώς HTTP μεταξύ τους — κάθε κλήση είναι υποψήφιος client-side trigger, με θύμα τον πιο privileged process της εφαρμογής.
- IMDS: τα metadata endpoints (π.χ. 169.254.169.254 και οι ομοιότητές του) μετατρέπουν οποιοδήποτε RCE σε κλοπή cloud credentials μέσα σε δευτερόλεπτα.
- Ephemeral confidence: τα containers θεωρούνται «προσωρινά», άρα συχνά υπο-παρακολούθούνται. Ένα RCE σε ephemeral pod με προσεκτική exfiltration μπορεί να μην φανεί ποτέ στα logs.
Τρία kill chains, βήμα-βήμα
Η θεωρία τελειώνει εδώ. Ακολουθούν τα τρία ρεαλιστικά σενάρια με τα οποία το HTTP2Shell γίνεται περιστατικό — το καθένα με ανάλυση «γιατί δουλεύει» και, το σημαντικότερο, «τι το σπάει».
SSRF → εσωτερικό HTTP client → RCE → κλοπή cloud identity
Η εφαρμογή έχει λειτουργία που κάνει fetch σε URL: preview εικόνων, import από link, webhook verification, URL health-check. Ο attacker καταχωρεί το δικό του endpoint. Όταν το service κάνει fetch, ο attacker-server απαντά 200 με Content-Encoding: br και crafted payload. Ο bundled decoder corruptάρει τη μνήμη της εσωτερικής διεργασίας. Σε cloud environment, το πρώτο που ψάχνει ένα shell είναι το metadata service — και μόλις πάρει credentials, ο attacker δεν χρειάζεται πια το bug.
Γιατί δουλεύειΟ client «θέλει» να δεχθεί br (το διαπραγματεύτηκε ο ίδιος), η αποσυμπίεση είναι αόρατη στον κώδικα, και το θύμα είναι από τα πιο privileged processes της εφαρμογής.
Τι το σπάειEgress allowlist που εμποδίζει fetch σε μη εγκεκριμένους hosts · patchαρισμένο runtime · Accept-Encoding χωρίς br σε internal fetchers · token-based metadata protections (τύπου IMDSv2) που δεν απαντούν σε τυφλά GETs.
Edge/CDN/cache poisoning ή hostile network: το MITM μονοπάτι
Ο attacker δεν χρειάζεται να είναι ο endpoint: αρκεί να κάθεται «στο δρόμο». Compromised CDN, poisoned cache, εταιρικό proxy με TLS interception, hostile Wi-Fi. Η εφαρμογή κάνει fetch σε απολύτως νόμιμο domain — αλλά η απόκριση που φτάνει είναι παραποιημένη. Το σενάριο αντιστρέφει την κλασική ήρεμη διαπίστωση «κάνω fetch μόνο σε trusted hosts»: το «trusted» αφορά το DNS name, όχι τα bytes.
Γιατί δουλεύειΤα TLS termination σημεία και τα caches έχουν τα δικά τους compromise vectors, και η εφαρμογή εμπιστεύεται τον δρόμο που θεωρεί δικό της.
Τι το σπάειVerification/pinning όπου πρακτικά υπαρκτό, patchαρισμένα runtimes, και επίγνωση ότι το «TLS-only» δεν είναι συνώνυμο του «τέλος ζωής του MITM».
CI/CD: ο runner που κάνει fetch ως κλέφτης μυστικών
Οι CI runners είναι ο παράδεισος του HTTP2Shell: κάνουν αμέτρητα fetches (dependencies, base images, artifacts, release binaries), τρέχουν με τα ισχυρότερα tokens του οργανισμού, και κανείς δεν τα παρακολουθεί στενά. Ένα RCE σε runner σημαίνει: κλοπή provider credentials, poisoning των artifacts που παράγει, και — στο επόμενο deploy — RCE όπου τρέχουν τα artifacts.
Γιατί δουλεύειΟι runners συχνά τρέχουν ξεπερασμένα images γιατί «θα αναδημιουργηθούν ούτως ή άλλως» — υπόθεση που σπάνια επαληθεύεται ποτέ.
Τι το σπάειPinned & checksum-verified artifact sources · egress proxies στο CI · fresh runner images από patched bases · short-lived, scoped tokens αντί για αιώνια.
Το κοινό μοτίβο άμυνας: και στα τρία σενάρια το bug χρειάζεται δύο πράγματα για να δουλέψει: (1) ευάλωτο decoder στη διεργασία και (2) δρόμο για attacker-influenced bytes. Κατέστρεψε το ένα και το kill chain σπάει. Κατέστρεψε και τα δύο — και το σενάριο γίνεται θεωρία.
Η οικογένεια X2Shell: pattern, όχι σύμπτωση
Το HTTP2Shell δεν εμφανίστηκε σε κενό. Η ίδια εβδομάδα είδε τη δημοσιοποίηση του PHP2Shell — δύο κρίσιμα RCE στην PHP που patchάρονταν βιαστικά στις 20–21 Οκτωβρίου, με ένα τρίτο σχετικό bug που παραμένει χωρίς emergency fix. Τα «X2Shell» δεν μοιράζονται κώδικα· μοιράζονται φιλοσοφία: τα δεδομένα που «απλώς γίνονται parse» από native code είναι η πιο υποτιμημένη επιθετική επιφάνεια του internet.
| Όνομα | CVE(s) | Primitive | Πού χτυπά | Κατάσταση patch |
|---|---|---|---|---|
| Node2Shell | CVE-2026-111497 | OOB write σε bundled Brotli decoder | Node.js & Bun runtimes (client-side decompression) | PATCHED 21/10 |
| HTTP2Shell | CVE-2026-111497 WIDER LENS | Το ίδιο primitive σε ευρύτερη ανάγνωση: κάθε HTTP stack με brotli αποσυμπίεση | Python (brotli/brotlicffi), agents, CLIs, CI, native consumers | ΜΕΡΙΚΩΣ — ανά component |
| PHP2Shell (×2) | CVE-2026-109618 · CVE-2026-109619 | Unauthenticated RCE στην PHP engine (mod_php vector) | PHP ≥ 5.4, web frontends | PATCHED 20–21/10 |
| PHP2Shell (3ο bug) | — | Σχετικό bug χωρίς πλήρεις λεπτομέρειες | PHP | ΧΩΡΙΣ EMERGENCY FIX |
| Node.js hardening wave | CVE-2026-105481 → 106074 | Σειρά flaws στη Node.js (NVD batch) | Node.js internals | WAVE PATCHES |
Η χρονική συγκυρία έχει σημασία: την ίδια περίοδο το CISA KEV πρόσθεσε το CVE-2026-114625 (Arista VeloCloud Orchestrator — SQL injection στο edge) και το CVE-2026-110447 (Collabora Online/CODE server). Τεχνικά άσχετα μεταξύ τους — αλλά το μήνυμα της εβδομάδας είναι ενιαίο: τα επίπεδα που «απλώς δέχονται και επεξεργάζονται» bytes και queries είναι το κύριο θέατρο. Αν δουλεύεις σε blue team, αυτό είναι το briefing σου για το τρίμηνο.
Κατάσταση exploitation: τι είναι πραγματικό, τι ανοιχτό
📦 Τι είναι τεκμηριωμένο
Δημόσια PoC repositories για το Node2Shell/HTTP2Shell δημιουργήθηκαν εντός 24ώρου από τη δημοσιοποίηση. Στη γειτονική οικογένεια, το Exploit-DB φιλοξενεί το #52704 (PHP2Shell PoC, 22/10). Η τεχνική λεπτομέρεια του primitive είναι πλέον δημόσιο κτήμα.
🕵️ Τι δεν έχει συμβεί (ακόμη)
Καμία τεκμηριωμένη μαζική in-the-wild εκμετάλλευση που να οδηγεί σε προσθήκη στο CISA KEV (ως τις 24/10). Το KEV απαιτεί παρατηρηθείσα εκμετάλλευση — όχι θεωρητική δυνατότητα. Αυτό είναι παράθυρο, όχι εγγύηση.
8.1 · Η εκτίμηση για το παράθυρο threat outlook
Το κόστος exploit του HTTP2Shell είναι υψηλότερο από ένα κλασικό web RCE: χρειάζεται delivery (attacker-influenced response) και αξιόπιστη heap exploitation ανά build. Αυτό μεταφράζεται έτσι: μαζική σάρωση τύπου «loud scanner» είναι ενοχλητική αλλά εφικτή — με DoS ως παρενέργεια· η στοχευμένη εκμετάλλευση είναι το πραγματικό ρίσκο, ειδικά από initial access brokers που πωλούν πρόσβαση. Ιστορικά, τέτοια primitives οπλοποιούνται μέσα σε 2–6 εβδομάδες. Το patch window σου δεν είναι «μέχρι να μπει στο KEV» — είναι τώρα.
Anti-FOMO σημείωμα: το να μην είσαι στο KEV δεν σημαίνει «δεν με αφορά κανέναν». Σημαίνει «δεν το έκαναν ακόμη με τρόπο που να φάνηκε». Τα πιο ακριβά περιστατικά της δεκαετίας ξεκίνησαν ακριβώς έτσι: από ένα bug «που κανείς δεν χρησιμοποιούσε» — μέχρι που κάποιος το χρησιμοποίησε πολύ καλά.
CVSS & τοπίο κινδύνου
Το επίσημο scoring είναι δουλειά των vendor/advisory teams. Παρακάτω δίνονται editorial εκτιμήσεις ανά σενάριο, για να μπουν στο risk register σου με σωστές προσδοκίες — όχι ως επίσημα scores.
| Σενάριο | Vector (CVSS v3.1, εκτίμηση) | Score | Σχόλιο |
|---|---|---|---|
| Worst case: ανοιχτό, unauthenticated delivery (π.χ. δημόσιος fetcher χωρίς φίλτρα) | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H | 9.8 | Attacker-controlled response path χωρίς τριβή. |
| Ρεαλιστικό: SSRF-gated delivery (γενικότερα: delivery-dependent AC:H) | AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H | 8.1 | Το πιο συνηθισμένο πραγματικό σενάριο: ισχυρό bug, φραγμένος δρόμος. |
| Internal-only: adjacent network, περιορισμένη fetch επιφάνεια | AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H | 7.5 | Το segmentation μειώνει την πιθανότητα, όχι τη σοβαρότητα. |
9.1 · Κλίμακα σοβαρότητας ανά σενάριο severity ladder
Πώς να το διαβάσεις: η σοβαρότητα του bug δεν αλλάζει — αλλάζει ο δρόμος. Το delivery gate (allowlists, έλεγχος redirects, φιλτράρισμα encodings) είναι το μόνο που κρατά ένα 9.8 θεωρητικό και ένα 8.1 πρακτικό. Αυτό ακριβώς δουλεύει το πλάνο μετριασμού.
Ανίχνευση: βρες αν είσαι εκτεθειμένος — σήμερα
10.1 · Asset inventory: τρία commands, τρία λεπτά triage
# 1. Node.js runtime: πρέπει να είναι ≥ 22.23.4 / 24.22.1 / 26.12.1 node -p "process.versions.node" # 2. Bun runtime: αναβάθμισε στην τελευταία έκδοση bun --version # 3. Python: υπάρχει brotli decoder στο environment; python3 -c "import brotli" 2>/dev/null && echo "brotli: ΕΓΚΑΤΕΣΤΗΜΕΝΟ — έλεγξε έκδοση/advisory" python3 -c "import brotlicffi" 2>/dev/null && echo "brotlicffi: ΕΓΚΑΤΕΣΤΗΜΕΝΟ — έλεγξε έκδοση/advisory" # 4. Ποια έκδοση; (για τα αρχεία του περιστατικού) pip show brotli brotlicffi 2>/dev/null | grep -E "Name|Version"
Σε κλίμακα υποδομής (μία φορά, όχι χειροκίνητα): φέρε τα δεδομένα από SBOM, container image scans και dependency resolution σε κεντρικό inventory. Ρώτα συγκεκριμένα: «ποιος τρέχει Node/Bun παλαιότερο από τις patched εκδόσεις;» και «ποια images περιέχουν brotli/brotlicffi;». Αν η απάντηση είναι «δεν ξέρουμε», αυτό είναι το πρώτο σου finding.
10.2 · Log hunting: ψάξε αν οι δοκιμές ήδη γίνονται hunting
# Ποια upstream hosts στέλνουν br-encoded αποκρίσεις μέσω egress proxy; grep -i "content-encoding: br" egress-access.log | sort | uniq -c | sort -rn | head # Crash patterns: segfault/abort μέσα σε brotli symbols grep -E "BrotliDecoder|brotli.*SEGV|bit_reader" application.log
- EDR / ανάλυση διεργασίας: το πιο δυνατό post-exploit signal δεν είναι το crash — είναι η επιτυχία: spawn shell ή curl από διεργασία node/bun ή από Python worker που δεν κάνει τέτοιο πράγμα κανονικά. Κάνε το άμεσο alert.
- Proxy/WAF: alert σε αποκρίσεις με Content-Encoding: br από hosts εκτός allowlist — ειδικά όταν το request προήλθε από server-side fetcher και όχι από browser.
- Canary: στήσε ένα internal HTTP canary service που απαντά πάντα με br-encoded honeypot payload. Κάθε fetch προς αυτό που δεν είναι δικό σου, είναι ανακάλυψη σκιάς.
Πλάνο μετριασμού: 0–24 ώρες, 7 μέρες, 30 μέρες
Σταμάτα την αιμορραγία
- Αναβάθμισε όλα τα Node.js σε
22.23.4/24.22.1/26.12.1(ή νεότερα) και το Bun στην τελευταία έκδοση. Rebuild images — όχι μόνο restart. - Σε Python environments όπου το brotli δεν είναι λειτουργική ανάγκη:
pip uninstall brotli brotlicffi— η απουσία του πακέτου απενεργοποιεί τη διαπραγμάτευση br στους περισσότερους clients. - Σε egress proxies και CI: σκάλισε το
Accept-Encoding(χωρίς br) για τους server-side fetchers. - Έλεγξε τα CI runners και τα unattended agents — είναι τα Tier 3 θύματα που κανείς δεν θυμάται.
Κλείσε τους δρόμους
- Χαρτογράφησε κάθε server-side fetch: ποιες συναρτήσεις, ποια inputs τις τροφοδοτούν, ποια hosts επιτρέπονται. Μετέτρεψε σε egress allowlist ό,τι μπορεί.
- Οπλίσε την ανίχνευση: rules για br-encoded αποκρίσεις από άγνωστους upstreams και alert σε shell-spawn από runtime διεργασίες.
- Σβήσε τα SSRF φώτα: έλεγξε redirects (πολλοί fetchers ακολουθούν ανοιχτά redirects — εκεί πέφτει το SSRF gate), DNS rebinding, IP literals.
- Container base images: rebuild από patched bases και κανόνες στο registry που μπλοκάρουν παλιές εκδόσεις.
Άλλαξε την αρχιτεκτονική
- Κεντροποίησε την εξερχόμενη HTTP σε ένα wrapper: ένα σημείο policy για encodings, allowlist, timeouts, retries — και ένα σημείο logging για όλο το fetch surface.
- Πρόσθεσε «native decoder drift» στη διαδικασία SBOM: τρύπες σε bundled C libraries δεν φαίνονται στο dependency tree — θέλουν image-level scanning.
- Procurement rule: τα νέα SDKs/agents να δηλώνουν πώς κάνουν decompression και τι έχουν bundled. Ρώτα — είναι δωρεάν due diligence.
- Tabletop exercise: «RCE σε runner/agent με cloud credentials» — 90 λεπτά που αποκαλύπτουν τα κενά σου πριν τα αποκαλύψει κάποιος άλλος.
# Ο proxy κάνει fetch για λογαριασμό applications; Μην αφήνεις το br να περάσει: location / { proxy_set_header Accept-Encoding "identity"; # μη διαπραγματεύεσαι br proxy_hide_header Content-Encoding; # και μην το κουβαλήσεις πίσω proxy_pass http://upstream; }
FAQ — οι ερωτήσεις που θα σου κάνουν αύριο
Επηρεάζεται ο browser μου;
Το όνομα «HTTP2Shell» σημαίνει ότι αφορά το HTTP/2 πρωτόκολλο;
Έχω μόνο Python services — με αφορά;
Μπορώ απλώς να απεγκαταστήσω το brotli; Θα σπάσει κάτι;
Δεν είναι στο CISA KEV — γιατί να βιαστώ;
Είναι ίδιο bug με το PHP2Shell;
Έχω WAF — με καλύπτει;
Ποιο είναι το ένα action αν μπορώ να κάνω μόνο ένα;
Μίνι γλωσσάρι
Εγγραφή δεδομένων εκτός των ορίων ενός buffer στη μνήμη. Πιο «πολύτιμη» από OOB read: μπορεί να οδηγήσει σε εκτέλεση κώδικα, όχι μόνο σε διαρροή.
Αλγόριθμος συμπίεσης της Google (Content-Encoding: br), reference υλοποίηση σε C, που ενσωματώνουν δεκάδες runtimes και libraries.
C/C++ κώδικας που ενσωματώνεται απευθείας στη διεργασία ενός runtime — ίδιο heap, ίδιες permissions, κανένα sandbox ανάμεσα.
Η αυτόματη αποσυμπίεση ενός HTTP response από τον client βάσει του header Content-Encoding — χωρίς ρητή ενέργεια του developer.
Τεχνική στοίχειωσης της μνήμης heap ώστε η διαβρώση να πέσει σε αντικείμενα/pointers που ωφελούν τον attacker.
Server-Side Request Forgery: ο attacker πείθει ένα server-side component να κάνει request σε URL της επιλογής του. Το «on-ramp» του KC-A.
Instance Metadata Service: το metadata endpoint των cloud VMs, από το οποίο κλέβονται credentials μετά από RCE. Δείτε KC-A, βήμα 6.
Κανόνας που επιτρέπει εξερχόμενες συνδέσεις μόνο σε εγκεκριμένους προορισμούς. Σπάει το KC-A στο βήμα 2.
Software Bill of Materials: η «συνταγή» των components μιας εφαρμογής. Εδώ, με επέκταση και στα bundled native libraries — όχι μόνο στο dependency tree.
Known Exploited Vulnerabilities Catalog: επίσημος κατάλογος ευπαθειών με παρατηρηθείσα ενεργή εκμετάλλευση. Compliance anchor για πολλούς οργανισμούς.
Σύστημα βαθμολόγησης σοβαρότητας (0–10). Τα vectors του άρθρου είναι editorial εκτιμήσεις σεναρίων, όχι επίσημα scores.
Η αλυσίδα βημάτων από αρχική πρόσβαση έως τελικό στόχο. Σπάει κανείς ένα κρίκο, σταματά η επίθεση — δείτε τις Φάσεις 1–3.
Πηγές & receipts
Ό,τι αναφέρεται εδώ βασίζεται σε δημόσια διαθέσιμο υλικό την ώρα της σύνταξης (24/10/2026, 15:42 UTC). Κατηγορίες: ADVISORY για επίσημες πηγές, RESEARCH για ερευνητικές αναλύσεις, PRESS για δημοσιογραφική κάλυψη.
Διαφάνεια: τα research & press receipts (S1–S8) ήταν δημόσιες αναλύσεις την ημέρα σύνταξης και μπορούν να αλλάξουν χωρίς προειδοποίηση — οι επίσημες homepages/advisories παραμένουν η τελική πηγή αλήθειας. Οι CVSS βαθμολογίες του άρθρου είναι editorial εκτιμήσεις σεναρίων, όχι επίσημα scores. Όλες οι πηγές δημόσια προσβάσιμες · το άρθρο δεν αποτελεί συμβουλευτική ασφαλείας.