День добрый, пятница ясная, бравый молодец иль девица красная!

Можешь мне верить, можешь мне не верить, но начался сей сказ с пары весточек на мою почту электронную и вот такой вот картины, красоты неписанной:



Это 500 бравых молодцев онлайн (по депеше от гугла) на движке заморском, wordpress именуемом, на сервере Intel Xeon E3 1245v2 (soyoustart, E3-SSD-3). К полотну была приложена рукописъ, помочь в оптимизации сего хозяйства.

Disclaimer: В данном материале показана быстрая диагностика глючного сервера и приведены конфиги, которые можно счесть полезными. Если вам есть что дополнить или критиковать, не стесняйтесь писать об этом в комментариях, ибо одна голова хуже две, две хуже чем три, а n-1 хуже чем n. Заразки, выдранные с сервера залиты на gist с соотв. комментариями как private, ясен пень, что не стоит их запускать на системе, для этого есть debug и ideone.com .

Я все еще лениво висел в скайпе и хотел снять порчу по телефону, как правило это получается, ибо копипаста не врет, пока пробелы после паролей не копируют.

netstat -ntu | awk '{print $5}'| cut -d: -f1 | sort | uniq -c | sort -nr | more

выдал:



После команды:

iptables -I INPUT -s 188.138.89.112 -j DROP

стало красиво:



Но весь php встал на дыбы, в том числе и залитый phpinfo().

Все еще подгоняемый желанием решить удаленно и нежеланием подключаться я попросил запустить

tcpdump -i eth0 dst host 188.138.89.112 -v -XX

и следом разбанить:

iptables -D INPUT -s 188.138.89.112 -j DROP

После чего мне отправили лог:

        Host: broin.top
        Accept: */*

        0x0000:  0007 b400 0102 3860 7713 ffbe 0800 4500  ......8`w.....E.
        0x0010:  006f 900d 4000 4006 0592 2e69 6086 bc8a  .o..@.@....i`...
        0x0020:  5970 d8f5 0050 3be0 6f97 6bd8 b0e4 8018  Yp...P;.o.k.....
        0x0030:  00e5 a54b 0000 0101 080a 03dd ffa0 4221  ...K..........B!
        0x0040:  da17 4745 5420 2f6c 6e6b 2f69 6e6a 2e70  ..GET./lnk/inj.p
        0x0050:  6870 2048 5454 502f 312e 310d 0a48 6f73  hp.HTTP/1.1..Hos
        0x0060:  743a 2062 726f 696e 2e74 6f70 0d0a 4163  t:.broin.top..Ac
        0x0070:  6365 7074 3a20 2a2f 2a0d 0a0d 0a         cept:.*/*....
20:33:17.765232 IP (tos 0x0, ttl 64, id 50803, offset 0, flags [DF], proto TCP (6), length 52)
    server.s.55540 > xray874.dedicatedpanel.com.http: Flags [.], cksum 0xa510 (incorrect -> 0x6231), ack 15929, win 477, options [nop,nop,TS val 64880546 ecr 1109514777], length 0
        0x0000:  0007 b400 0102 3860 7713 ffbe 0800 4500  ......8`w.....E.
        0x0010:  0034 c673 4000 4006 cf66 2e69 6086 bc8a  .4.s@.@..f.i`...
        0x0020:  5970 d8f4 0050 8d4b 3a3b a61a 0724 8010  Yp...P.K:;...$..
        0x0030:  01dd a510 0000 0101 080a 03dd ffa2 4221  ..............B!
        0x0040:  da19                                     ..
