Page 1 of 1

sceSdGetLastIndex() kxploit

Posted: Thu Oct 02, 2014 4:18 am
by GBOT
So I had this reverse of sceSdGetLastIndex() prepared for some time now for you guys to see how it works. Note that the kxploit was found by other people so proper credits to them (you already know who they are).

I'm not going to bore you with plain mips assembly, so I'll include the C reverse I made, it's easy to understand as it's complex but not a hard concept. Disclaimer: My reversing skill is far from good, so this might have some **** errors but the idea remains valid.

See the full disasm here
The most important parts you'd like to see are:

Code: Select all

0x858	s1 = a0;
.. argument 0 is backuped in $s1
.. *(argument0 + 36) should be already swapped to our custom value before the next instruction for it to work
0x9EC	a3 = *(u32 *)(s1 + 36);
.. a3 will take the new value of *(argument0 + 36) 
0xA4C	a1 = a3 + s1;
.. a1 will now be a pointer to (argument0 + *(argument0 + 36))
0xA58 	v1 = -128;
..
0xA68	*(u8 *)(a1 + 20) = v1;
.. 0x80 (-128) gets stored in (argument0 + *(argument0 + 36) + 20)
Here comes the not so ugly C version:

Code: Select all

//sceSdGetLastIndex kxploit partial reverse by GBOT

#define ERROR_KERNEL_ADDRESS -259
#define ERROR_SEMA -260
#define ERROR -1026

typedef struct _pspChnnlsvContext1 //taken from pspsdk
{
	int	mode;
	char buffer1[0x10];
	char buffer2[0x10];
	int	count;
} pspChnnlsvContext1;

static SceUID global_semaphore; // *(10964);
static char * global_data; // *(6724);
static char * global_data2; //6784

int sceSdGetLastIndex(pspChnnlsvContext1 * ctx, unsigned char * hash, unsigned char * cryptkey)
{
	char data[16];
	char t0, v0;
	int fp;
	
	//filters kernel addresses
	if(((hash + 16) | hash) & 0x80000000 < 0)
		return ERROR_KERNEL_ADDRESS;

	if(((cryptkey + 16) | cryptkey) & 0x80000000 < 0)
		return ERROR_KERNEL_ADDRESS;
	
	if(((ctx + 40) | ctx) & 0x80000000 < 0)
		return ERROR_KERNEL_ADDRESS;
	
	//loc_000008DC
	if(sceKernelWaitSema(global_semaphore, 1, 0)) //locks semaphore
		return ERROR_SEMA;

	if(ctx->count >= 17) //invalid value
	{
		//loc_00000C80
		if(sceKernelSignalSema(global_semaphore, 1))
			return ERROR_SEMA;
			
		return ERROR;				
	};
	
	//defines fp based on context mode
	if(ctx->mode == 6)
		fp = 17;
	else if(ctx->mode == 4)
		fp = 13;
	else if(ctx->mode == 2)
		fp = 5;
	else if(ctx->mode == 1)
		fp = 3;
	else if(ctx->mode ^ 3)
		fp = 16;
	else
		fp = 12;
	
	//loc_00000950:

	//fills area with 0's
	for(i = 0; i < 16; i++)
		global_data[i] = 0x0;
	
	if(sub_000013C8(global_data2, 16, fp))
	{
		//loc_00000C80
		if(sceKernelSignalSema(global_semaphore, 1))
			return ERROR_SEMA;
			
		return ERROR;	
	};

	//loc_0000098C:
	for(int i = 0; i < 16; i++)
		data[i] = global_data[i];

	t0 = (data[0] < 0)? 135: 0; 
		
	//loc_000009C0:
	for(int i = 0; i < 15; i++)
		data[i] = (data[i] << 1) | (data[i + 1] >> 7);
		
	v0 = t0 ^ (data[15] << 1);
	data[15] = v0; 
	
	if(ctx->count != 16)
	{
		t0 = (data[0] < 0)? 16: 0;
			
		//loc_00000A1C:
		
		for(int i = 0; i < 15; i++)
			data[i] = (data[i + 1] >> 7) | (data[i] << 1);
		
		data[15] = t0 | (data[15] << 1);
		
		//alter ctx->count via multiple thread 
		//mem. access to manipulate the following write line
		unsigned char * pointer = ctx + ctx->count; 
		
		pointer[20] = 0x80; //exploit me
		
		//...more, not important
		
	};
	
	//...more, not important
	
	return 0;
};
There's a lot of extra code that doesn't affect the exploit but I wanted to keep it there.
The fault here is they read the value again from the argument pointer, instead of backing up the first valid value for ctx.count as soon as it enters the function and passes the address checks.
Doing some maths we can get to any address we want to write 0x80 there.

