-
-
Save SciresM/5b8e8f8d4874e2836df905d424e45191 to your computer and use it in GitHub Desktop.
Conversation snippets from initial attempts to gain kernel code execution on the Nintendo Switch.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SciresM: so um | |
SciresM: there are kernel objects in dram. | |
SciresM: like I am looking at what is very clearly a KHandleTable. | |
hthh: are we talking 3.0 or 1.0? | |
SciresM: 1.0 | |
SciresM: 3.0 has nada. | |
hthh: nice :) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
hthh: it's really tricky trying to do these attacks before we have code, | |
but it's nice that switchbrew documented all this already | |
hthh: (hmm, just trying to think about options that might be interesting | |
and/or possible to explore blindly - you could make it a generic KAutoObject UAF, | |
by copying the type and pointer to a new handle then closing it repeatedly) | |
hthh: (we'd really like an arbitrary read, so maybe if you can figure out the | |
different ObjectType you could find an SVC that reads from one of them without | |
using the vtable, e.g. svcGetProcessId might just cast to KProcess and read from | |
offset 0x250 - but idk how ObjectType works so just a total guess) | |
hthh: (svcGetThreadId might be a safer bet because it supports fewer handle | |
types and it might be easy to create tons of threads to figure out what the type should be) | |
SciresM: I located the handle table for a specific process. | |
SciresM: :) | |
SciresM: :) :) :) | |
hthh: Haha nice! | |
SciresM: Yeah, psc heh. | |
SciresM: hahahaha | |
SciresM: I caused a kernel fatal error. | |
SciresM: Error 2001-0114. | |
SciresM: 1. Replace a KSession's ptr with null | |
SciresM: 2. Send a message to that session. | |
hthh: Ahaha that’s awesome! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SciresM: heh | |
SciresM: just successfully got the kernel to treat an arbitrary DRAM ptr like a handle entry. | |
SciresM: that's hilarious. | |
hthh: heh - I don't quite follow - you're pointing it at the physical memory? | |
SciresM: basically | |
SciresM: handle tables consist of used entries, free entries. | |
SciresM: used entries are KHandleEntry or w/e | |
SciresM: free entries are just a pointer to the next free entry (0x10 later) | |
hthh: makes sense | |
SciresM: I modified the first free entry to say the next free one was later. | |
SciresM: (in arbitrary dram, outside table) | |
SciresM: then I wrote a fake next pointer in arbitrary dram | |
SciresM: (pointing back into the table) | |
SciresM: duplicated session twice -> sure enough... | |
hthh: hahaha nice! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
hthh: oh, uh, this is a bit messy, but if we need we should be able | |
to get the vtable, I think: find a KProcess, set NextFreeEntry to point | |
to that, allocate a new handle (corrupting the KProcess) and then read the NextFreeEntry | |
(with a bit of luck you could repeat that and read whole objects, the big downside | |
being it corrupts everything it reads, so it can't read read-only memory) | |
SciresM: ...lol. | |
SciresM: Yeah actually that might work | |
SciresM: Oh god that's so stupid | |
hthh: haha yeah, it'll make things super unstable, | |
which'd be painful, but hopefully it's enough of a leak | |
to make your general fake-object technique just about guaranteed to be workable |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
hthh: I have backup ideas - you might like to weigh in on the 3DS implementations, | |
but one was using svcGetProcessId on a copied thread (corrupting the KProcess* | |
within the thread structure). Or trying to see if svcGetInfo can somehow read via a | |
layer of indirection. | |
hthh: also I mentioned before: what does the HandleId look like? is there any chance | |
we could have arbitrary values from 0x00-0xFF in the low byte and then write along | |
incrementing one byte at a time? with an arbitrary write we could try to write into | |
the kernel .text via the dram mapping, use a vtable call from a fake object (probably | |
on svcCloseHandle refcount == 0) to jump there and memcpy the rest of the code out | |
SciresM: handle ID is just an incrementing u16 | |
SciresM: can't have too many because handle table limits :p | |
hthh: how limited is the table? and if you open-close-open do you get the same value as before? | |
SciresM: open close open -> yes | |
SciresM: nv's table has space for 0x176 more entries. | |
SciresM: I can try the u8 thing | |
SciresM: if that works that would be so stupid | |
hthh: (wow, that's actually plausible - super slow, messy ugly and horrible, but I don't see what | |
could stop it? LibAppletWeb has Handle Table Size: 512 too, might it be worth trying to find that | |
table just to avoid the nvcall speed difference? I guess you could implement it with nv ACE, | |
so either should work.) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SciresM: Easiest way is probably incref or decref? | |
hthh: yeah, my guess is that incref/decref would be non-virtual, but the destructor would be | |
virtual, so decref to zero and then it'll jump to offset 0 or 8 in the vtable? | |
SciresM: On 3ds, decref is virtual | |
SciresM: and calls destructor on ref == 0 | |
SciresM: I guess if we just point the whole vtable at the shellcode.... | |
SciresM: worth a shot? | |
hthh: yeah - that's what I'd try | |
SciresM: fuck this is gonna be dumb | |
` (...) ` | |
SciresM: okay | |
SciresM: moment of truth coming in ~60 seconds | |
SciresM: (I added logging) | |
hthh: exciting! (is that because kernWriteU8 + nvcall is slow?) | |
SciresM: SUPER slow. | |
SciresM: │switch> eval utils.log(utils.paddr(sc.nv.gpuRead(0x83EEB800))) | |
SciresM: 0xdeadcafecafebabe | |
SciresM: we have arb write. | |
hthh: :D | |
SciresM: Now just need shellcode to copy to physmem 0xC0000000 :) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SciresM: I can do ~14 increments per second....so the worst case to write a byte is 20 seconds | |
SciresM: So guaranteed < 20 minutes to write it all out. | |
SciresM: you gonna stay up to see if it works? | |
hthh: yeah - we could optimise it pretty easily using the nvdrv ACE - I'm happy to wait if you are :) | |
SciresM: I'm happy to wait :) | |
SciresM: It's already written 4 dwords! | |
hthh: haha :D (whether or not this is the time that works, we're insanely close) | |
SciresM: yeah, no kidding. | |
(...) | |
SciresM: about halfway there btw | |
SciresM: it just wrote out 0xea, 0xff, 0xff, 0xf2, // movk x10, #65535, lsl #48 | |
SciresM: 4 instructions left.... | |
hthh: god, we could really optimise it... how did you implement the nvdrv fast gpu read code? | |
SciresM: 7 hand written instructions :p | |
hthh: haha yeah - did you hook it into an IPC service? I mean we really just want | |
closeHandle(createSharedMemory(0x1000)) N times - pretty comparable complexity | |
SciresM: eval utils.log(utils.paddr(sc.nv.gpuRead(0xC0000000))) | |
SciresM: 0xd2a00149d2800008 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment