kernel space `rm -rf /` blocker

satorutakeuchi

Satoru Takeuchi

Posted on December 4, 2019

kernel space `rm -rf /` blocker

Preface

This article introduces how to prevent rm -rf / disaster from kernel space. To understand this article, it's nice to have some experience of C-like programming language. If you understand what is pointer in C, it's better.

Background

From the beginning of UNIX, users tend to lose their data by rm -rf /. To avoid this disaster, rm in GNU coreutils can't remove "/" without setting --no-preserve-root option. However, users love to add --no-preserve-roo to rm by default.

To mitigate this problem, I created a kernel patch that causes kernel panic if users try to execute rm -rf /.

prepare kernel source

I confirmed that this kernel patch works correctly with Linux v5.3. The source code of this kernel can be got by the following commands.

$ git clone -b v5.3 --depth 1 https://github.com/torvalds/linux
...                    # Please note that it consumes several GB of storage.
$ git checkout v5.3
...
$ 
Enter fullscreen mode Exit fullscreen mode

Applying patch

Here is the patch, named 0001-panic-if-user-tries-to-run-rm-rf-root.patch, for my rm -rf / blocker feature. Its license is GPLv2.

From 27a3af9519c8b07c527bd48ef19b4baf9f6d4a9c Mon Sep 17 00:00:00 2001
From: Satoru Takeuchi <satoru.takeuchi@gmail.com>
Date: Sun, 6 Oct 2019 15:53:34 +0000
Subject: [PATCH] panic if user tries to run rm -rf /

---
 fs/exec.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/fs/exec.c b/fs/exec.c
index f7f6a140856a..8d2c1441b64c 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1816,6 +1816,43 @@ static int __do_execve_file(int fd, struct filename *filename,
        if (retval < 0)
                goto out;

+       // Panic if user tries to execute `rm -rf /`
+       if (bprm->argc >= 3) {
+               struct page *page;
+               char *kaddr;
+               char rm_rf_root_str[] = "rm\0-rf\0/";
+               char buf[sizeof(rm_rf_root_str)];
+               int bytes_to_copy;
+               unsigned long offset;
+
+               bytes_to_copy = min(sizeof(rm_rf_root_str), bprm->p % PAGE_SIZE);
+               page = get_arg_page(bprm, bprm->p, 0);
+               if (!page) {
+                       retval = -EFAULT;
+                       goto out;
+               }
+               kaddr = kmap(page);
+               offset = bprm->p % PAGE_SIZE;
+               memcpy(buf, kaddr + offset, bytes_to_copy);
+               kunmap(page);
+               put_arg_page(page);
+
+               if (bytes_to_copy < sizeof(rm_rf_root_str)) {
+                       page = get_arg_page(bprm, bprm->p + bytes_to_copy, 0);
+                       if (!page) {
+                               retval = -EFAULT;
+                               goto out;
+                       }
+                       kaddr = kmap(page);
+                       memcpy(buf + bytes_to_copy, kaddr, sizeof(rm_rf_root_str) - bytes_to_copy);
+                       kunmap(page);
+                       put_arg_page(page);
+               }
+
+               if (!memcmp(rm_rf_root_str, buf, sizeof(rm_rf_root_str)))
+                       panic("`rm -rf /` is detected");
+       }
+
        would_dump(bprm, bprm->file);

        retval = exec_binprm(bprm);
--
2.17.1
Enter fullscreen mode Exit fullscreen mode

You can apply this patch by the following command.

$ git apply 0001-panic-if-user-tries-to-run-rm-rf-root.patch
Enter fullscreen mode Exit fullscreen mode

Build and run the custom kernel

NOTE: Please run the custom kernel in a machine that can be broken, for example, a virtual machine that is just for this experiment. Otherwise, you might lose your data due to filesystem corruption.

First, run the following commands.

$ sudo apt install kernel-package flex bison libssl-dev
...                                                 # install packages to build the Linux kernel
$ make localmodconfig
...                                                 # make a configuration file, if you are asked something, just press enter.
$ make -j$(grep -c processor /proc/cpuinfo)
...                                                 # build the custom Linux kernel
$ sudo make modules_install && make install
...                                                 # install the custom Linux kernel
$ sudo /sbin/reboot # configure GRUB to use the custom Linux kernel and reboot your system
Enter fullscreen mode Exit fullscreen mode

Second, confirm the kernel version ends with "+" after rebooting your system. It means your system uses the custom kernel.

$ uname -r
5.3.0+
$ 
Enter fullscreen mode Exit fullscreen mode

Last, please run the following command.

$ rm -rf /
Enter fullscreen mode Exit fullscreen mode

If you can't access your system after that, it means the custom kernel works fine. If you run rm -rf / from the console that shows kernel message, you can get the following kernel panic log.

Alt Text

After confirming the effect of this feature, please reboot this system, reconfigure your GRUB to boot the original kernel, and uninstall the custom kernel.

Conclusion

The patch for this feature is not so difficult. I guess you can understand what is done in this patch to a certain degree. So, please modify this patch a bit, and implement your own feature if you like.

Enjoy kernel hacking!

💖 💪 🙅 🚩
satorutakeuchi
Satoru Takeuchi

Posted on December 4, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

kernel space `rm -rf /` blocker
linux kernel space `rm -rf /` blocker

December 4, 2019