Summary
  • Main thread sets ctx.count to a valid value (ctx.count = 16)
    Thread2 is created.
    Main thread calls sceSdGetLastIndex(&ctx, something, something2);
    sceSdGetLastIndex() checks the addresses in the arguments and validates ctx->count.
    Suddenly Thread2 interrupts the execution and swaps ctx.count to another value.
    sceSdGetLastIndex() resumes then and stores 0x80 (byte) in the address (&ctx + ctx.count).
Note that this is a try-fail case, that's why the function calls are inside loops in both main and qwikthread.

This function is named sceChnnlsv_C4C494F8() in pspsdk btw.
dismiss

Re: sceSdGetLastIndex() kxploit

Posted: Thu Oct 02, 2014 5:22 am
by yifanlu
Nice. Classic ToCToU race condition ;)

Re: sceSdGetLastIndex() kxploit

Posted: Thu Oct 02, 2014 5:44 am
by qwikrazor87
You can find the reverse for it on uOFW. :)
https://github.com/uofw/uofw/blob/maste ... /chnnlsv.c

Re: sceSdGetLastIndex() kxploit

Posted: Thu Oct 02, 2014 6:03 am
by Omega2058
Nice, this one is pretty interesting. :)

Re: sceSdGetLastIndex() kxploit

Posted: Thu Oct 02, 2014 6:29 am
by krystalgamer
This looks awesome.I don't own a vita but it's really amazing to see this progress.
Ty, GBOT :3

Re: sceSdGetLastIndex() kxploit

Posted: Thu Oct 02, 2014 6:51 am
by maciak
Is it still valid on 3.30? ;)

Re: sceSdGetLastIndex() kxploit

Posted: Thu Oct 02, 2014 8:00 am
by endrift
Very clever :) There's a reason the kernel usually disables interrupts every three instructions :lol: I'd never thought of doing a ToCtToU attack like this before, I should take a deeper look at some more exposed functions ;)

E] For those who aren't familiar, a ToCtToU attack is a "time of check to time of use" attack, and is a class of vulnerabilities wherein, between validating a value and using that value, the value has changed. This is a pretty plain example of that.

Re: sceSdGetLastIndex() kxploit

Posted: Thu Oct 02, 2014 8:13 am
by Blaze
maciak wrote:Is it still valid on 3.30? ;)
Even if it would, any type of kexploits won't get released.

Re: sceSdGetLastIndex() kxploit

Posted: Thu Oct 02, 2014 3:46 pm
by GBOT
maciak wrote:Is it still valid on 3.30? ;)
No, the thread is about the kxploit qwik released here which is the one used in every 3.18 TN/ARK/PS1 release. It's patched in 3.30.
qwikrazor87 wrote:You can find the reverse for it on uOFW. :)
https://github.com/uofw/uofw/blob/maste ... /chnnlsv.c
Yeah I know you can do it better, they are pretty similar though :lol:

Re: sceSdGetLastIndex() kxploit

Posted: Sat Oct 04, 2014 3:47 pm
by Joel16
Nice read, sounds pretty interesting.
I can't understand the disasm, but I can understand the ' not so ugly C version' pretty well. Lol this is coming from GigaBot? :o