Advertising (This ad goes away for registered users. You can Login or Register)

sceSdGetLastIndex() kxploit

Open discussions on programming specifically for the PS Vita.
Forum rules
Forum rule Nº 15 is strictly enforced in this subforum.
Post Reply
User avatar
GBOT
Developer
Posts: 321
Joined: Wed Apr 25, 2012 8:00 pm
Contact:

sceSdGetLastIndex() kxploit

Post by GBOT » Thu Oct 02, 2014 4:18 am

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
Advertising
Github
Twitter

Can't give enough crepes

yifanlu
Guru
Posts: 760
Joined: Sun Mar 11, 2012 6:42 am
Contact:

Re: sceSdGetLastIndex() kxploit

Post by yifanlu » Thu Oct 02, 2014 5:22 am

Nice. Classic ToCToU race condition ;)
Advertising

User avatar
qwikrazor87
Guru
Posts: 2869
Joined: Sat Apr 21, 2012 1:23 pm
Location: The North Pole

Re: sceSdGetLastIndex() kxploit

Post by qwikrazor87 » Thu Oct 02, 2014 5:44 am

You can find the reverse for it on uOFW. :)
https://github.com/uofw/uofw/blob/maste ... /chnnlsv.c
PSP 2001 - TA-085 - 6.61 PRO-C2
PS Vita 3G - PCH-1101 - 3.65 HENkaku Ensō
Maxwest Nitro 4 phone - Android 5.1
Laptop - Toshiba Satellite L305D-S5974 - Ubuntu 16.04 LTS

User avatar
Omega2058
Developer
Posts: 246
Joined: Tue Sep 28, 2010 4:27 am
Contact:

Re: sceSdGetLastIndex() kxploit

Post by Omega2058 » Thu Oct 02, 2014 6:03 am

Nice, this one is pretty interesting. :)

krystalgamer
Posts: 34
Joined: Sun Mar 17, 2013 5:35 pm
Location: /boot

Re: sceSdGetLastIndex() kxploit

Post by krystalgamer » Thu Oct 02, 2014 6:29 am

This looks awesome.I don't own a vita but it's really amazing to see this progress.
Ty, GBOT :3

maciak
Posts: 47
Joined: Wed May 25, 2011 11:58 am

Re: sceSdGetLastIndex() kxploit

Post by maciak » Thu Oct 02, 2014 6:51 am

Is it still valid on 3.30? ;)

User avatar
endrift
Guru
Posts: 42
Joined: Mon Feb 27, 2012 10:43 pm
Location: California
Contact:

Re: sceSdGetLastIndex() kxploit

Post by endrift » Thu Oct 02, 2014 8:00 am

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.

User avatar
Blaze
Posts: 186
Joined: Tue Sep 04, 2012 7:47 pm
Location: Greece

Re: sceSdGetLastIndex() kxploit

Post by Blaze » Thu Oct 02, 2014 8:13 am

maciak wrote:Is it still valid on 3.30? ;)
Even if it would, any type of kexploits won't get released.

User avatar
GBOT
Developer
Posts: 321
Joined: Wed Apr 25, 2012 8:00 pm
Contact:

Re: sceSdGetLastIndex() kxploit

Post by GBOT » Thu Oct 02, 2014 3:46 pm

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:
Github
Twitter

Can't give enough crepes

User avatar
Joel16
Posts: 912
Joined: Wed Oct 12, 2011 8:47 pm

Re: sceSdGetLastIndex() kxploit

Post by Joel16 » Sat Oct 04, 2014 3:47 pm

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
"Forever in darkness, a guardian devil."

Post Reply

Return to “Programming and Security”