Fuse is Alive!!!! on PlayStation 4 Retail


About security on devices

Big corporations often bring us cool electronic devices like:

When you want to analyze one of these cool electronic devices, you need to do a research, to get all public information available about it like:

Sometimes devices use propietary operating systems and libraries, but lately, almost all are based on different linux or bsd flavours and many open source libraries. Is that bad? Of course not, a lot of money saving with it, but security is an important issue and must be an important point at design time. Don't you?

All big corporations should have a security department, with different security technical profiles, tracking all security issues about a device from the begining of the project. It is very important for a good service level for final customers and also to avoid marketing disasters related to security issues.

One of the gold rule/best practice is called Security by Design. Involving security guys from early stages of the project, and analyzing in each stage all issues related to security from different points of view to avoid future problems and mitigate risks is a must have. Of course other best practice can be the teams who check security issues should be different from development teams who are making the code.

I speak about mitigation, because eliminate all risks is impossible. You should need leave device inside a faraday jail, of course without internet connection and without final user using it, and never power on the device to eliminate only some risks xD.

I am engineer, and sometimes some engineers when they design a device, they believe that people will use their devices only how they were designed, without considering that factor, the Final User's factor. People will use the device and some of them can do things that will change the normal expected execution conditions. Without considering that behaviour, you will have a bad design from the point of view of security. Remember mitigate.

A good risk management and of course costs will decide which measures must be considered in each stage of the project, for a right risks mitigation.


Reverse Engineering

It's an art for me. The art of know how things are done without full information about who or about how was made/coded/manufactured it.

Which is my experience/background in this field?

I am 45 years old and since 80's i have done things with different platforms/devices in personal life:

Reverse Engineering on PlayStation 4

First of all we will try answer the initial questions:

We need access to decrypted kernels from different firmware versions but you can dump yourself with proper tools.

I have different PlayStation 4 consoles with different firmwares from 1.05,1.76,4.x,5.05,and one always in last firmware to play with games.

Next is time to use ida/ghidra to get a lot of fun. Kernel from 1.05 has almost all symbols intact so is pure gold, let to translate/look for functions to compare with other firmware versions without symbols easily and you can track the changes from one version to other.

Sony has basically 3 different console flavours:

