22

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 "/" and to consider whether it is reasonably low, but I would like a more accurate method for detection.

[edit 20080916 142430 EST] Simply looking around the filesystem isn't sufficient, as it's not difficult to duplicate things like /boot and /dev to fool the jailed user.

[edit 20080916 142950 EST] For Linux systems, checking for unexpected values within /proc is reasonable, but what about systems that don't support /proc in the first place?

Topaz
  • 233
  • 1
  • 3
  • 8
  • 3
    See also [How do I tell I'm running in a chroot?](http://unix.stackexchange.com/questions/14345/how-do-i-tell-im-running-in-a-chroot/24248#24248) – Gilles 'SO- stop being evil' Nov 09 '11 at 12:28
  • 1
    Not totally portable (and only works as suid) but Debian-based systems have `ischroot` installed by default. See: https://manpages.debian.org/jessie/debianutils/ischroot.1.en.html – thom_nic Jan 10 '18 at 15:22

8 Answers8

13

The inode for / will always be 2 if it's the root directory of an ext2/ext3/ext4 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.

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
user10392
  • 1,384
  • 8
  • 12
  • 8
    Out of curiosity, what gets inode 1? – Topaz Sep 22 '08 at 20:48
  • 6
    bad blocks (historical/legacy) – user10392 Sep 24 '08 at 21:11
  • 6
    On linux, /sys and /proc are both inode == 1. devtmpfs is inode == 3. Cool trick but only reliable on "real" filesystems. – SpamapS Jan 31 '11 at 21:04
  • 9
    This isn't true for every filesystem. With XFS, for example, the root has inode 128. – caf Feb 22 '11 at 04:27
  • 2
    IOW, `root_inode=$(stat -c %i /); if [ $root_inode -ne 2 -a $root_inode -ne 128 ]; ...` – l0b0 Mar 03 '11 at 16:22
  • 1
    @Topaz *inode 1* represents a mount point that is currently in use (has something mounted). I've noticed that is not true of `devtmpfs`. – jankes May 29 '12 at 12:40
  • 1
    @SpamapS Not true about `devtmpfs` on Arch Linux `ext4` file system. `ls -di /dev` shows 1025. – zeekvfu Nov 07 '13 at 04:17
  • 1
    Another reason why this is not reliable is e.g. btrfs, Where the true / of the file hierarchy may still not be the root of the filesystem (but. e.g. a subvolume of it), which then has some other inode number. – calestyo Aug 28 '22 at 14:29
6

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)" ]
Community
  • 1
  • 1
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
5

If you are not in a chroot and the root filesystem is ext2/ext3/ext4, 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).

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
Jérôme Pouiller
  • 9,249
  • 5
  • 39
  • 47
  • Another reason why this is not reliable is e.g. btrfs, Where the true / of the file hierarchy may still not be the root of the filesystem (but. e.g. a subvolume of it), which then has some other inode number. – calestyo Aug 28 '22 at 14:30
4

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.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • +1 .. device+inode of /proc/1/root or /proc/1/exe, are how several programs in Debian and Ubuntu determine whether or not they're being run in a chroot. – SpamapS Feb 01 '11 at 10:09
  • If `/sbin/init` is hardlinked into the chroot environment, it will have the same dev/inode as the running one: false negative. If `/sbin/init` has been upgraded since the system was booted, it will have a different dev/inode even on the real root: false positive. – caf Feb 22 '11 at 04:32
  • The false negative here requires that root inode not be 2. The false positive is hyper-rare. – Joshua Feb 22 '11 at 20:51
4

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.

sammyo
  • 1,023
  • 1
  • 7
  • 8
2

I wanted the same information for a jail running on FreeBSD (as Ansible doesn't seem to detect this scenario).

On the FreeNAS distribution of FreeBSD 11, /proc is not mounted on the host, but it is within the jail. Whether this is also true on regular FreeBSD I don't know for sure, but procfs: Gone But Not Forgotten seems to suggest it is. Either way, you probably wouldn't want to try mounting it just to detect jail status and therefore I'm not certain it can be used as a reliable predictor of being within a jail.

I also ruled out using stat on / as certainly on FreeNAS all jails are given their own file system (i.e. a ZFS dataset) and therefore the / node on the host and in the jail both have inode 4. I expect this is common on FreeBSD 11 in general.

So the approach I settled on was using procstat on pid 0.

[root@host ~]# procstat 0
  PID  PPID  PGID   SID  TSID THR LOGIN    WCHAN     EMUL          COMM        
    0     0     0     0     0 1234 -        swapin    -             kernel      
[root@host ~]# echo $?
0
[root@host ~]# jexec guest tcsh
root@guest:/ # procstat 0
procstat: sysctl(kern.proc): No such process
procstat: procstat_getprocs()
root@guest:/ # echo $?
1

I am making an assumption here that pid 0 will always be the kernel on the host, and there won't be a pid 0 inside the jail.

Samuel Harmer
  • 4,264
  • 5
  • 33
  • 67
2

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.

SpoonMeiser
  • 19,918
  • 8
  • 50
  • 68
  • While it's easy to bind /proc, discrepancies within that data would be cumbersome to mask. Answer accepted. – Topaz Sep 16 '08 at 18:27
  • Actually, scratch that - what about non-linux systems that don't have /proc and friends to begin with? – Topaz Sep 16 '08 at 18:29
  • 1
    The question says to assume a standard Linux or BSD system. To the best of my knowledge, both have /proc. – SpoonMeiser Sep 16 '08 at 18:32
0

If you entered the chroot with schroot, then you can check the value of $debian_chroot.

Alex
  • 1,306
  • 3
  • 15
  • 25