Wenn 'a' nicht gleich 'a' ist. Nach einem Hack

Eine unangenehme Geschichte ist einem meiner Freunde passiert. Aber soweit es sich für Mikhail als unangenehm herausstellte, war es für mich genauso interessant.

Ich muss sagen, dass mein Freund ein ziemlicher UNIX- Benutzer ist: Er kann das System selbst installieren, MySQL , PHP installieren und die einfachsten Nginx- Einstellungen vornehmen.
Und er hat ein Dutzend und eine halbe Baustelle für Bauwerkzeuge.

Eine dieser Websites, die Kettensägen gewidmet ist, befindet sich eng in den TOP-Suchmaschinen. Diese Seite ist ein gemeinnütziger Rezensent, aber jemand ist auf seine Kehle gestoßen und hat ihn angegriffen. Entweder DDoS , dann Brute Force, dann Kommentare schreiben obszön und senden Missbräuche an das Hosting und an die ILV.
Plötzlich beruhigte sich alles und diese Flaute war nicht gut, und die Website begann allmählich, die obersten Zeilen der Ergebnisse zu verlassen.

Bild

Das war ein Sprichwort, dann das Admin-Bike selbst.

Die Zeit näherte sich dem Schlaf, als das Telefon klingelte: „Sanya, wirst du nicht auf meinen Server schauen? Es scheint mir, dass sie mich gehackt haben, ich kann es nicht beweisen, aber das Gefühl hat die dritte Woche nicht verlassen. Vielleicht muss ich mich nur wegen Paranoia behandeln lassen? “

Als nächstes folgte eine halbstündige Diskussion, die sich wie folgt zusammenfassen lässt:

  • der Nährboden war ziemlich fruchtbar;
  • Cracker könnten Superuser-Rechte erlangen;
  • Der Angriff (falls er stattfand) war speziell auf diese Site gerichtet.
  • Problembereiche sind behoben und Sie müssen nur verstehen, ob es eine Tatsache der Penetration gab;
  • Hacking konnte den Site-Code und die Datenbanken nicht berühren.

Zum letzten Absatz.

Bild

Nur das weiße IP-Frontend schaut in die Welt. Es gibt keinen Austausch zwischen Backends und Frontend außer http (s), Benutzer / Passwörter sind unterschiedlich, Schlüssel werden nicht ausgetauscht. Bei grauen Adressen sind alle Ports außer 80/443 geschlossen. Weiße IP-Backends sind nur zwei Benutzern bekannt, denen Michael voll und ganz vertraut.

Debian 9 ist am Frontend installiert und zum Zeitpunkt des Aufrufs ist das System durch eine externe Firewall von der Welt isoliert und gestoppt.

„Ok, gib mir Zugang“, entscheide ich mich, den Schlaf um eine Stunde zu verschieben. "Ich werde mit meinen eigenen Augen sehen."

Im Folgenden:

$ 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 

Auf der Suche nach einem möglichen Hack


Ich starte den Server zuerst im Rettungsmodus . Ich mounte Festplatten, blättere nach Möglichkeit durch Authentifizierungsprotokolle , Verlauf , Systemprotokolle usw., überprüfe die Erstellungsdaten der Dateien, obwohl ich verstehe, dass ein normaler Cracker hinter ihm „schwitzen“ würde und Mischa bereits bei der Suche nach sich selbst wissentlich „getrampelt“ hat.

Ich beginne im normalen Modus, vor allem weiß ich noch nicht, wonach ich suchen soll. Ich studiere Konfigurationen. Er interessiert sich hauptsächlich für Nginx, da im Frontend im Allgemeinen nichts anderes als er ist.
Die Konfigurationen sind klein, gut strukturiert in ein Dutzend Dateien, ich schaue sie nur nacheinander durch. Alles scheint sauber zu sein, aber Sie haben einige nicht verpasst, ich werde eine vollständige Auflistung machen:

 $ 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 

Ich habe nicht verstanden: "Wo ist die Auflistung?"

 $ 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 

Die zweite wird der Auflistungsfrage hinzugefügt: "Warum so eine alte Version von Nginx?"