20:33:17.765486 IP (tos 0x0, ttl 64, id 50804, offset 0, flags [DF], proto TCP (6), length 52)
    server.s.55540 > xray874.dedicatedpanel.com.http: Flags [.], cksum 0xa510 (incorrect -> 0x5c72), ack 17377, win 500, options [nop,nop,TS val 64880546 ecr 1109514777], length 0
        0x0000:  0007 b400 0102 3860 7713 ffbe 0800 4500  ......8`w.....E.
        0x0010:  0034 c674 4000 4006 cf65 2e69 6086 bc8a  .4.t@.@..e.i`...
        0x0020:  5970 d8f4 0050 8d4b 3a3b a61a 0ccc 8010  Yp...P.K:;......
        0x0030:  01f4 a510 0000 0101 080a 03dd ffa2 4221  ..............B!

Видно, что дергается http://broin.top/lnk/inj.php, вот ссылка на gist и на декодированный.
Моя лень подсказала несчастному админу, мол найти все моменты влючения этого безобразия. Файлы были найдены, пути относительно «корня сайта»:


Следом я попросил скинуть мне index.php из WP и получил вот такое чудо

@include_once('./wp-includes/wp-mod.php');

В итоге под раздачу попала пачка файлов в /wp-includes/:

  • 9f120c79d956d543a1cd44902f0f50056b3338cc1136effb6c208bf46fcf74fd wp-010617.php
  • d0eb12533eed5d316504573976e611a71fd066f8fe5591422f6f49cdcaa8ff5e wp-0bf.php
  • cfb91fbc45eb7a74791a7845e7b18c2b8771b217ee27150a6f8422a1a073cc6d wp-accuracy.php
  • d67051bc4d20782f770dac771cfdf68e876156a0b012be2462f8a436dad07ceb wp-file.php
  • 5503f88744374570f9168216ee174ced87c80ef17d8f17accb2001f985862267 wp-fiscal.php
  • ee53973940016c403f457762e0c270d9729a101337bcdf3d6e4667b050549316 wp-mod.php
  • 5361bdbd75b368e79d17a47282f0ce6cd2759ccfdef8833ff38fe2a5dee95170 wp-obf.php
  • 89e96f69b8cb9d54959b300cc03b1312a0500c24ddffb6659e6acb688a1cfd8d wp-pas.php
  • 6c752d54255ead4b7db10e00f7b53ba9132b0c143c5bb3f667e2a81eb98801cc wp-wso.php

В общем больше нельзя было оставаться в стороне, и я забрал ssh доступ на скромный свеже установленный (февраль 2017) debian 7, php 5.4, демо ISP панель, приправленный proftpd… апачем до кучи (сайтами же рулил php-fpm)…

После поиска php файлов в разных директориях я нарвался на /wp-content/uploads/:

  • cfb91fbc45eb7a74791a7845e7b18c2b8771b217ee27150a6f8422a1a073cc6d wp-accuracy.php
  • 5503f88744374570f9168216ee174ced87c80ef17d8f17accb2001f985862267 wp-fiscal.php
  • 9b53f3e7243b78e6d0978817aab98c58e4598aa6a3c012ed10621b6e59c9283e wp-uso.php

Затем в папке /wp-content/plugins/, схожие файлы загружать не стал:

  • cfb91fbc45eb7a74791a7845e7b18c2b8771b217ee27150a6f8422a1a073cc6d wp-accuracy.php
  • 510785b58d10fc7687a63548adffc4cee82b68974fe1c349824d90cbfbfe39e7 wp-dojika.php
  • d67051bc4d20782f770dac771cfdf68e876156a0b012be2462f8a436dad07ceb wp-file.php
  • 5503f88744374570f9168216ee174ced87c80ef17d8f17accb2001f985862267 wp-fiscal.php
  • 86a4d9ef20d506eab64ece2ef1c6555bc2404c4ded0aac6a846d64837e1a509b wp-jojiro.php
  • 5361bdbd75b368e79d17a47282f0ce6cd2759ccfdef8833ff38fe2a5dee95170 wp-obf.php
  • 5361bdbd75b368e79d17a47282f0ce6cd2759ccfdef8833ff38fe2a5dee95170 wp-orekio.php
  • 89e96f69b8cb9d54959b300cc03b1312a0500c24ddffb6659e6acb688a1cfd8d wp-pas.php
  • 6c752d54255ead4b7db10e00f7b53ba9132b0c143c5bb3f667e2a81eb98801cc wp-wso.php

wp-dojika.php — фанат «Game of trones» как и wp-jojiro.php.

Как видно — они обфусцированы. В том или иной мере, что следующим шагом я выбрал все php с

grep -A 3 -B 3 --include=\*.php -rnw '/path/to/www/' -e ".*base64_decode.*"

и нашел еще один, WAIpro.php, из которого выдирается вот такой вот декодированный файлик, который дергает много разной гадости, как вот к примеру apiword.press/addadmin_1.txt (gist).

Файлы были перемещены для будущих ковыряний.

Траффик упал очень сильно. Через сутки зайдя и увидя повышенный траф я прописал:

tcpdump -i eth0 dst port 25 -v -XX

Я получил простыню, которая вертелась быстрее, чем армейский вентилятор из одноименного адегдота. После выключения php проблема не исчезла. Не верю, сказал я и ввел crontab -u admin -e.

… и, вишенкой на торте, стал вывод:

crontab -u admin -e

*/10 * * * * /var/tmp/eumqvTiN >/dev/null 2>&1

Затем sha256sum:

694fc1f7f17d5f3c447fcdb83fa6177b736b241430e70309a4b3111ef1d0e3b9  eumqvTiN

Который некто иной, как Linux/Mumblehard.U. Как он туда попадал… честно разбираться уже было лень. К счастью у несчастного админа была его старая виртуалка (OVH VPS SSD 3), проплаченная до 21 февраля, с которое сие было перенесено другим гореадмином 4 февраля по причине, того, что она (виртуалка) не справлялась с нагрузкой. И «несчастный админ» вышел на меня 6 февраля с простой просьбой по оптимизации сего хозяйства.

В итоге, сайт (как это возможно) был очищен от налепленных на него бяк, перенесен на переставленную старую виртуалку (которая бодро рассылала спам на всю ширину канала), и теперь показывает вот такие вот скромные числа, вот значение в пике (700 юзеров онлайн по googleAnal, кеш 3 сек):



Конфигурация достаточно простая: php-fpm 7.0.16 (7.1.1 не воспринимается некоторыми плагинами)

./configure --prefix=/opt/php-7.0 --with-pdo-pgsql --with-zlib-dir --with-freetype-dir --enable-mbstring --with-libxml-dir=/usr --enable-soap --enable-calendar --with-curl --with-mcrypt --with-zlib --with-gd --with-pgsql --disable-rpath --enable-inline-optimization --with-bz2 --with-zlib --enable-sockets --enable-sysvsem --enable-sysvshm --enable-pcntl --enable-mbregex --enable-exif --enable-bcmath --with-mhash --enable-zip --with-pcre-regex --with-pdo-mysql --with-mysqli --with-mysql-sock=/var/run/mysqld/mysqld.sock --with-jpeg-dir=/usr --with-png-dir=/usr --enable-gd-native-ttf --with-openssl --with-fpm-user=www-data --with-fpm-group=www-data --with-libdir=/lib/x86_64-linux-gnu --enable-ftp --with-imap --with-imap-ssl --with-kerberos --with-gettext --with-xmlrpc --with-xsl --enable-opcache --enable-fpm --enable-intl

+ memcached

fpm-fpm сидит в chroot'e,

[wwwsomeone]

listen = 127.0.0.1:9001
listen.allowed_clients = 127.0.0.1

user = wwwsomeone
group = wwwsomeone
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 10
chroot = /path/to/folder
chdir = /
catch_workers_output = yes

Nginx тоже на достаточно простой конфигурации (fpm подцеплен на порте, а не на сокете), а кеш имеет вид:

