I don't claim here that running puppy in frugal install mode provides a significant security advantage over a full install for the purposes of containing a process to a chroot jail but it is interesting nonetheless that a puppy running via frugal install will thwart the most naive chroot escape attempts. rufwoof, in one post made the comment that most attackers don't have a lot of skill so perhaps the ultra-naive hacker is out there that will get thwarted by a frugally installed puppy. So why is this so?
Well first some background. I found the following interesting:
(The smallest part, in fact. And you should be aware that root can always
get out of a chdir() if he just has enough tools - and the tools aren't
even very big. "mknod" + "mount" will do it even in the absense of a way
to add binaries, as will /proc access).
http://lkml.iu.edu/hypermail//linux/ker ... /0185.html
and so for curiosity I decided to search for chroot escapes using mknod and mount and the first thing I found was a briliantly naive attempt to escape out of a chroot:
https://gist.github.com/0xquad/7ea47ccf8e2c055748e7
It's brilliant because someone like me that has a hard time reading linux c code is able to follow it. It's naive because as we will see it not only assumes a full install but also assumes that the root will be at the top level of the device file system and only in one of the first two partitions.
So here is what it does, It essentially only looks at the first two partiations of the first two hard drives via this command:
Code: Select all
dev_t devs[] = {makedev(8, 0), makedev(8, 1), makedev(8, 2)};
https://gist.github.com/0xquad/7ea47ccf ... esc-c-L137
the function makedev takes the major and minor device number. These are given in columns 5 and 6 of the following table:
Code: Select all
brw-rw---- 1 root disk 8, 0 Mar 15 2002 sda
brw-rw---- 1 root disk 8, 1 Mar 15 2002 sda1
brw-rw---- 1 root disk 8, 2 Mar 15 2002 sda2
https://tldp.org/LDP/Linux-Filesystem-H ... l/dev.html
from which we see that the devices are sda, sda1 and sda2 (i.e. the first two partitions of the first hard drive). We make a directory to mount the device or a partion of the device:
Code: Select all
mkdir(mnt, 0755);
https://gist.github.com/0xquad/7ea47ccf ... sc-c-L144]
and then we try to mount the device or partion at this directory:
Code: Select all
ret = mount_fs(devs[i], fs_types[j], mnt);
https://gist.github.com/0xquad/7ea47ccf ... esc-c-L148
If this succeeds then we check to see if the directory has the following folders
Code: Select all
char *dirs[] = { "proc", "dev", "home", "usr", "var" };
https://gist.github.com/0xquad/7ea47ccf ... esc-c-L117
What I don't understand is why that stat command should throw an error if the directory exists:
Code: Select all
int isrootfs(const char *mnt)
...
err |= stat(path, &st);
...
return err;
https://gist.github.com/0xquad/7ea47ccf ... esc-c-L115
Code: Select all
int mount_escape()
...
if (isrootfs(mnt)) {
...
ret = shell();
}
https://gist.github.com/0xquad/7ea47ccf ... esc-c-L150
The return value of stat is as follows:
Code: Select all
On success, the functions return zero, and on error, −1 is returned and errno is set appropriately.
https://en.wikipedia.org/w/index.php?ti ... =956142916
So it looks to me that the code will only return a shell if one of the stat system calls fails (i.e. returns a -1). Since a true value in C is any non-zero value. This seems wrong to me, but I'm not an expert at c programming in linux.