*36C3 preroll music*
Herald: The following talk is titled "It's Not Safe in the Streets, especially for
your 3DS" and it's about exploring a new attack surface on the 3DS. And the speaker
is nba::yoh. The show is yours.
*applause*
nba::yoh: Hi, everyone. I'm nba::yoh and today I'm going to talk about 3DS hacking
and especially about a protocol, an undocumented protocol. And we're gonna see
how I attacked the protocol and got remote code execution on the system. But before
before saying, oh, I did that, I'd like to do a quick recap of the state of 3DS
hacking in 2019, because there have been a lot of userland exploits, a lot of patched
kernel flaws and there's a lot of documentation online about the system. And
during the last few years, people have been working on it and broke the hardware
keyscambler. And they managed to dump the bootroms. And as a result, anyone who now
have the bootrom can derive all the secret keys of the system. And as a bonus, they
were able to find a permanent unpatchable bootrom exploit. So at that point, you
might be wondering what's left to do in the system. And actually, I wonder if it
would be possible to use those keys to attack features that were protected until
then by encryption. So that's why today I'm going to talk about StreetPass.
First, I'm going to do a quick introduction about the feature and then we'll see how I
expoited it and see what's possible to do once you get code execution. So what is
StreetPass? This is a local and wireless communication feature. The basic idea
behind StreetPass was that users would take their 3DS with them and go out and it
would automatically communicate with other people's systems. The point of the feature
was to share data between applications like custom levels for your games or
messages, avatars, et cetera. So this is quite an interesting feature for players,
but also for hackers. So this is the player point of view so you can send your
messages and other people will be able to receive them. But we'd like to - from an
hacker point of view - we'd like to replace one of these systems with a PC to
tamper with the protocol, for example. And eventually it would be nice if we could
send some corrupted messages and see if we can get code execution on foreign systems.
But for doing that, you need to know how the street the StreetPass feature works.
So it's quite simple. It acts like a mailbox. So you have CECD, which is a system
module, and it's the only one that manages all this StreetPass feature. So all your
applications have an inbox and an associated outbox in the CECD file system
and they can put and get messages from and to these boxes via IPC. Then messages in
these boxes are used to craft packets which are then sent using the StreetPass
protocol. And we can already see on this diagram which path we could try to attack.
The first one you might think about are our application boxes because it's very
likely that you'll be able to find some kind of game or application that have a float
parser. So we might take and get remote code execution in such an application. But
the most interesting one would be to try to attack CECDs parser. And this would
give us remote code execution in a system module, which would be nice. But for doing
that, we need to figure out how the StreetPass protocol works and nobody
really knows how it works. There is a bit of documentation online about the
pairings, so "oh both our systems are seeing a hand here. Would like would you
like to communicate with me?" But it has never been successfully reproduced, at
least publicly. And we know that it's using an unknown and encrypted protocol
that uses the secret AES key that we can now get. So let's reverse the protocol.
First, well, let's reverse the pairing and replicate it. So it's fairly easy to
understand: You have both peers, the client and the master and before
communicating they randomize their console ID and their MAC address. Then the client
sends a bunch of probe requests with our bundle specific tag, containing a list of
the applications that have StreetPass activated. And then eventually the master
received that public list and analyzes it. It's checked if an application from the
client that has StreetPass activated matches one of it's own applications with
StreetPass activated. If it's the case, it sends the probe response to the client,
which will do the same. And if both peers agree that they can exchange data, they
will start communicating after deriving a common key. So replicating the pairing is
not super odd if you know what tool to use. I tried to reproduce the pairing
using a monitor mode, but it's really hard because you have to deal with all the
action item frames et cetera. So I used nl80211 which lets you register some
specific callback for some specific frames and send custom frames and everything is
handled by your wifi adapter driver. So at that point the 3DS starts sending
encrypted data and we have to decrypt them. So let's review the encryption. They
do this in two passes, the first one they use an HMAC-SHA1 over both consoles CIDs
and both MAC addresses and the output of this pass is used as an input counter for
on AES-CTR using the AES key slot we can now get. And the output of this encryption
is used as a session key for the communication. So nl80211 lets you
register CCMP keys so it's quite easy to send and receive encrypted packets using
it. So now we can start reversing the protocol. Let's take a look at the
structure of packets before doing any reverse engineering. So I put some packets
I was able to receive and the first one is one of the smallest one you can actually
observe. So first you have a header and then you have some data and you can spot
some magic values here. Actually, it's easy to spot them because CECD is using
really recognizable magic values. It's always the same byte repeated twice. Once
you know the structure of the packets, you can view that there are actually
two protocols. The first one is SPTCP. It's an equivalent of TCP, but for local
communication. It's mainly designed to ensure reliability and data segmentation.
And then you have SPMTP which is built over SPTCP and handles all the exchange of
stricter data and StreetPass messages. So we have two protocols and we need to
review both of them. Let's start with SPTCP. Actually, there is not much to say
because you only basically have to reverse and understand the header. You have a
magic value and some constants. But the most important field here is the flag
field because if you can understand what are the flags, you can understand the
meaning of the packets you are sending and receiving. And so you can basically
understand the protocol. And fortunately, in this case, they're using the same flags
as TCP, so it was really easy to understand the protocol and how it worked.
And what about SPTCP security? Actually, it's OK. I did not find any bug. But the
attack surface is really small. We can basically only tamper with the header and
that's it. So SPMTP should be much more interesting. Let's take a look at the
structure of SPMTP packets, because there are actually two different packet types.
The first one is an info packet. It's basically used in the handshake and it's
only here to share information between both peers. And then you have message box
packets which are much more interesting because they contain actual StreetPass
data and you can even spot the CECD message via magic value, which means that
we actually reach actual game data and SPMTP is the last layer of encapsulation
of the whole protocol. So once you figure out how SPMTP works, you can reimplement
the protocol. So let's first review info packets. There's a bunch of things in
here. Many fixed size data like friend codes, MAC addresses, et cetera. It's not
much interesting when you're looking for vulnerabilities. There are variable size
data, which are much more interesting. They are sending application lists,
metadata lists, and it's much more interesting because when you process them,
you can fail your parser. And if there is some vulnerability here, we might exploit
them to get remote execution. So let's take a look at one of this parser. This is
a function that parses meta data lists. So let's focus on the for-loop. They are
actually copying a list of entries to the stack. The destination is on the stack and
they do not check the number of entries in the list. So this is clearly a buffer
overflow, but is it exploitable? Let's illustrate this with a diagram. So this is
a regular copy. So you have your packet buffer. And you have memcopy called on
each entry and everything's is all right here. But if you had more entries, you
have a bunch of entries, compete on the stack, that overwrite a bunch of things.
But there's a problem here, because the packet buffer is not large enough for us
to reach the return address on the stack. So we are probably copying uncontrol data.
So I was like, it's too bad, we can't do anything with this, but let's check the
buffer next to our packet buffer. Maybe there are some control data in there. And
actually, yes, it's this buffer just next to the packet buffer, dedicated to a
list that we sent just before. So actually we can rewrite the return address. So, how
do we exploit it now? On the 3DS, you have the NX bit, but there is no stack cookies
or no ASLR, so it's pretty straightforward. You can just embed a ROP-
chain, a small ROP-chain, and then send another one in some kind of packets and
stack-pivot to it. And then you get remote code execution in CECD. So this one was
quite easy. Let's move on to message box packets. Message box packets are packets
to send a list of StreetPass messages for some specific applications. They are
actually stored in some temporary files for avoiding delays and they are parsed
once the communication is over. So they are parsed. So let's take a look at the
parser. This is the function that actually loads a temp file into an associated
structure on the stack and whoops, they do not check the number of messages in the
box. So this is another buffer overflow. You are basically overflowing the message
pointers array and the message sizes array. So let's treat this with another
diagram. So you have on the right the temp file and on the left the stack and you see
that you have this structure on the stack we can see that there is the message
pointers with pointer pointing to your temp file buffer and you have the message
sizes. And if you add another message in the temporary file, you overflow both
arrays and start overwriting some data on the stack. We are a bit concerned because
we are writing partially or uncontrolled data on the stack. Obviously you cannot
control the message pointers and you can not still control message size because you
can not put some arbitrary values in there. You'd have to send gigabytes of
messages and you can not do that. So what can we do? What you can see here is that
you can actually set the last message size to an arbitrary value because they are
checking if the current message being parsed is actually inside of the temporary
file, a buffer. And if the current message pointer goes out of the buffer,
they break the loop
without returning an error. So what you can do is set the last message size to an
arbitrary value and then the pointer will go out of the buffer and you will write
one 32-bit value on the stack. But we need to know what to write. You cannot,
unfortunately, you cannot directly overwrite the return address because
remember that we are writing mainly uncontrolled data and reaching the return
address would require you to overwrite the whole stack frame with uncontrolled or
partially uncontrolled data. So the only thing I was able to rewrite without
crashing the system is this particular variable. It's a pointer to a critical
section and it's used for signed synchronization and mutual exclusion. And
you can see it's used after the temporary file has been parsed. So maybe we can do
something with it. This is the leave_critical_section call at the end of the loop
iteration. And you can see that they're using the pointer to decrement some kind
of count. So it's basically the number of threads that are using the critical
section and we can override the lock pointer. So by overwriting it, we can
decrement an arbitrary value in memory, but we need to find what to overwrite,
that would give us more control of the memory and control the execution flow. So
I've been looking for something like this and I found something interesting in the
function that deinitialized the structure associated to temporary files. This is the
function for the structure deinitialization. And you can spot this
variable. They actually implemented some kind of allocation mode. And if it's
equal to pointer mode, it will not try to free the pointer. The pointers in the
message pointers array. But if it's not pointer mode, they will free all of them.
And while this value should be pointer mode in any case. But we can decrement it
using the the vulnerability we've seen before. So we can get some pointer freed.
And since we control everything at the location pointed by message pointers, we can
try to make it free some crafted and fake chunks. But there is another problem
because they're actually resetting the allocation mode each time a temporary box
is passed. So we have to find a solution to this. And what you can do is try to
make that function return early before the allocation mode is restored. But this
implies making it return an invalid return code. But actually, it's not a problem
because they're not checking the return code. So what can we do so far with
this? So you can send a first temporary box. This will overwrite the lock variable
in the stack and decrement the allocation mode. Then you can send your
invalid second temorary box and the parser will return early and the
message pointers array will not be updated. And in the end, all the pointers
in that particular array will be freed. But since the message pointers array is
not updated, the pointer in that particular array are still pointing to the
first temporary file buffer which has been freed. That's not a
problem. If you send a xecond temporary box with the same size, the buffer will be
re-allocated for that second temporary file and we eventually free
up pointers to control the buffer. So what's next? We can craft some fake heap
chunks. We can have the application free them. What do we do? The free, yes, it is
actually really insecure. You can exploit the classic unsafe linker vulnerability,
so you get one arbitrary write for each chunk you can free. And you still need to
know what to overwrite. But you can just rewrite the heap free list head pointer. So the
next malloc call will return a pointer to wherever you want, and you can especially
put a pointer to the stack in there. So the next malloc call will return a
pointer to the stack and it will be used to store your third temporary file. So
it's a bit hard to understand. So let's again illustrate this with a diagram. So
first you have your first temporary file loaded in memory. So on the right it's
parsed and the associated structure is written in stack and it overwrites the
lock pointer to make it point to the alloc mode in memory. In the end,
leave_critical_section is called. So you have your temporary buffer freed and your location
mode decremented. Then your second temporary file is loaded in memory. The
buffer used for the first file is relocated and the pointers in the
structure still point to our controlled data and especially our fake
chunks. So your second temporary file is loaded and parsed. Then all the chunks are
freed and the field it is at is moved to point on the stack. And finally, you can
see that your last temporary file is read in on the stack so we can overwrite
the return address and put a ROP-chain in there. So this gives us a second remote
code execution vulnerability in CECD. And this one this one was quite trickier. So
what's next? Another one. Yeah. Again, there is another vulnerability in the
message parser. It's actually an SDK function. So any application that uses
SteetPass is vulnerable, not only CECD but all application and games that use
StreetPass are vulnerable for this one.
But I'm not going to talk about it and explain everything.
It's up to you to exploit it. So this gives us a third Remote Code Execution in
CECD and you can get Code Execution in any application using StreetPass. And this
also give us a persistent backdoor in CECD because of CECD usually parses all the
messages in the in and out boxes at startup. So you can trigger the vulnerability
once the system boots. So we've got a Remote Code Execution in CECD, what can we
do? No, actually, CECD does not have much privileges. It's only a userspace
application. And it's pretty well sandboxed. You can not access the
internet, for example or not the SD card. So if you want more privileges and we
want more privileges, you need to take care of something else. And your
best choice would be trying to take over ARM11 Kernel, which is the kernel for the
userland processor. This this would give you total control over this processor.
And if you want really full system control, you'd like to also take over the
ARM9 security processor. So this is the processor that do all the encryption and
signature stuff. And we will see this later. So let's first try to take over the
ARM11 kernel. But first, I need to talk about IPC and especially
what are called static buffers. So when you are doing IPC, you need to sometimes
send data from a sender process to a receiver process and on the 3DS, you can
do this in multiple ways. The first one is if you want to send large regular
buffers, you can map parts of the sender's memory into the receivers, but you can
also use what are called static buffers. If you want to send some small buffers,
the receiver can register static buffers and the ARM11 kernel will do the copy for
you to that particular buffer. And sometimes you need some buffers
to be sent to the ARM9 processor. So the ARM11 kernel need to
write some pairs of physical addresses and size to the static
buffers because the ARM9 does not have an MMU so it's only using physical addresses
and the copy of data is eventually done by the Process9, which is the only process
running on the ARM9 side. So let's talk about a vulnerability now. So it's called
LazyPixie and it has been found by TuxSH. So it's not me. How does the kernel handle the
PXI buffers case because it seems a bit complicated. So first
they check the alignment of the destination state buffer, they check the
size of the destination static buffer. They check the permissions for the source
buffers. Then they do cache operations, they copy metadata. So the physical address and
the size of the static buffer to the destination and then the copy is
done by the ARM9 side. But I think there is something missing here because
they do not check the permissions for the destination buffer. So what you can do is
use an arbitrary address as a destination. And so you can just overwrite the MMU
table and make your kernnel read, write and execute, which is obviously enough to take
it over. So at that point, the ARM11 Kernel has fallen and we have
the full control of that processor. But we would like a bit more privileges because
why not? We want the full system control. So let's take the road to full system
control and see why taking over CECD was one of the best ideas ever. So I am going
to talk about SAFEHAX. Maybe some of you know what SAFEHAX is because it's a really
cool vulnerability. It's actually race a condition in the firmware header parsing
you can take over the ARM9 side if you control the ARM11 kernel. It has
been fixed in the system version 9.5 for the regular native firmware
and fixed in the safe mode firmware, which is basically the recovery firmware if
something went wrong for your console. So people have been exploiting it both on the
native firmware and the safe mode firmware and it has been
mitigated in version 11.3 and 11.4. So it does not work anymore, but it has only
been mitigated and not patched. So let's take a look at that mitigation because
how do they prevent us to exploit that vulnerability? So this so-called
mitigation is a boolean flag that has been added on the ARM9 side and when it's set to
1 the system just panics. When you try to launch the safe mode firmware. So this
flag is actually set to 1 whenever you try to launch an application, so this was the
usual way to exploit it, you were launching the homebrew menu through an
application and then exploiting the ARM11 kernel and then running SAFEHAX. So they
set the flag to 1 whenever you try to launch a specific application, except some
of them because your reconnection ARMs needs some applications to run. So this is there is
an exception for the home menu and the system modules. And guess what? We are
exploiting CCD, which is a system module and we are getting Remote Code Execution
in CCD. So the the flag is never set to 1 when we are getting code executing on the
CCD. So with that kind of exploit. You can easily replicate the initial SAFEHAX
exploit. So then you get a full control, remote code execution without any user
interaction. And it's StreetPass and it's doing all of this thing in the background
and on any firmwre version at the time this was developed because Nintendo
patched it with firmware version 11.12. So I guess it's time for a little demo. I'm
not going to do it live because I don't want to some exploits in the air. So I
have a little video. So I'm running my exploit on my laptop and you can see the
LED is turned on to see that the exploit is running in CCD. And then you once
you're you can exploit and you can launch the installer for the Boot ROM exploit,
for example. *applause*
Thanks. So now, some some takeaways. Well, you'd better check your return value,
really, because there's a second vulnerability would have been really,
really out to exploit without that mistake. And really, you should not hide
behind cryptography because one day your encryption will be broken and this might
come sooner than you think. And for this specific case, there was a bunch of dumb
mistakes and basically all vulnerabilities were only buffer overflows. Then,
assessing hard-to-reach features is really arduous. I spent a lot of time doing this,
especially figuring out how to replicate all the features, parts and all the
different protocols involved. But eventually, you can get some really
interesting results like this. Then, I'd say please fix your flows, and do not
implement some poor mitigation, like for SAFEHAX. And there's things still to do on
the 3DS. I think I was able to show this today. There is, this is an amazing system
you can start to work on and do some practical things. And there's still things
to document on the open source wiki, so feel free to contribute. So, in the end, I
would like to thank @TuxSH for the LazyPixie and helping me getting this full
chain exploit done, and @hedgeberg for recurring support with a lot of things. So
now, if you have some questions, feel free to ask.
Herald: Thank you very much.
*applause*
Herald: We are very, very much on time. So ask any questions, but please do ask them
at a microphone. Go ahead. No, I thought there was going to ask a question. No
questions? Oh, the Internet has one. Great.
Signal angel: So, yes, we have two questions. The first one is what tools and
environments do you use for your research? For example, someone mentioned how do you
get all the source code? nba::yoh: Oh, everything on the 3DS is
closed source. So you have to reverse engineer everything. I used IDA and Ghidra
to reverse the binaries of CCD and, yeah, that, that's it.
Signal angel: OK. Thank you. We have a second question: Is there any procedure
for the Switch that is compatible with all what you've done?
nba::yoh: Sorry, could you repeat that question?
Signal angel: Well, all the things you have done, all the code. Is there anything
similar for the Switch? nba::yoh: I don't think there is something
similar on the Switch, at least something that looks like the StreetPass feature.
But I don't really know how the Switch works, I've only done things on the 3DS.
Herald: OK, first question for the room. Microphone: Thanks for the talk, great.
Did you really need all the three exploits? And which one did you use in the
end for the full chain? Thanks. nba::yoh: Could you repeat the question?
Microphone: Did you need all the three exploits that you had or could you just
use the easiest one? And which one did you use in the end?
nba::yoh: Well, no, you do not need all three exploits, at least in CCD. You only
need one basically to get remote code execution. But I found it fun to just show
all the exploits for CCD. Herald: Next question.
Microphone: Are the StreetPass messages passed to the applications even when those
applications are not running? So, for example, when you have like PokÃ©mon or
something installed... nba::yoh: Could you speak louder, please?
Microphone: Okay. Are the applications parsing the messages even if they are not
running? Like, is there some sort of a handler being run by the OS even if you
don't have an application running, just installed? So that if you have a
vulnerable application with the old SDK built in there, will it automatically
parse the corrupted message? nba::yoh: Could you reformulate your
question? I don't understand. Microphone: Okay. In your tree
exploitation method, you mentioned the third method that mentions the SDK being
broken. nba::yoh: Yeah.
Microphone: And if you have an application built with that old SDK, does it
automatically parse the message even if it's not running, so that even if you have
a patched OS, but not patched applications, it will still get exploited?
nba::yoh: Yeah, all the applications using the SDK should be updated to fix the
vulnerability. So the exploit is triggered when the application parsed the messages.
So you have to run the application to exploit it. CCD has been patched, so there
is no more remote code execution in CCD, nor a permanent backdoor in CCD, that
automatically runs, when the system is started. But you can probably still
exploit games and applications that use the old SDK.
Microphone: Okay, thanks. Herald: There's a question over there.
Microphone: Yes. Can you go back to the slide where you showed how the encryption
for the packets worked? nba::yoh: The encryption?
Microphone: Yes, the encryption. Yeah. Yeah. That one. So my question is, if all
you're, if the only thing that you're changing is the counter, and the data is
constant and the key is constant, and it's CTR, then you're basically just XOring a
known block with your HMAC output. So why do you even need the key here?
nba::yoh: Well, the counter changed every time you start a new StreetPass
communication, because the CID's are randomized and the MAC address is, the MAC
address is also randomized before starting a new communication.
Microphone: Right. But I guess what I'm asking is, why do you need key slot 2E? In
my mind, having the CCD HMAC key would be enough, because you can just XOR the, you
know, output of that with the final output, and that removes, you know, the
CTR part, and now you have the raw output of the null block encrypted with key slot
2E, which is always going to be constant, and then you can just XOR whatever output
to get the final result, right? nba::yoh: Yeah, well I'm not super
familiar with all the cryptography, but maybe we could talk about it. I was just
putting this for, for people to reproduce it if they want.
Herald: Okay. Are there any more questions? Thank you so much.
nba::yoh: Thanks.
*applause*
*postroll music*
subtitles created by c3subtitles.de in the year 2020. Join, and help us!