본문 바로가기

컴터 때찌/E​xperience

Mempodipper - Linux Local Root for >=2.6.39, 32-bit and 64-bit


새로운 로컬 exploit이 나왔습니다

그동안 여러 공격에 대비해 읽기 쓰기 인터페이스인 /proc/pid/mem 보호가 충분했다고 판단해서
커널 버전 2.6.39 이상 부터 임의의 프로세스 메모리에 쓰기를 방지를 삭제했습니다.
이것 때문에 생긴 취약점을 이용해서 메모리에 코드를 올리는데
그 위치로 su 명령어의 .text section을 사용하네요 ㅎㅎ
대부분의 su 가 pie 옵션이 적용되지 않은체 컴파일 되어서 .text section의 ASLR이 해제 되어 있고
항상 일정한 위치의 su의 0x402178에 쉘코드를 넣어서 exploit 하는 방식입니다 
재밌네요 ㅎㅎ
pie 옵션에 대해 궁금하신 분은 x82님의 http://x82.inetcop.org/h0me/papers/FC_exploit/0x82-breakeat-pie.txt  를 보시면
간단히 나와있네요,



/proc/pid/mem 은 읽기와 쓰기를 위한 인터페이스이다. 직접적으로, 프로세스 메모리가 , 프로세스의 가상 공간으로써 같은 주소의 검색으로 쓰이는 주소이다,


2.6.39에서 방어에 대항에서 /proc/pid/mem 에 인증되지 않은 접근이 충분히 보호됬다고 생각했고, 그래서 임의의 프로세스 메모리에 쓰기 지원을 방지하는 #ifdef을 삭제했다.

올바른 권한을 가진 사람들은 누구나 프로세스 메모리에 쓸수 있다.

결국 권한 checking이 형편없이 되었다는것이 밝혀졌다

이것은 2.6.39 이상의 모든 커널이 취약하다는 것을 의미한다. 이것이 무슨 일을 일으키는지 오래된 커널의 코드를 한단계씩 살펴보자,  

   /proc/pid/mem 을 열어 보면 아래와 같은 코드를 호출한다

static int mem_open(struct inode* inode, struct file* file)

{

file->private_data = (void*)((long)current->self_exec_id);

/* OK to pass negative loff_t, we can catch out-of-range */

file->f_mode |= FMODE_UNSIGNED_OFFSET;

return 0;

}


오픈하는데 제한이 없다. 누구나 어떤 프로세스에 대한 /proc/pid/mem fd를 오픈할수 있다.

읽기 쓰기를 체크하는 중 열림과 동시에 저장하는 원래 프로세스의 self_exec_id 노트를 간단히 만들수 있게 한다.

하지만, 일기과 쓰기 제한을 확인한다.

쓰기 함수를 보면

static ssize_t mem_write(struct file * file, const char __user *buf,

size_t count, loff_t *ppos)

{

 

/* unimportant code removed for blog post */

 

struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);

 

/* unimportant code removed for blog post */

 

mm = check_mem_permission(task);

copied = PTR_ERR(mm);

if (IS_ERR(mm))

goto out_free;

 

/* unimportant code removed for blog post */

 

if (file->private_data != (void *)((long)current->self_exec_id))

goto out_mm;

 

/* unimportant code removed for blog post

 * (the function here goes onto write the buffer into the memory)

 */


그래서 인증되지 않은 쓰기를 대항하는 두개의 적절한 검사가 있다

check_mem_permission 과 self_exec_id이다.

하나씩 살펴보면

check_mem_permission 코드는 단순히 __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);

 

/*

* A task can always look at itself, in case it chooses

* to use system calls instead of load instructions.

*/

if (task == current)

return mm;

 

/*

* If current is actively ptrace'ing, and would also be

* permitted to freshly attach with ptrace now, permit it.

*/

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;

}

 

/*

* No one else is allowed.

*/

mmput(mm);

return ERR_PTR(-EPERM);

}


메모리 쓰기가 허가 되는 두가지 방법이 있다.

하나는 task == current인데 이것은 프로세스가 프로세스를 작성하거나, 현재 프로세스가 쓰여질 프로세스에 작업을 하기 위해 ptrace-level 권한을 가지고 있는 것을 의미한다.

당신은 ptrace 코드를 속일수 있다고 생각하는가? 이것을 솔깃하다, 하지만 나도 모른다.

대신에 어떻게 스스로 메모리를 쓰는 프로세스를 만들수 있는지 알아 보자, 이것이 task == current이다.



Now naturally, we want to write into the memory of suid processes, since then we can get root. Take a look at this:




.....



대부분의 su가 pie 옵션이 적용되지 않은체 컴파일 되었다는 것을 이용하여서 .text section 의 ASLR을 불능하게 만들었다.

그래서 우리는 지혜롭게 su를 사용했다, 메모리의 오프셋은 항상 동일하다

su의 exit@plt를 읽어서 0x402178에 쉘코드를 놓는다. 


So naturally, we want to write to 0×402178 minus the number of letters in the string “Unknown id: “, so that our shellcode is placed at exactly the right place.



ref:  http://www.exploit-db.com/download/18411

'컴터 때찌 > E​xperience' 카테고리의 다른 글

putty bepp turn off  (0) 2012.08.28
Local File Inclusion_Leopardan  (0) 2012.03.21
[ubuntu 7.04 package server]  (0) 2012.01.18
VMFusion Port Forwarding  (0) 2011.10.30
[MAC OSX]  (0) 2011.10.07