fastcgi_cache_key "$scheme:$server_name:$server_port:$request_uri";
fastcgi_cache mapcgi;
fastcgi_cache_lock on;
fastcgi_cache_lock_timeout 6s;
fastcgi_cache_valid 200 301 302 304 3s;
fastcgi_cache_valid 403 404 10s;
fastcgi_no_cache $cookie_wordpress_auth_cookie;
fastcgi_cache_methods GET HEAD;

Где $cookie_wordpress_auth_cookie проверяет наличие куки wordpress_auth_cookie и запрещает кеширование ответов. Сейчас кеш составляет, отчего нагрузка упала больше всего:

fastcgi_cache_lock_timeout 10s;
fastcgi_cache_valid 200 301 302 304 2m;
fastcgi_cache_valid 403 404 30s;

Кеш лежит на ремдиске (tmpfs) объемом 1 Гб. Если кому интересны тесты такой же ВПС (но пустой), то вот они (gist).

Динамика отдается на 12 r/sec, статика на 256 r/sec. В принципе кеш всему голова… 470 онлайн без кеша nginx:



Вот с кешем nginx:



MySQL MariaDB отличается от дефолта кодом ниже:

[mysqld]
user		= mysql
pid-file	= /var/run/mysqld/mysqld.pid
socket		= /var/run/mysqld/mysqld.sock
port		= 3306
basedir		= /usr
datadir		= /var/lib/mysql
tmpdir		= /tmp
#init-connect='SET NAMES utf8'
lc-messages-dir	= /usr/share/mysql
language = /usr/share/mysql/english
skip-external-locking
bind-address = 127.0.0.1

collation-server = utf8_unicode_ci
character-set-server = utf8
event_scheduler = on

innodb_file_per_table = 1
innodb_flush_method=O_DIRECT
innodb_lock_wait_timeout = 50
innodb_buffer_pool_size = 1G

join_buffer_size = 32M

max_connections = 1024
max_allowed_packet = 256M
max_join_size = 4096000
myisam_sort_buffer_size = 32M
myisam-recover = BACKUP

thread_cache_size = 16
key_buffer_size=32M
net_buffer_length = 16K
read_buffer_size=64M
read_rnd_buffer_size = 8M
sort_buffer_size = 32M
bulk_insert_buffer_size = 256M
expire_logs_days = 10
max_binlog_size = 100M
#
tmp_table_size = 1G
max_heap_table_size = 1G
#
table_cache = 8192
open-files-limit = 262144
transaction-isolation = READ-COMMITTED
query_cache_type = 2
query_cache_limit = 2M
query_cache_size = 256M
connect_timeout=30
wait_timeout=300

Логи доступа и ошибок nginx пишутся активно (1.5Гб в день), 3 дня полет нормальный и сервер чистый, судя по логах и активности в целом, в том числе и в логе fpm.

Так же в логе fpm уже на свежем ВПС:

[16-Feb-2017 23:06:42] WARNING: [pool wwwюзер] child 15075 said into stderr: "NOTICE: PHP message: PHP Fatal error:  Uncaught Error: Call to undefined function mysql_escape_string() in /www/wp-content/themes/одна-такая-старая-тема-которая-не-используется/functions.php:60"

  • 3622502af8083e53ced808af6ca7aca0bd3cb088d1df309964ab865f51ec90ca functions.php

После чего были поиски файлов, которые могут быть похожи на functions.php в той или иной мере (аналогичные команды), но ничего подозрительного найдено не было.

Следующим этапом будет запереть сайты за cloudflare, играя add_header Cache-Control "public, max-age=123456789"; для контента без авторизации и add_header Cache-Control "private, max-age=0"; когда кукисы присуствуют в ответе и включить их файрволл, обеспечив дополнительную защиту «несчастному админу» и его блогам.

Теплого вам солнца и настоящих выходных!
Поделиться с друзьями
-->

Комментарии (24)


  1. selivanov_pavel
    18.02.2017 00:13

    Всё хорошо, но


    ./configure --prefix=/opt/php-7.0 ...

    Я правильно понимаю, что этот php теперь не будут там обновлять до следующего заражения и вызова шамана админа?


    1. POS_troi
      18.02.2017 01:23
      +2

      Вас смущает то что ПхП был собран из сорцов а не установлен пакетом?
      Так собственно они всёравно не будут обновлять пакеты а если и будут то имеют не малые шансы всё уронить и всёравно вызывать шамано но только уже «по срочняку».
      Уж лучше пусть всё будет как положенно — админы ведут сервер, а остальный тусуются не дальше уютненькой админки WP-шечки.


    1. nikitasius
      18.02.2017 01:55

      Так, ничего не мешает ручками скачать, проверить хеш и собрать новый php.

      Структура простая:
      /opt/php-7.0/
      /opt/php-7.1/
      /opt/src/тут-все-сырцы


      Ну и
      systemctl ляля php-7.0-fpm.service
      systemctl ляля php-7.1-fpm.service

      Текущему админу не нужно ломать голову, так как все запускается после старта виртуалки автоматом и автоматом папочки на ремдиске делаются. Если же ему надо будет что-то перезапустить, что вместо ковыряния консоли он запускает mc, следом заходит в /usr/ssi14/ и там пачка скриптов для запуска, статусов и прочего + завтра добавлю ручной бекап (если ему надо сделать до или после того, как автоматический сработает).

      И, самое приятное, мне не зачем забивать на этот контакт, так как есть вероятность других совместных проектов, где я сам займусь серверной частью. Поэтому сервер будет периодически проверяться.

      Ну и… сейчас в unstable ветке 7.0.15 и 7.1.1, 7.0.16 & 7.1.2 доступны только с сайта, на момент этого коммента.

      Далее, я придерживаюсь политики, что лучше всего ставить софт из пакетов, из репы… пока это не оказывает проблем.

      А проблема: попробуйте поставить из коробки 7.0.16 + 7.1.2 через apt-get или aptitude (я к последней привык, и приложил усилие отвыкнуть от первой).
      Уверен, что решение будет длиннее, чем собрать их ручками, «скопировать файлики» в init.d и в system, подцепить их и…
      systemctl ляля php-7.0-fpm.service
      systemctl ляля php-7.1-fpm.service
      как я писал выше.


      1. selivanov_pavel
        18.02.2017 02:58
        +1

        попробуйте поставить из коробки 7.0.16 + 7.1.2 через apt-get или aptitude

        add-apt-repository ppa:ondrej/php && apt-get update && apt-get -y install php7.0-fpm php7.1-fpm
        
        php7.0-fpm  7.0.16-1+deb.sury.org~trusty+2
        php7.1-fpm  7.1.2-1+deb.sury.org~trusty+1

        Конфиги и сокеты разделены по отдельным папкам, все счастливы. Оно не совсем официальное, но по сути это собранные под убунту пакеты из debian unstable. Аккуратно, может притащить с собой libssl с отключенным SSLv3 для клиентской части, лечится прибиванием гвоздями apt-pin libssl1.0.0 из основных реп.


        Я не против ручной сборки, но ИМХО это для проектов, где есть ресурсы чтобы заниматься этим каждый раз при выходе CVE-шек, а для простых смертных лучше нормально обновляющийся репозиторий.


        1. MxMaks
          18.02.2017 14:43

          Репозиторий с именем ondrej я бы как минимум постеснялся ставить на продакшн.


          1. LuckyRaul
            18.02.2017 21:25
            +4

            Ondrej Sury — maintainer php в debian.

            + Dotdeb отказался делать дальше пакеты в пользу его


        1. nikitasius
          18.02.2017 14:45

          ppa:ondrej/php

          Я в свое время юзал какую-то другую репу из инета, давным давно все тащил из нее, так как в дебианских пакетах не было нужного софта, но
          это собранные под убунту пакеты

          может притащить с собой

          Вот поэтому я скачиваю исходники, сверяю sum и сам их ставлю. Просто разный подход.


  1. http3
    18.02.2017 11:31

    После первого прочтения какое-то ощущение потока сознания. :)
    Возможно из-за того, что рисунки недоступны. Загрузите, пожалуйста, их на хабрахранилище.

    Этот сайт не может обеспечить безопасное соединение

    На сайте blog-static.saraeff.net используется неподдерживаемый протокол.
    Клиент и сервер поддерживают разные версии протокола SSL или набора шифров. Скорее всего, сервер использует шифр RC4, который считается небезопасным.


    Кстати, если использовать cloudflare, то в netstat получим его IP :)
    Их можно как-то исключить? Вдруг кто лезет напрямую. :)


    1. intelligence
      18.02.2017 12:07

      исключить через grep -v
      либо вообще оставить доступ на 80\443 порты только из подсетей CF https://www.cloudflare.com/ips/


    1. nikitasius
      18.02.2017 14:47

      А кто или что вам сие выдает?
      Ибо SSL lab пишет приятные вещи, а CF не дураки и следят за шифрами.


      1. http3
        18.02.2017 19:16

        Опера 36.


        1. nikitasius
          18.02.2017 21:23

          43.0.2442.806 другого мнения, обновите систему.


          1. http3
            19.02.2017 00:59

            Виндаус XP, последняя под нее. :)
            Будет возможность, гляну на 7.
            Отпишусь. :)


            1. http3
              20.02.2017 10:24

              На 7 работают рисунки. Видимо, каких-то корневых сертификатов не хватает.

              Хотя иногда и на 7 не работают другие сайты.


              1. nikitasius
                20.02.2017 17:10

                Писалось в 2014 году, но смысл статьи (blog) не изменился. Её лучше открыть в чем-то современном :)


  1. lolipop
    18.02.2017 23:29
    +1

    сказ про то, как дырявые вордпрессы через свежую дырку захачили(cve от feb 2017), а админ залил снова еще раз старый дырявый вордпресс на другую виртуалку и ждет, пока его еще раз хакнут?


    1. nikitasius
      19.02.2017 00:45
      -1

      То, что системам, которые пишешь не ты верить нельзя — это америку не открыл. Соотвественно максимум что можно сделать, эти теми или иными способами блокировать и фильтровать те или иные запросы, так сказать соломки постелить.
      ВП свежий, обновленный.


  1. vanburg
    19.02.2017 00:28

    Боже, как я раньше с этим порождением дьявола работал вообще? Бррр. С обоими даже.


    1. nikitasius
      19.02.2017 00:46
      -1

      Ну, так если надо работать, то придется :)


  1. bykvaadm
    19.02.2017 20:49

    Может что-то я не так понимаю, но судя по htop, средняя нагрузка на проц за 15 минут при\без кеша практически не отличается. Или у Вас просто статистика не набрана?


    1. nikitasius
      20.02.2017 17:20

      Как же не отличается? 60% против 14%, как кеш растолстеет — будет еще меньше.
      Так же сегодня будет небольшой апдейт для кеша, чтобы страницы (page) кешировались по 2 минуты, а сам актуальный материал — сутки. Тем самым нагрузка уменьшится + у админа не будет проблем с публикацией нового материала.


      1. bykvaadm
        20.02.2017 20:07

        Вы меня не поняли. я говорю не о моментальной нагрузке на ядра, а о статистической за 15 минут. на картинке без кеша 0.77, с кешем 0.80. С другой стороны если предположить по разнице аптайма, что картинки были сделаны с разницей в 5 минут, то да, статистика не набежала.


  1. UA3MQJ
    20.02.2017 01:52
    -1

    Обожаю такие картинки с htop. Видно, что техника работает не зря ))

    Скрытый текст


    1. nikitasius
      20.02.2017 17:21

      5 дней всего?)