I have only retail PS4 models :( but nevermind many things can be done still with retail :P.

Fuse: what on earth is it?

Well the best is go to the source: libfuse project

But better go to freebsd flavour:

Other good reference is before kernel incorporation was done here.

Fuse High level diagram

Advice: download a 10.3 freebsd vanilla to avoid problems with deprecated port version for newbies. The first cd iso is enough to get a fresh vm to play with fuse stuff and jails.

So a good challenge, let's the game begin!!!


Fuse kernel module

Freebsd kernel modules can be loaded/unloaded in vanilla freebsd with kldload/kldunload utils in command line. We will check the kernel module included already on freebsd kernel and different modification of it to add some features for jails and from greater versions and to mimic the PlayStation 4 behaviour, it will help to trace problems and understand how stuff is done.


root@bigfree10:~ # kld
kldconfig kldload   kldstat   kldunload kldxref
root@bigfree10:~ # kldstat
Id Refs Address            Size     Name
 1    1 0xffffffff80200000 17bcd08  kernel
root@bigfree10:~ # kldload fuse
root@bigfree10:~ # kldstat
Id Refs Address            Size     Name
 1    3 0xffffffff80200000 17bcd08  kernel
 2    1 0xffffffff81a11000 df9a     fuse.ko

root@bigfree10:~ # lsvfs
Filesystem                              Num  Refs  Flags
-------------------------------- ---------- -----  ---------------
devfs                            0x00000071     1  synthetic, jail
ufs                              0x00000035     1
msdosfs                          0x00000032     0
nfs                              0x0000003a     0  network
cd9660                           0x000000bd     0  read-only
procfs                           0x00000002     0  synthetic, jail
fusefs                           0x000000ed     0  synthetic
root@bigfree10:/usr/src/sys/fs/fuse # kldunload fuse
root@bigfree10:/usr/src/sys/fs/fuse # lsvfs
Filesystem                              Num  Refs  Flags
-------------------------------- ---------- -----  ---------------
devfs                            0x00000071     1  synthetic, jail
ufs                              0x00000035     1
msdosfs                          0x00000032     0
nfs                              0x0000003a     0  network
cd9660                           0x000000bd     0  read-only
procfs                           0x00000002     0  synthetic, jail

root@bigfree10:~ # cd /usr/src/sys/fs/fuse
root@bigfree10:/usr/src/sys/fs/fuse # ls
@			fuse_internal.c		fuse_node.h
Makefile		fuse_internal.h		fuse_node.o
export_syms		fuse_internal.o		fuse_param.h
fuse.h			fuse_io.c		fuse_vfsops.c
fuse.ko			fuse_io.h		fuse_vfsops.o
fuse.ko.debug		fuse_io.o		fuse_vnops.c
fuse.ko.symbols		fuse_ipc.c		fuse_vnops.o
fuse_debug.h		fuse_ipc.h		machine
fuse_device.c		fuse_ipc.o		salida.txt
fuse_device.o		fuse_kernel.h		vnode_if.h
fuse_file.c		fuse_main.c		vnode_if_newproto.h
fuse_file.h		fuse_main.o		vnode_if_typedef.h
fuse_file.o		fuse_node.c		x86
root@bigfree10:/usr/src/sys/fs/fuse # kldload ./fuse.ko
root@bigfree10:/usr/src/sys/fs/fuse # kldstat
Id Refs Address            Size     Name
 1    3 0xffffffff80200000 17bcd08  kernel
 2    1 0xffffffff81a11000 d38d     fuse.ko
root@bigfree10:/usr/src/sys/fs/fuse # lsvfs
Filesystem                              Num  Refs  Flags
-------------------------------- ---------- -----  ---------------
devfs                            0x00000071     1  synthetic, jail
ufs                              0x00000035     1
msdosfs                          0x00000032     0
nfs                              0x0000003a     0  network
cd9660                           0x000000bd     0  read-only
procfs                           0x00000002     0  synthetic, jail
fusefs                           0x000000ed     0  synthetic, jail


					

We are in PlayStation 4 retail so no command line available obviously. No problem, there is api in kernel land to make the same. But... hey 5.05 kernel on retail has many kld functions in not implemented mode so return always 0x4e aka ENOSYS fuck!!!, devkit kernel has it implemented. It could be a problem if fuse kernel module was not implemented in kernel land on retail. But lucky you it is implemented in kernel and fuse_loader function is waiting for us at offset 0x49DDB0 in 5.05. So we only need some patches to some functions to call fuse_loader and if all is fine it must be return 0.

I got some problems when i was tryng to open/create fuse device although module was loaded fine, after some research i found the function needed to patch to avoid the fucking EPERM retur code xD. Example fuse_device_open available at offset 0x4A2850:


static int fuse_device_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
{
	struct fuse_data *fdata;	

	if(sceSblACMgrIsDebuggerProcess(td->td_ucred))
	{
		result=ENXIO; //6
		if(dev->si_usecount>=2)
		{
			result=EBUSY;//16
			if(dev->si_usecount==2)
			{
				result=0;
				mtx_lock_flags(&fuse_mtx,0,__FUNCTION__,__LINE__);
				if(fuse_get_devdata(dev)) //dev->si_drv1
				{
					fdata->dataflags|=0x8000; //fdata+33=|0x80
					mtx_unlock_sleep(&fuse_mtx,0,__FUNCTION__,__LINE__);
				}
				else
				{
					mtx_unlock_sleep(&fuse_mtx,0,__FUNCTION__,__LINE__);
					result=ENXIO;//6
				}

			}

		}
	}
	else if(sceSblACMgrIsSyscoreProcess(td->td_ucred))
	{
		result=EBUSY;
		if(dev->si_usecount<=1)
		{
			fdata = fdata_alloc(dev, td->td_ucred);
			mtx_lock_flags(&fuse_mtx,0,__FUNCTION__,__LINE__);
			if(fuse_get_devdata(dev)) //dev->si_drv1
			{
				fdata_trydestroy(fdata);
				mtx_unlock_sleep(&fuse_mtx,0,__FUNCTION__,__LINE__);
			}
			else
			{
				fdata->dataflags |= FSESS_OPENED;//2
				dev->si_drv1 = fdata;
				mtx_unlock_sleep(&fuse_mtx,0,__FUNCTION__,__LINE__);
				result=0;
			}	
		}
	}
	else
	{
		result=EPERM;  //1 FUCKING EPERM Operation not permitted on vanilla ps4 retail
	}
	return result;

}
					

So patches needed on 5.05 are:


		int *ksuser_enabled=(int *)(kernel_base+0x2300B88);
		struct vfsconf *p=(struct vfsconf *)(kernel_base + 0x19FC380 );
		//suser_enabled in priv_check_cred
		*ksuser_enabled=1;
		//add jail friendly for fuse file system
		p->vfc_flags=0x00400000 | 0x00080000;
		//avoid enforce_dev_perms checks
		//*kfuse_enforce_dev_perms=0;
		//default prison_priv_check to 0
		kernel_ptr[0x3B219E]=0;

		//skip devkit/testkit/dipsw check in fuse_loader
		kernel_ptr[0x49DDDE] = 0xEB;
		kernel_ptr[0x49DDDF] = 0x1B;

		//skip sceSblACMgrIsSyscoreProcess check in fuse_open_device
		kernel_ptr[0x4A28EE] = 0xEB;
		kernel_ptr[0x4A28EF] = 0x0;

		//skip sceSblACMgrIsDebuggerProcess/sceSblACMgrIsSyscoreProcess check in fuse_close_device
		kernel_ptr[0x4A29E2] = 0xEB;

		//skip sceSblACMgrIsDebuggerProcess/sceSblACMgrIsSyscoreProcess check in fuse_poll_device
		kernel_ptr[0x4A2F34] = 0xEB;
		
		// skip sceSblACMgrIsSyscoreProcess check in fuse_vfsop_mount
		kernel_ptr[0x4A30F7] = 0xEB;
		kernel_ptr[0x4A30F8] = 0x04;
  
		// skip sceSblACMgrIsMinisyscore/unknown check in fuse_vfsop_unmount
		kernel_ptr[0x4A384C] = 0xEB;
		kernel_ptr[0x4A384D] = 0x00;
  
		// skip sceSblACMgrIsSystemUcred check in fuse_vfsop_statfs
		kernel_ptr[0x4A3BED] = 0xEB;
		kernel_ptr[0x4A3BEE] = 0x04;	
				

And for call fuse_loader:


		printfkernel("load kernel module FUSEFS\n");
		// load FUSEFS module
		int (*fuse_loader)(void* m, int op, void* arg)=(void *)(kernel_base + 0x49DDB0);
		printfkernel("calling fuse_loader\n");
		kernel_ptr[0x4A27FC]=0xB6; <-----let create fuse device with rw-rw-rw
		ret=fuse_loader(NULL, 0, NULL);
		printfkernel(" fuse_loader return %d\n",ret);
				

Choose the best stable way to apply patches. You will have inestability/panic issues without a proper conditional hook system, avoid permanent patches in common functions or you can get a lot of panics.


libfuse

For libfuse try to use 2.8.6 release. For liborbis minimal changes are needed mainly avoid forking stuff and calling external mount_fusefs tool not available on retail in mount_bsd.c. We will do it in kernel land with special function.

Fuse file system

We will use fuse-nfs but threre are plenty of options availables. This in particular needs libnfs.

For libnfs try all first in vanilla freebsd. Build will generate rpc functions on fly, so it is better to generate all first and then get the .c and .h files for liborbis. Problem found was that there are some functions not availables in c implementation on PlayStation 4. You cand avoid it in rpc_connect_sockaddr_async in socket.c instead of use getservbyport you can control the ports negotiation and retry until is fine, check on freebsd vanilla first. It is important here get access to backport traces on panics to fix things and network logs from debugnet on liborbis.

No PlayStation 4 were harmed in the making of the stuff described in this article, many reboots only :P

December 2019


Thanks