Darüber hinaus geht das System davon aus, dass die Version neu installiert wurde:

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

Ich rufe an:
- Mischa, warum baust du Nginx wieder auf ?
- Ok, ich weiß nicht mal, wie es geht!
- Ok, gut, schlaf ...

Nginx wird eindeutig wieder zusammengesetzt und die Auflistung für "-T" ist aus einem bestimmten Grund ausgeblendet. Es gibt keinen Zweifel an Hacking und Sie können es einfach akzeptieren und (da Mischa den Server sowieso durch einen neuen ersetzt hat) das Problem als gelöst betrachten.

Und in der Tat, da jemand Root-Rechte hat , ist es sinnvoll, nur das System neu zu installieren und nach dem zu suchen, was dort schlecht verwendet wurde, aber diesmal hat die Neugier den Traum besiegt. Wie kann man herausfinden, was sie vor uns verstecken wollten?

Versuchen wir zu verfolgen:

 $ strace nginx -T 

Wir schauen durch die Spur ist eindeutig nicht genug Zeilen a la

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

Vergleichen Sie zum Spaß die Schlussfolgerungen

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

Ich denke, dass ein Teil des Codes /src/core/nginx.c

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

wurde auf die Form reduziert:

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

oder

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

Daher wird die Auflistung nach -T nicht angezeigt.

Aber wie sieht man unsere Konfiguration?


Wenn meine Idee richtig ist und das Problem nur in der Variablen ngx_dump_config liegt, werden wir versuchen, sie mit gdb zu installieren, da der Schlüssel --with-cc-opt -g vorhanden ist und wir hoffen, dass die Optimierung -O2 uns nicht schadet. Da ich nicht weiß, wie ngx_dump_config im Fall 'T': verarbeitet werden kann, rufen wir diesen Block nicht auf, sondern installieren ihn mit dem Fall 't':

Warum kann ich '-t' zusammen mit '-T' verwenden?
Die Verarbeitung des if (ngx_dump_config) -Blocks erfolgt innerhalb von 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; } 

Wenn der Code in diesem Teil geändert wird und nicht im Fall 'T':, funktioniert meine Methode natürlich nicht.

Testen Sie nginx.conf
Nachdem das Problem bereits empirisch gelöst worden war, wurde festgestellt, dass für den Betrieb der Malware eine minimale Nginx- Konfiguration des Formulars erforderlich ist:

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

Wir werden es der Kürze halber im Artikel verwenden.

Führen Sie den Debugger aus
 $ 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 


Die Schritte:

  • Setzen Sie einen Haltepunkt in der Funktion main ()
  • Führen Sie das Programm aus
  • Ändern Sie den Wert der Variablen, die die Ausgabe der Konfiguration ngx_dump_config = 1 definiert
  • Programm fortsetzen / beenden

Wie Sie sehen können, unterscheidet sich die reale Konfiguration von unserer. Wir wählen ein falsches Stück daraus aus:

 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; 

Betrachten wir in der Reihenfolge, was hier passiert.

Definiert durch Yandex / Google von User-Agent :

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

Wordpress-Serviceseiten sind ausgeschlossen :

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

Und für diejenigen, die unter beide oben genannten Bedingungen fallen

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

im Text der HTML- Seite ändert sich 'o' in 'o' und 'a' in 'a' :

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

Genau so ist die Subtilität, dass 'a'! = 'A' dasselbe ist wie 'o'! = 'O' :

Bild

So erhalten Suchmaschinen-Bots anstelle des normalen 100% kyrillischen Textes modifizierten Müll, der mit lateinischen 'a' und 'o' verdünnt ist. Ich nehme nicht an, darüber zu diskutieren, wie sich dies auf SEO auswirkt, aber es ist unwahrscheinlich, dass sich ein solcher wörtlicher Hash positiv auf Positionen im SERP auswirkt.

Was sagen Jungs mit Fantasie.

Referenzen


Debuggen mit GDB
gdb (1) - Linux-Manpage
strace (1) - Linux-Manpage
Nginx - Modul ngx_http_sub_module
Über Sägen, Kettensägen und Motorsägen

Source: https://habr.com/ru/post/de465355/


All Articles