How to create a chroot from which root cannot escape using chdir(“..”) on Linux, inside a filesystem?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












1















I have a directory /var/mychoot on the same filesystem as /, and I've started the program /var/mychroot/prog as sudo chroot /var/mychroot /prog, so the program is running as EUID 0.



If the program executes the chdir("..") escape technique, then it is able to escape the chroot and see everything within /. (I've verified this on Linux 4.18.)



I want to prevent such an escape. In fact I want to prevent all kinds of chroot escapes, but in this question I'm only interested in how the chdir("..") escape technique can be prevented on modern Linux systems. For this I'm looking for alternatives of the chroot(2) system call.



I've found 2 solutions: pivot_root and MS_MOVE, but they only work if /var/mychroot is a mount point, so they fail if /var/mychroot is just a subdirectory within the / filesystem. Is there another solution in this case?



I want to avoid techniques using LD_PRELOAD (because LD_PRELOAD doesn't affect statically linked executables), techniques using ptrace(2) (because then I'm not able to run strace within the chroot, and also ptrace(2) is very tricky to get right: processes will crash or hang) and real virtualization (e.g. Xen or KVM or QEMU; because of the performance overhead and the less flexible memory provisioning).



To recap, I need:



  • an alternative of chroot(2) system call,

  • with which root can restrict processes running as root (EUID 0),

  • to a subdirectory of the filesystem of /,

  • which prevents the chdir("..") escape technique,

  • and doesn't use LD_PRELOAD or


  • ptrace(2) or

  • virtualization (e.g. Xen, KVM or QEMU),

  • and it runs on a modern Linux system,

  • with and unpatched kernel.

Does it exist?










share|improve this question
























  • Why would you need root to run in a jail? Usually you'd drop root privileges once set up and depend on services or setuid exes for access to privileged calls and areas (e.g., network).

    – melds
    Jan 5 at 2:17






  • 1





    @melds: For example, running in a jail as root is useful if apt-get install UNTRUSTED-PACKAGE is run, where the the install script of the package can contain malicious code. A similar use case is docker run UNTRUSTED-IMAGE, the default commands of such packages run as root. Docker is not using chroot for filesystem isolation, and I'm also interested in understanding what would break if it did, and how it could be fixed.

    – pts
    Jan 5 at 2:29
















1















I have a directory /var/mychoot on the same filesystem as /, and I've started the program /var/mychroot/prog as sudo chroot /var/mychroot /prog, so the program is running as EUID 0.



If the program executes the chdir("..") escape technique, then it is able to escape the chroot and see everything within /. (I've verified this on Linux 4.18.)



I want to prevent such an escape. In fact I want to prevent all kinds of chroot escapes, but in this question I'm only interested in how the chdir("..") escape technique can be prevented on modern Linux systems. For this I'm looking for alternatives of the chroot(2) system call.



I've found 2 solutions: pivot_root and MS_MOVE, but they only work if /var/mychroot is a mount point, so they fail if /var/mychroot is just a subdirectory within the / filesystem. Is there another solution in this case?



I want to avoid techniques using LD_PRELOAD (because LD_PRELOAD doesn't affect statically linked executables), techniques using ptrace(2) (because then I'm not able to run strace within the chroot, and also ptrace(2) is very tricky to get right: processes will crash or hang) and real virtualization (e.g. Xen or KVM or QEMU; because of the performance overhead and the less flexible memory provisioning).



To recap, I need:



  • an alternative of chroot(2) system call,

  • with which root can restrict processes running as root (EUID 0),

  • to a subdirectory of the filesystem of /,

  • which prevents the chdir("..") escape technique,

  • and doesn't use LD_PRELOAD or


  • ptrace(2) or

  • virtualization (e.g. Xen, KVM or QEMU),

  • and it runs on a modern Linux system,

  • with and unpatched kernel.

Does it exist?










share|improve this question
























  • Why would you need root to run in a jail? Usually you'd drop root privileges once set up and depend on services or setuid exes for access to privileged calls and areas (e.g., network).

    – melds
    Jan 5 at 2:17






  • 1





    @melds: For example, running in a jail as root is useful if apt-get install UNTRUSTED-PACKAGE is run, where the the install script of the package can contain malicious code. A similar use case is docker run UNTRUSTED-IMAGE, the default commands of such packages run as root. Docker is not using chroot for filesystem isolation, and I'm also interested in understanding what would break if it did, and how it could be fixed.

    – pts
    Jan 5 at 2:29














1












1








1








I have a directory /var/mychoot on the same filesystem as /, and I've started the program /var/mychroot/prog as sudo chroot /var/mychroot /prog, so the program is running as EUID 0.



If the program executes the chdir("..") escape technique, then it is able to escape the chroot and see everything within /. (I've verified this on Linux 4.18.)



I want to prevent such an escape. In fact I want to prevent all kinds of chroot escapes, but in this question I'm only interested in how the chdir("..") escape technique can be prevented on modern Linux systems. For this I'm looking for alternatives of the chroot(2) system call.



I've found 2 solutions: pivot_root and MS_MOVE, but they only work if /var/mychroot is a mount point, so they fail if /var/mychroot is just a subdirectory within the / filesystem. Is there another solution in this case?



I want to avoid techniques using LD_PRELOAD (because LD_PRELOAD doesn't affect statically linked executables), techniques using ptrace(2) (because then I'm not able to run strace within the chroot, and also ptrace(2) is very tricky to get right: processes will crash or hang) and real virtualization (e.g. Xen or KVM or QEMU; because of the performance overhead and the less flexible memory provisioning).



To recap, I need:



  • an alternative of chroot(2) system call,

  • with which root can restrict processes running as root (EUID 0),

  • to a subdirectory of the filesystem of /,

  • which prevents the chdir("..") escape technique,

  • and doesn't use LD_PRELOAD or


  • ptrace(2) or

  • virtualization (e.g. Xen, KVM or QEMU),

  • and it runs on a modern Linux system,

  • with and unpatched kernel.

Does it exist?










share|improve this question
















I have a directory /var/mychoot on the same filesystem as /, and I've started the program /var/mychroot/prog as sudo chroot /var/mychroot /prog, so the program is running as EUID 0.



If the program executes the chdir("..") escape technique, then it is able to escape the chroot and see everything within /. (I've verified this on Linux 4.18.)



I want to prevent such an escape. In fact I want to prevent all kinds of chroot escapes, but in this question I'm only interested in how the chdir("..") escape technique can be prevented on modern Linux systems. For this I'm looking for alternatives of the chroot(2) system call.



I've found 2 solutions: pivot_root and MS_MOVE, but they only work if /var/mychroot is a mount point, so they fail if /var/mychroot is just a subdirectory within the / filesystem. Is there another solution in this case?



I want to avoid techniques using LD_PRELOAD (because LD_PRELOAD doesn't affect statically linked executables), techniques using ptrace(2) (because then I'm not able to run strace within the chroot, and also ptrace(2) is very tricky to get right: processes will crash or hang) and real virtualization (e.g. Xen or KVM or QEMU; because of the performance overhead and the less flexible memory provisioning).



To recap, I need:



  • an alternative of chroot(2) system call,

  • with which root can restrict processes running as root (EUID 0),

  • to a subdirectory of the filesystem of /,

  • which prevents the chdir("..") escape technique,

  • and doesn't use LD_PRELOAD or


  • ptrace(2) or

  • virtualization (e.g. Xen, KVM or QEMU),

  • and it runs on a modern Linux system,

  • with and unpatched kernel.

Does it exist?







linux chroot containers






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 5 at 6:57









filbranden

7,4002837




7,4002837










asked Jan 5 at 0:46









ptspts

384413




384413












  • Why would you need root to run in a jail? Usually you'd drop root privileges once set up and depend on services or setuid exes for access to privileged calls and areas (e.g., network).

    – melds
    Jan 5 at 2:17






  • 1





    @melds: For example, running in a jail as root is useful if apt-get install UNTRUSTED-PACKAGE is run, where the the install script of the package can contain malicious code. A similar use case is docker run UNTRUSTED-IMAGE, the default commands of such packages run as root. Docker is not using chroot for filesystem isolation, and I'm also interested in understanding what would break if it did, and how it could be fixed.

    – pts
    Jan 5 at 2:29


















  • Why would you need root to run in a jail? Usually you'd drop root privileges once set up and depend on services or setuid exes for access to privileged calls and areas (e.g., network).

    – melds
    Jan 5 at 2:17






  • 1





    @melds: For example, running in a jail as root is useful if apt-get install UNTRUSTED-PACKAGE is run, where the the install script of the package can contain malicious code. A similar use case is docker run UNTRUSTED-IMAGE, the default commands of such packages run as root. Docker is not using chroot for filesystem isolation, and I'm also interested in understanding what would break if it did, and how it could be fixed.

    – pts
    Jan 5 at 2:29

















Why would you need root to run in a jail? Usually you'd drop root privileges once set up and depend on services or setuid exes for access to privileged calls and areas (e.g., network).

– melds
Jan 5 at 2:17





Why would you need root to run in a jail? Usually you'd drop root privileges once set up and depend on services or setuid exes for access to privileged calls and areas (e.g., network).

– melds
Jan 5 at 2:17




1




1





@melds: For example, running in a jail as root is useful if apt-get install UNTRUSTED-PACKAGE is run, where the the install script of the package can contain malicious code. A similar use case is docker run UNTRUSTED-IMAGE, the default commands of such packages run as root. Docker is not using chroot for filesystem isolation, and I'm also interested in understanding what would break if it did, and how it could be fixed.

– pts
Jan 5 at 2:29






@melds: For example, running in a jail as root is useful if apt-get install UNTRUSTED-PACKAGE is run, where the the install script of the package can contain malicious code. A similar use case is docker run UNTRUSTED-IMAGE, the default commands of such packages run as root. Docker is not using chroot for filesystem isolation, and I'm also interested in understanding what would break if it did, and how it could be fixed.

– pts
Jan 5 at 2:29











2 Answers
2






active

oldest

votes


















2














To protect against the specific chdir("..") escape technique you mentioned, you can simply drop the capability to execute chroot(2) again once you're chrooted to /var/mychroot yourself. The escape technique requires another call to chroot(), so blocking that is enough to prevent it from working.



You can do that with Linux capabilities, by dropping CAP_SYS_CHROOT which is the one needed for chroot(2) to be available.



For example:



outside# chroot /var/mychroot
inside# capsh --drop=cap_sys_chroot --
inside# ./escape_chroot
chroot(baz): Operation not permitted


(The second prompt inside the chroot is from a shell spawned from capsh. You can make it run another command with, for example, capsh --drop=cap_sys_chroot -- -c 'exec ./escape_chroot'.)




But a much better technique is to just use pivot_root, since it protects from many of the other possible exploits that chroot(2) will not protect from.



You mentioned that it only works if /var/mychroot is a mount point, but you can make it a mount point by simply bind mounting it into itself.



Note that you need to create a mount namespace to use pivot_root to create a jail, otherwise it will try to change the root of all processes in your filesystem, which is most likely not what you want here...



So the whole sequence goes:



outside# unshare -m
outside# mount --bind /var/mychroot /var/mychroot
outside# cd /var/mychroot
outside# mkdir old_root
outside# pivot_root . old_root
limbo# exec chroot .
inside# umount /old_root


(Again, many of these commands are spawning new shells. unshare does that, so does chroot itself. You can work around those by passing commands as extra arguments. In some cases you might want to pass sh -c '...' for a full script.)



At this point, you're inside a pivot_root jail in a separate mount namespace, and the fact that /var/mychroot is simply a directory of the original root (and not a mount of a separate device or loop device) didn't really prevent this from working, thanks to the bind mount into itself.



Running the escape code, you'll see that the jail works as expected (even though the escape code claims otherwise):



inside# touch /inside_jail
inside# ./escape_chroot
Exploit seems to work. =)
inside# # ls -ld /inside_jail /old_root
-rw-r--r--. 1 0 0 0 Jan 5 23:45 /inside_jail
dr-xr-xr-x. 20 0 0 4096 Jul 5 23:45 /old_root


As you can see, still inside the jail... The exploit code is just a bit naive and assumes that as long as the operations (chroot, chdir) have succeeded, that was enough to escape the jail, which is not really the case...



So consider using this technique to create a jail that is superior to chroot and does not require using Linux capabilities to block operations inside it (such as creating additional chroots, which might actually be useful or required for what you're actually trying to run in a jail.)






share|improve this answer


















  • 1





    FYI I've found the command-line tool jchroot which automates this pivot_root(2) technique. I've verified on Linux 3.13 and 4.18 that the chdir("..") escape technique doesn't work with this pivot_root(2) technique as used by jchroot in github.com/vincentbernat/jchroot/blob/…

    – pts
    Jan 5 at 12:47


















3















they only work if /var/mychroot is a mount point, so they fail if /var/mychroot is just a subdirectory within the / filesystem.




You can make any directory a mount point: mount --rbind /var/mychoot /var/mychoot.



This step - and others that you will need - are covered here:



How to perform chroot with Linux namespaces?




In fact I want to prevent all kinds of chroot escapes




Combine the above with user namespaces.



Alternatively, seccomp. This is how Docker prevents contained processes calling mount(), for example. (When you mount a block device a second time, you get a second view of that filesystem, which more or less counts as an escape). Copy Docker's list of allowed syscalls - root has lots of different ways of escaping.



https://docs.docker.com/engine/security/seccomp/



https://github.com/moby/moby/blob/master/profiles/seccomp/default.json






share|improve this answer




















  • 1





    Thank you for both the solution and the insightful links on hardening a containers and chroot environments. Unfortunately it's not possible to accept multiple answers, but you have my very keen upvote.

    – pts
    Jan 5 at 12:51











Your Answer








StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "106"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f492607%2fhow-to-create-a-chroot-from-which-root-cannot-escape-using-chdir-on-linux%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









2














To protect against the specific chdir("..") escape technique you mentioned, you can simply drop the capability to execute chroot(2) again once you're chrooted to /var/mychroot yourself. The escape technique requires another call to chroot(), so blocking that is enough to prevent it from working.



You can do that with Linux capabilities, by dropping CAP_SYS_CHROOT which is the one needed for chroot(2) to be available.



For example:



outside# chroot /var/mychroot
inside# capsh --drop=cap_sys_chroot --
inside# ./escape_chroot
chroot(baz): Operation not permitted


(The second prompt inside the chroot is from a shell spawned from capsh. You can make it run another command with, for example, capsh --drop=cap_sys_chroot -- -c 'exec ./escape_chroot'.)




But a much better technique is to just use pivot_root, since it protects from many of the other possible exploits that chroot(2) will not protect from.



You mentioned that it only works if /var/mychroot is a mount point, but you can make it a mount point by simply bind mounting it into itself.



Note that you need to create a mount namespace to use pivot_root to create a jail, otherwise it will try to change the root of all processes in your filesystem, which is most likely not what you want here...



So the whole sequence goes:



outside# unshare -m
outside# mount --bind /var/mychroot /var/mychroot
outside# cd /var/mychroot
outside# mkdir old_root
outside# pivot_root . old_root
limbo# exec chroot .
inside# umount /old_root


(Again, many of these commands are spawning new shells. unshare does that, so does chroot itself. You can work around those by passing commands as extra arguments. In some cases you might want to pass sh -c '...' for a full script.)



At this point, you're inside a pivot_root jail in a separate mount namespace, and the fact that /var/mychroot is simply a directory of the original root (and not a mount of a separate device or loop device) didn't really prevent this from working, thanks to the bind mount into itself.



Running the escape code, you'll see that the jail works as expected (even though the escape code claims otherwise):



inside# touch /inside_jail
inside# ./escape_chroot
Exploit seems to work. =)
inside# # ls -ld /inside_jail /old_root
-rw-r--r--. 1 0 0 0 Jan 5 23:45 /inside_jail
dr-xr-xr-x. 20 0 0 4096 Jul 5 23:45 /old_root


As you can see, still inside the jail... The exploit code is just a bit naive and assumes that as long as the operations (chroot, chdir) have succeeded, that was enough to escape the jail, which is not really the case...



So consider using this technique to create a jail that is superior to chroot and does not require using Linux capabilities to block operations inside it (such as creating additional chroots, which might actually be useful or required for what you're actually trying to run in a jail.)






share|improve this answer


















  • 1





    FYI I've found the command-line tool jchroot which automates this pivot_root(2) technique. I've verified on Linux 3.13 and 4.18 that the chdir("..") escape technique doesn't work with this pivot_root(2) technique as used by jchroot in github.com/vincentbernat/jchroot/blob/…

    – pts
    Jan 5 at 12:47















2














To protect against the specific chdir("..") escape technique you mentioned, you can simply drop the capability to execute chroot(2) again once you're chrooted to /var/mychroot yourself. The escape technique requires another call to chroot(), so blocking that is enough to prevent it from working.



You can do that with Linux capabilities, by dropping CAP_SYS_CHROOT which is the one needed for chroot(2) to be available.



For example:



outside# chroot /var/mychroot
inside# capsh --drop=cap_sys_chroot --
inside# ./escape_chroot
chroot(baz): Operation not permitted


(The second prompt inside the chroot is from a shell spawned from capsh. You can make it run another command with, for example, capsh --drop=cap_sys_chroot -- -c 'exec ./escape_chroot'.)




But a much better technique is to just use pivot_root, since it protects from many of the other possible exploits that chroot(2) will not protect from.



You mentioned that it only works if /var/mychroot is a mount point, but you can make it a mount point by simply bind mounting it into itself.



Note that you need to create a mount namespace to use pivot_root to create a jail, otherwise it will try to change the root of all processes in your filesystem, which is most likely not what you want here...



So the whole sequence goes:



outside# unshare -m
outside# mount --bind /var/mychroot /var/mychroot
outside# cd /var/mychroot
outside# mkdir old_root
outside# pivot_root . old_root
limbo# exec chroot .
inside# umount /old_root


(Again, many of these commands are spawning new shells. unshare does that, so does chroot itself. You can work around those by passing commands as extra arguments. In some cases you might want to pass sh -c '...' for a full script.)



At this point, you're inside a pivot_root jail in a separate mount namespace, and the fact that /var/mychroot is simply a directory of the original root (and not a mount of a separate device or loop device) didn't really prevent this from working, thanks to the bind mount into itself.



Running the escape code, you'll see that the jail works as expected (even though the escape code claims otherwise):



inside# touch /inside_jail
inside# ./escape_chroot
Exploit seems to work. =)
inside# # ls -ld /inside_jail /old_root
-rw-r--r--. 1 0 0 0 Jan 5 23:45 /inside_jail
dr-xr-xr-x. 20 0 0 4096 Jul 5 23:45 /old_root


As you can see, still inside the jail... The exploit code is just a bit naive and assumes that as long as the operations (chroot, chdir) have succeeded, that was enough to escape the jail, which is not really the case...



So consider using this technique to create a jail that is superior to chroot and does not require using Linux capabilities to block operations inside it (such as creating additional chroots, which might actually be useful or required for what you're actually trying to run in a jail.)






share|improve this answer


















  • 1





    FYI I've found the command-line tool jchroot which automates this pivot_root(2) technique. I've verified on Linux 3.13 and 4.18 that the chdir("..") escape technique doesn't work with this pivot_root(2) technique as used by jchroot in github.com/vincentbernat/jchroot/blob/…

    – pts
    Jan 5 at 12:47













2












2








2







To protect against the specific chdir("..") escape technique you mentioned, you can simply drop the capability to execute chroot(2) again once you're chrooted to /var/mychroot yourself. The escape technique requires another call to chroot(), so blocking that is enough to prevent it from working.



You can do that with Linux capabilities, by dropping CAP_SYS_CHROOT which is the one needed for chroot(2) to be available.



For example:



outside# chroot /var/mychroot
inside# capsh --drop=cap_sys_chroot --
inside# ./escape_chroot
chroot(baz): Operation not permitted


(The second prompt inside the chroot is from a shell spawned from capsh. You can make it run another command with, for example, capsh --drop=cap_sys_chroot -- -c 'exec ./escape_chroot'.)




But a much better technique is to just use pivot_root, since it protects from many of the other possible exploits that chroot(2) will not protect from.



You mentioned that it only works if /var/mychroot is a mount point, but you can make it a mount point by simply bind mounting it into itself.



Note that you need to create a mount namespace to use pivot_root to create a jail, otherwise it will try to change the root of all processes in your filesystem, which is most likely not what you want here...



So the whole sequence goes:



outside# unshare -m
outside# mount --bind /var/mychroot /var/mychroot
outside# cd /var/mychroot
outside# mkdir old_root
outside# pivot_root . old_root
limbo# exec chroot .
inside# umount /old_root


(Again, many of these commands are spawning new shells. unshare does that, so does chroot itself. You can work around those by passing commands as extra arguments. In some cases you might want to pass sh -c '...' for a full script.)



At this point, you're inside a pivot_root jail in a separate mount namespace, and the fact that /var/mychroot is simply a directory of the original root (and not a mount of a separate device or loop device) didn't really prevent this from working, thanks to the bind mount into itself.



Running the escape code, you'll see that the jail works as expected (even though the escape code claims otherwise):



inside# touch /inside_jail
inside# ./escape_chroot
Exploit seems to work. =)
inside# # ls -ld /inside_jail /old_root
-rw-r--r--. 1 0 0 0 Jan 5 23:45 /inside_jail
dr-xr-xr-x. 20 0 0 4096 Jul 5 23:45 /old_root


As you can see, still inside the jail... The exploit code is just a bit naive and assumes that as long as the operations (chroot, chdir) have succeeded, that was enough to escape the jail, which is not really the case...



So consider using this technique to create a jail that is superior to chroot and does not require using Linux capabilities to block operations inside it (such as creating additional chroots, which might actually be useful or required for what you're actually trying to run in a jail.)






share|improve this answer













To protect against the specific chdir("..") escape technique you mentioned, you can simply drop the capability to execute chroot(2) again once you're chrooted to /var/mychroot yourself. The escape technique requires another call to chroot(), so blocking that is enough to prevent it from working.



You can do that with Linux capabilities, by dropping CAP_SYS_CHROOT which is the one needed for chroot(2) to be available.



For example:



outside# chroot /var/mychroot
inside# capsh --drop=cap_sys_chroot --
inside# ./escape_chroot
chroot(baz): Operation not permitted


(The second prompt inside the chroot is from a shell spawned from capsh. You can make it run another command with, for example, capsh --drop=cap_sys_chroot -- -c 'exec ./escape_chroot'.)




But a much better technique is to just use pivot_root, since it protects from many of the other possible exploits that chroot(2) will not protect from.



You mentioned that it only works if /var/mychroot is a mount point, but you can make it a mount point by simply bind mounting it into itself.



Note that you need to create a mount namespace to use pivot_root to create a jail, otherwise it will try to change the root of all processes in your filesystem, which is most likely not what you want here...



So the whole sequence goes:



outside# unshare -m
outside# mount --bind /var/mychroot /var/mychroot
outside# cd /var/mychroot
outside# mkdir old_root
outside# pivot_root . old_root
limbo# exec chroot .
inside# umount /old_root


(Again, many of these commands are spawning new shells. unshare does that, so does chroot itself. You can work around those by passing commands as extra arguments. In some cases you might want to pass sh -c '...' for a full script.)



At this point, you're inside a pivot_root jail in a separate mount namespace, and the fact that /var/mychroot is simply a directory of the original root (and not a mount of a separate device or loop device) didn't really prevent this from working, thanks to the bind mount into itself.



Running the escape code, you'll see that the jail works as expected (even though the escape code claims otherwise):



inside# touch /inside_jail
inside# ./escape_chroot
Exploit seems to work. =)
inside# # ls -ld /inside_jail /old_root
-rw-r--r--. 1 0 0 0 Jan 5 23:45 /inside_jail
dr-xr-xr-x. 20 0 0 4096 Jul 5 23:45 /old_root


As you can see, still inside the jail... The exploit code is just a bit naive and assumes that as long as the operations (chroot, chdir) have succeeded, that was enough to escape the jail, which is not really the case...



So consider using this technique to create a jail that is superior to chroot and does not require using Linux capabilities to block operations inside it (such as creating additional chroots, which might actually be useful or required for what you're actually trying to run in a jail.)







share|improve this answer












share|improve this answer



share|improve this answer










answered Jan 5 at 7:51









filbrandenfilbranden

7,4002837




7,4002837







  • 1





    FYI I've found the command-line tool jchroot which automates this pivot_root(2) technique. I've verified on Linux 3.13 and 4.18 that the chdir("..") escape technique doesn't work with this pivot_root(2) technique as used by jchroot in github.com/vincentbernat/jchroot/blob/…

    – pts
    Jan 5 at 12:47












  • 1





    FYI I've found the command-line tool jchroot which automates this pivot_root(2) technique. I've verified on Linux 3.13 and 4.18 that the chdir("..") escape technique doesn't work with this pivot_root(2) technique as used by jchroot in github.com/vincentbernat/jchroot/blob/…

    – pts
    Jan 5 at 12:47







1




1





FYI I've found the command-line tool jchroot which automates this pivot_root(2) technique. I've verified on Linux 3.13 and 4.18 that the chdir("..") escape technique doesn't work with this pivot_root(2) technique as used by jchroot in github.com/vincentbernat/jchroot/blob/…

– pts
Jan 5 at 12:47





FYI I've found the command-line tool jchroot which automates this pivot_root(2) technique. I've verified on Linux 3.13 and 4.18 that the chdir("..") escape technique doesn't work with this pivot_root(2) technique as used by jchroot in github.com/vincentbernat/jchroot/blob/…

– pts
Jan 5 at 12:47













3















they only work if /var/mychroot is a mount point, so they fail if /var/mychroot is just a subdirectory within the / filesystem.




You can make any directory a mount point: mount --rbind /var/mychoot /var/mychoot.



This step - and others that you will need - are covered here:



How to perform chroot with Linux namespaces?




In fact I want to prevent all kinds of chroot escapes




Combine the above with user namespaces.



Alternatively, seccomp. This is how Docker prevents contained processes calling mount(), for example. (When you mount a block device a second time, you get a second view of that filesystem, which more or less counts as an escape). Copy Docker's list of allowed syscalls - root has lots of different ways of escaping.



https://docs.docker.com/engine/security/seccomp/



https://github.com/moby/moby/blob/master/profiles/seccomp/default.json






share|improve this answer




















  • 1





    Thank you for both the solution and the insightful links on hardening a containers and chroot environments. Unfortunately it's not possible to accept multiple answers, but you have my very keen upvote.

    – pts
    Jan 5 at 12:51
















3















they only work if /var/mychroot is a mount point, so they fail if /var/mychroot is just a subdirectory within the / filesystem.




You can make any directory a mount point: mount --rbind /var/mychoot /var/mychoot.



This step - and others that you will need - are covered here:



How to perform chroot with Linux namespaces?




In fact I want to prevent all kinds of chroot escapes




Combine the above with user namespaces.



Alternatively, seccomp. This is how Docker prevents contained processes calling mount(), for example. (When you mount a block device a second time, you get a second view of that filesystem, which more or less counts as an escape). Copy Docker's list of allowed syscalls - root has lots of different ways of escaping.



https://docs.docker.com/engine/security/seccomp/



https://github.com/moby/moby/blob/master/profiles/seccomp/default.json






share|improve this answer




















  • 1





    Thank you for both the solution and the insightful links on hardening a containers and chroot environments. Unfortunately it's not possible to accept multiple answers, but you have my very keen upvote.

    – pts
    Jan 5 at 12:51














3












3








3








they only work if /var/mychroot is a mount point, so they fail if /var/mychroot is just a subdirectory within the / filesystem.




You can make any directory a mount point: mount --rbind /var/mychoot /var/mychoot.



This step - and others that you will need - are covered here:



How to perform chroot with Linux namespaces?




In fact I want to prevent all kinds of chroot escapes




Combine the above with user namespaces.



Alternatively, seccomp. This is how Docker prevents contained processes calling mount(), for example. (When you mount a block device a second time, you get a second view of that filesystem, which more or less counts as an escape). Copy Docker's list of allowed syscalls - root has lots of different ways of escaping.



https://docs.docker.com/engine/security/seccomp/



https://github.com/moby/moby/blob/master/profiles/seccomp/default.json






share|improve this answer
















they only work if /var/mychroot is a mount point, so they fail if /var/mychroot is just a subdirectory within the / filesystem.




You can make any directory a mount point: mount --rbind /var/mychoot /var/mychoot.



This step - and others that you will need - are covered here:



How to perform chroot with Linux namespaces?




In fact I want to prevent all kinds of chroot escapes




Combine the above with user namespaces.



Alternatively, seccomp. This is how Docker prevents contained processes calling mount(), for example. (When you mount a block device a second time, you get a second view of that filesystem, which more or less counts as an escape). Copy Docker's list of allowed syscalls - root has lots of different ways of escaping.



https://docs.docker.com/engine/security/seccomp/



https://github.com/moby/moby/blob/master/profiles/seccomp/default.json







share|improve this answer














share|improve this answer



share|improve this answer








edited Jan 5 at 13:59

























answered Jan 5 at 10:14









sourcejedisourcejedi

23.4k437103




23.4k437103







  • 1





    Thank you for both the solution and the insightful links on hardening a containers and chroot environments. Unfortunately it's not possible to accept multiple answers, but you have my very keen upvote.

    – pts
    Jan 5 at 12:51













  • 1





    Thank you for both the solution and the insightful links on hardening a containers and chroot environments. Unfortunately it's not possible to accept multiple answers, but you have my very keen upvote.

    – pts
    Jan 5 at 12:51








1




1





Thank you for both the solution and the insightful links on hardening a containers and chroot environments. Unfortunately it's not possible to accept multiple answers, but you have my very keen upvote.

– pts
Jan 5 at 12:51






Thank you for both the solution and the insightful links on hardening a containers and chroot environments. Unfortunately it's not possible to accept multiple answers, but you have my very keen upvote.

– pts
Jan 5 at 12:51


















draft saved

draft discarded
















































Thanks for contributing an answer to Unix & Linux Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f492607%2fhow-to-create-a-chroot-from-which-root-cannot-escape-using-chdir-on-linux%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown






Popular posts from this blog

How to check contact read email or not when send email to Individual?

Displaying single band from multi-band raster using QGIS

How many registers does an x86_64 CPU actually have?