How can one detect being in a chroot jail without root privileges? Assume a standard BSD or Linux system. The best I came up with was to look at the inode value for \"/\"
Preventing stuff like that is the whole point. If it's your code that's supposed to run in the chroot, have it set a flag on startup. If you're hacking, hack: check for several common things in known locations, count the files in /etc, something in /dev.
I guess it depends why you might be in a chroot, and whether any effort has gone into disguising it.
I'd check /proc, these files are automatically generated system information files. The kernel will populate these in the root filesystem, but it's possible that they don't exist in the chroot filesystem.
If the root filesystem's /proc has been bound to /proc in the chroot, then it is likely that there are some discrepancies between that information and the chroot environment. Check /proc/mounts for example.
Similrarly, check /sys.
The inode for / will always be 2 if it's the root directory of a filesystem, but you may be chrooted inside a complete filesystem. If it's just chroot (and not some other virtualization), you could run mount and compare the mounted filesystems against what you see. Verify that every mount point has inode 2.
On BSD systems (check with uname -a), proc should always be present. Check if the dev/inode pair of /proc/1/exe (use stat on that path, it won't follow the symlink by text but by the underlying hook) matches /sbin/init.
Checking the root for inode #2 is also a good one.
On most other systems, a root user can find out much faster by attempting the fchdir root-breaking trick. If it goes anywhere you are in a chroot jail.
If you are not in a chroot, the inode for / will always be 2. You may check that using
stat -c %i /
or
ls -id /
Interresting, but let's try to find path of chroot directory. Ask to stat
on which device / is located:
stat -c %04D /
First byte is major of device and lest byte is minor. For example, 0802, means major 8, minor 1. If you check in /dev, you will see this device is /dev/sda2. If you are root you can directly create correspondong device in your chroot:
mknode /tmp/root_dev b 8 1
Now, let's find inode associated to our chroot. debugfs allows list contents of files using inode numbers. For exemple, ls -id /
returned 923960:
sudo debugfs /tmp/root_dev -R 'ls <923960>'
923960 (12) . 915821 (32) .. 5636100 (12) var
5636319 (12) lib 5636322 (12) usr 5636345 (12) tmp
5636346 (12) sys 5636347 (12) sbin 5636348 (12) run
5636349 (12) root 5636350 (12) proc 5636351 (12) mnt
5636352 (12) home 5636353 (12) dev 5636354 (12) boot
5636355 (12) bin 5636356 (12) etc 5638152 (16) selinux
5769366 (12) srv 5769367 (12) opt 5769375 (3832) media
Interesting information is inode of ..
entry: 915821. I can ask its content:
sudo debugfs /tmp/root_dev -R 'ls <915821>'
915821 (12) . 2 (12) .. 923960 (20) debian-jail
923961 (4052) other-jail
Directory called debian-jail
has inode 923960. So last component of my chroot dir is debian-jail
. Let's see parent directory (inode 2) now:
sudo debugfs /tmp/root_dev -R 'ls <2>'
2 (12) . 2 (12) .. 11 (20) lost+found 1046529 (12) home
130817 (12) etc 784897 (16) media 3603 (20) initrd.img
261633 (12) var 654081 (12) usr 392449 (12) sys 392450 (12) lib
784898 (12) root 915715 (12) sbin 1046530 (12) tmp
1046531 (12) bin 784899 (12) dev 392451 (12) mnt
915716 (12) run 12 (12) proc 1046532 (12) boot 13 (16) lib64
784945 (12) srv 915821 (12) opt 3604 (3796) vmlinuz
Directory called opt
has inode 915821 and inode 2 is root of filesystem. So my chroot directory is /opt/debian-jail
. Sure, /dev/sda1
may be mounted on another filesystem. You need to check that (use lsof or directly picking information /proc
).
On Linux with root permissions, test if the root directory of the init process is your root directory. Although /proc/1/root
is always a symbolic link to /
, following it leads to the “master” root directory (assuming the init process is not chrooted, but that's hardly ever done). If /proc
isn't mounted, you can bet you're in a chroot.
[ "$(stat -c %d:%i /)" != "$(stat -c %d:%i /proc/1/root/.)" ]
# With ash/bash/ksh/zsh
! [ -x /proc/1/root/. ] || [ /proc/1/root/. -ef / ]
This is more precise than looking at /proc/1/exe because that could be different outside a chroot if init
has been upgraded since the last boot or if the chroot is on the main root filesystem and init
is hard linked in it.
If you do not have root permissions, you can look at /proc/1/mountinfo
and /proc/$$/mountinfo
(briefly documented in filesystems/proc.txt in the Linux kernel documentation). This file is world-readable and contains a lot of information about each mount point in the process's view of the filesystem. The paths in that file are restricted by the chroot affecting the reader process, if any. If the process reading /proc/1/mountinfo
is chrooted into a filesystem that's different from the global root (assuming pid 1's root is the global root), then no entry for /
appears in /proc/1/mountinfo
. If the process reading /proc/1/mountinfo
is chrooted to a directory on the global root filesystem, then an entry for /
appears in /proc/1/mountinfo
, but with a different mount id. Incidentally, the root field ($4
) indicates where the chroot is in its master filesystem. Again, this is specific to Linux.
[ "$(awk '$5=="/" {print $1}' </proc/1/mountinfo)" != "$(awk '$5=="/" {print $1}' </proc/$$/mountinfo)" ]