Εν ολίγοις: Συγκαλυμμένος κώδικας PHP είναι κώδικας που έγινε σκόπιμα δυσανάγνωστος, συνήθως με κωδικοποίηση και συμπίεση που λύνονται μόνο κατά την εκτέλεση. Τυπικά μοτίβα αναγνώρισης είναι το eval μαζί με base64_decode, gzinflate ή str_rot13, πολύ μακριές γραμμές, συμβολοσειρές hex και ονόματα συναρτήσεων που συναρμολογούνται από επιμέρους τμήματα. Αναζητήστε με αθώες εντολές grep, μην εκτελέσετε ποτέ τον κώδικα και αντιπαραβάλετε τα ευρήματα με το αρχικό πακέτο.
Γιατί οι επιτιθέμενοι κρύβουν τον κώδικά τους
Όποιος αποθέτει ένα webshell ή μια κερκόπορτα σε έναν διακομιστή, θέλει να μείνει για πολύ απαρατήρητο. Ο ευανάγνωστος κώδικας θα έκανε εντύπωση σε κάθε διαχειριστή που ανοίγει το αρχείο και κάθε σαρωτής θα τον έβρισκε. Γι' αυτό οι επιτιθέμενοι πακετάρουν τον κώδικά τους: κωδικοποιείται, συμπιέζεται και συναρμολογείται και εκτελείται μόνο κατά την κλήση.
Για το PHP2Shell δεν υπάρχουν επιβεβαιωμένοι Indicators of Compromise. Ο συγκαλυμμένος κώδικας δεν είναι χαρακτηριστικό αυτής της ευπάθειας, αλλά γενικό σημάδι ότι κάποιος παρεισέφρησε κώδικα, ανεξάρτητα από τον δρόμο. Πώς βρίσκετε και αφαιρείτε συνολικά webshells, περιγράφει το Αναγνώριση και αφαίρεση webshell. Αυτό το άρθρο εστιάζει στο ερώτημα πώς αναγνωρίζετε συγκαλυμμένο κώδικα.
eval, base64 και παρόμοια: τα τυπικά δομικά στοιχεία
Η συγκάλυψη ακολουθεί σχεδόν πάντα την ίδια αρχή: ένα δυσανάγνωστο μπλοκ δεδομένων μετατρέπεται από μία ή περισσότερες συναρτήσεις ξανά σε κώδικα, που στη συνέχεια εκτελείται. Τα παρακάτω δομικά στοιχεία εμφανίζονται ξανά και ξανά.
| Δομικό στοιχείο | Παραδείγματα | Γιατί προκαλεί προσοχή |
|---|---|---|
| Εκτέλεση | eval, assert με συμβολοσειρά (έως PHP 7.x), create_function (έως PHP 7.x) |
εκτελεί κείμενο ως κώδικα |
| Αποκωδικοποίηση | base64_decode, str_rot13, hex2bin, strrev, convert_uudecode |
κάνει τα δεδομένα ξανά αναγνώσιμα |
| Αποσυμπίεση | gzinflate, gzuncompress, gzdecode |
αποσυμπιέζει ενσωματωμένο κώδικα |
| Συναρμολόγηση | αλυσίδες chr(), συνένωση μικρών τμημάτων, escapes \x |
κρύβει ονόματα συναρτήσεων |
| Δυναμική κλήση | μεταβλητές ως όνομα συνάρτησης, call_user_func, array_map με συμβολοσειρά |
η κλήση δεν εμφανίζεται πουθενά σε καθαρό κείμενο |
| Πρόσβαση στο σύστημα | system, exec, shell_exec, passthru, proc_open, popen |
εντολές στον διακομιστή |
Σύμφωνα με το εγχειρίδιο PHP, η assert() δεν αξιολογεί από την PHP 8.0 συμβολοσειρές ως κώδικα και η create_function() αφαιρέθηκε στην PHP 8.0. Σε διακομιστές με παλαιότερες εκδόσεις PHP και οι δύο παραμένουν σχετικοί όροι αναζήτησης.
Ένα μεμονωμένο δομικό στοιχείο σπάνια είναι ύποπτο, το base64_decode το χρησιμοποιούν πολλές εφαρμογές θεμιτά. Ύποπτος γίνεται ο συνδυασμός: αποκωδικοποίηση της οποίας το αποτέλεσμα ρέει απευθείας σε εκτέλεση.
Αναζήτηση χωρίς κίνδυνο: αθώες εντολές grep
Οι παρακάτω εντολές ερευνούν τα αρχεία μόνο ως κείμενο. Δεν εκτελούν κώδικα PHP. Προσαρμόστε τη διαδρομή.
# Εκτέλεση σε συνδυασμό με αποκωδικοποίηση ή αποσυμπίεση
grep -rlE "(eval|assert)\s*\(\s*(base64_decode|gzinflate|gzuncompress|str_rot13|hex2bin)" /var/www --include="*.php*"
# Πολύ μακριές γραμμές (συχνά πακεταρισμένος κώδικας)
grep -rlE ".{2000,}" /var/www --include="*.php*"
# Πολλά escapes hex ή αλυσίδες chr() σε μία γραμμή
grep -rlE "(\\\\x[0-9a-fA-F]{2}){20,}" /var/www --include="*.php*"
grep -rlE "(chr\s*\(\s*[0-9]+\s*\)\s*\.\s*){8,}" /var/www --include="*.php*"
# Κλήση συνάρτησης μέσω μεταβλητής αμέσως μετά από εκχώρηση συμβολοσειρών
grep -rnE "\\\$[a-zA-Z_]+\s*=\s*['\"][a-z_]{4,}['\"]\s*;\s*\\\$[a-zA-Z_]+\s*\(" /var/www --include="*.php*" | head -50
Ελέγξτε τα ευρήματα πρώτα με ls -l και τη χρονοσήμανση και ανοίξτε τα μετά μόνο ως κείμενο, για παράδειγμα με less. Μην καλείτε ποτέ ύποπτα αρχεία μέσω του προγράμματος περιήγησης, γιατί έτσι θα τα εκτελέσει ο διακομιστής.
Πού βρίσκεται τυπικά ο συγκαλυμμένος κώδικας
Ο συγκαλυμμένος κώδικας βρίσκεται σπάνια σε ένα δικό του, εμφανές αρχείο. Συχνότερα παρεμβάλλεται σε υπάρχοντα αρχεία, στην αρχή ή στο τέλος, συχνά μετά από πολλές κενές γραμμές, ώστε να μην πέφτει αμέσως στο μάτι κατά το άνοιγμα. Δημοφιλή είναι αρχεία που φορτώνονται σε κάθε κλήση: το κεντρικό index.php, αρχεία διαμόρφωσης και τα αρχεία συναρτήσεων ενός theme.
Ένας ιδιαίτερα διακριτικός δρόμος περνά από τη διαμόρφωση. Με τη ρύθμιση auto_prepend_file η PHP φορτώνει ένα πρόσθετο αρχείο πριν από κάθε script. Αν βρίσκεται σε ένα .user.ini ή .htaccess που δημιούργησε ένας επιτιθέμενος, ο κώδικάς του τρέχει σε κάθε κλήση σελίδας χωρίς να έχει αλλάξει ούτε ένα αρχείο της εφαρμογής. Το ενσωματωμένο αρχείο φέρει συχνά αθώα κατάληξη όπως .ico ή .png.
# Φορτώνεται πρόσθετος κώδικας πριν ή μετά από κάθε script;
grep -rnE "auto_(prepend|append)_file" /var/www --include=".user.ini" --include=".htaccess" --include="*.conf" 2>/dev/null
# Κώδικας PHP σε αρχεία με κατάληξη εικόνας ή εικονιδίου
grep -rlE "<\?php" /var/www --include="*.ico" --include="*.png" --include="*.jpg" --include="*.gif" 2>/dev/null
Διάκριση θεμιτής συγκάλυψης από κακόβουλο κώδικα
Δεν είναι κακόβουλος κάθε δυσανάγνωστος κώδικας. Τρεις περιπτώσεις εμφανίζονται συχνά:
- Εμπορικοί encoders: Ορισμένοι κατασκευαστές plugins και λογισμικού προστατεύουν τον κώδικά τους με εργαλεία όπως το ionCube ή το SourceGuardian. Τα αρχεία είναι δυσανάγνωστα, παραπέμπουν όμως τυπικά στον απαιτούμενο loader. Αντιπαραβάλετέ τα με το αρχικό πακέτο.
- Κώδικας minified ή παραγόμενος: Οι caches και οι μηχανές προτύπων παράγουν μακριές γραμμές χωρίς αλλαγές γραμμής, όμως χωρίς εκτέλεση αποκωδικοποιημένου κειμένου.
- Ενσωματωμένα δεδομένα: Εικόνες ή γραμματοσειρές ως συμβολοσειρά Base64 σε αρχείο PHP είναι ασυνήθιστες, αλλά αθώες, εφόσον το αποτέλεσμα δεν εκτελείται.
Ο πιο αξιόπιστος έλεγχος είναι η αντιπαραβολή με την πηγή: αν το αρχείο προέρχεται αναλλοίωτο από το πακέτο του κατασκευαστή, είναι θεμιτό. Αν δεν βρίσκεται σε κανένα αρχικό πακέτο ή αποκλίνει από αυτό, πρέπει να διερευνηθεί.
Μην προσπαθήσετε να αποκρυπτογραφήσετε συγκαλυμμένο κώδικα στον παραγωγικό διακομιστή, για παράδειγμα αντικαθιστώντας το eval με μια έξοδο και καλώντας το αρχείο. Ακόμη και ένα λάθος εκεί εκτελεί τον κακόβουλο κώδικα. Η ανάλυση ανήκει σε απομονωμένο περιβάλλον.
Όρια της αναζήτησης μοτίβων
Οι επιτιθέμενοι γνωρίζουν τις συνήθεις εντολές αναζήτησης. Χωρίζουν ονόματα συναρτήσεων, τα συναρμολογούν κατά την εκτέλεση ή μεταφέρουν τον πραγματικό κώδικα στη βάση δεδομένων, σε αρχεία εικόνων ή σε καταχωρίσεις διαμόρφωσης. Μια αναζήτηση μοτίβων βρίσκει γι' αυτό πολλά, όχι όμως τα πάντα. Γιατί και οι σαρωτές φτάνουν εδώ σε όρια, εξηγεί το Σαρωτές κακόβουλου λογισμικού για ιστότοπους: τι βρίσκουν και τι όχι.
Ένα εύρημα είναι εξάλλου σπάνια το μοναδικό. Οι επιτιθέμενοι χρησιμοποιούν συχνά σε πολλά αρχεία το ίδιο δομικό στοιχείο, για παράδειγμα μια χαρακτηριστική ονομασία μεταβλητής ή μια διακριτική ακολουθία χαρακτήρων στην αρχή του κωδικοποιημένου μπλοκ. Αναζητήστε γι' αυτό μετά από ένα επιβεβαιωμένο εύρημα στοχευμένα ακριβώς αυτή την ακολουθία σε ολόκληρο τον κατάλογο ιστού, και σε άλλους ιστότοπους στον ίδιο διακομιστή. Έτσι βρίσκετε αδελφά αρχεία που καμία από τις γενικές αναζητήσεις μοτίβων δεν εντόπισε.
Συμπληρώστε την αναζήτηση με την αντιπαραβολή με πρωτότυπα αρχεία και με μια ματιά στη βάση δεδομένων, βλ. Έλεγχος της βάσης δεδομένων για παρεισφρήσαντα κώδικα. Προληπτικά μπορούν να αποκλειστούν οι συναρτήσεις συστήματος που δεν χρειάζονται, όπως περιγράφεται στον οδηγό Σωστή χρήση του disable_functions.
Πώς βοηθά συγκεκριμένα η PHP SOS;
- Αναζήτηση: Ερευνούμε αρχεία και βάση δεδομένων με εκτεταμένα μοτίβα και αντιπαραβάλλουμε με αρχικά πακέτα.
- Ανάλυση: Εξετάζουμε ύποπτο κώδικα σε απομονωμένο περιβάλλον, χωρίς να θέσουμε σε κίνδυνο το σύστημά σας.
- Καθαρισμός: Με την αφαίρεση κακόβουλου λογισμικού αφαιρούμε όλα τα συστατικά και κλείνουμε τον δρόμο εισόδου.
- Θωράκιση: Περιορίζουμε τις συναρτήσεις PHP και τα δικαιώματα εγγραφής και παρακολουθούμε νέα ή αλλαγμένα αρχεία.
Συχνές ερωτήσεις
Είναι το eval με base64_decode πάντα κακόβουλος κώδικας;
Όχι πάντα, αλλά πολύ συχνά. Ο θεμιτός κώδικας σχεδόν δεν χρειάζεται αυτόν τον συνδυασμό. Σε αρχεία που δεν ανήκουν στην εφαρμογή σας ή σε γνωστό plugin είναι ισχυρό προειδοποιητικό σημάδι.
Επιτρέπεται να αποκωδικοποιήσω συγκαλυμμένο κώδικα για να τον διαβάσω;
Μόνο χωρίς να τον εκτελέσετε και όχι στον παραγωγικό διακομιστή. Ακόμη και μια ακούσια εκτέλεση μπορεί να προκαλέσει ζημιά. Αφήστε σε περίπτωση αμφιβολίας την ανάλυση σε ειδικούς που εργάζονται με απομονωμένα περιβάλλοντα.
Γιατί ο σαρωτής κακόβουλου λογισμικού δεν βρίσκει τον κώδικα;
Οι σαρωτές αναζητούν γνωστά μοτίβα. Οι επιτιθέμενοι αλλάζουν συνεχώς τον κώδικά τους, χωρίζουν τα ονόματα συναρτήσεων ή τα συναρμολογούν κατά την εκτέλεση. Μια κενή σάρωση δεν αποτελεί γι' αυτό απόδειξη καθαρού διακομιστή.
Τι ισχύει για εμπορικά κρυπτογραφημένα plugins;
Ορισμένοι κατασκευαστές προστατεύουν τον κώδικά τους με encoders όπως το ionCube ή το SourceGuardian. Τέτοια αρχεία είναι δυσανάγνωστα, αλλά θεμιτά αν προέρχονται από τον κατασκευαστή. Αντιπαραβάλετέ τα με το αρχικό πακέτο.
Βοηθά το disable_functions απέναντι σε συγκαλυμμένο κώδικα;
Εν μέρει. Συναρτήσεις όπως exec ή system μπορούν να αποκλειστούν, το eval όμως όχι, επειδή είναι γλωσσική δομή και όχι συνάρτηση. Ο αποκλεισμός δυσκολεύει το επόμενο βήμα των επιτιθέμενων, δεν εμποδίζει όμως κάθε κακόβουλο κώδικα.