The “rm -rf” Command in Linux

Just for fun, I decided to boot a Linux server and run“rm –rf /”as root to see what would survive. The result? Nothing was deleted! So you must add –no-preserve-root and try again:

  1. # rm -rf –no-preserve-root /

When you press“Enter”some important tools like

  • /bin/ls
  • /bin/cat
  • /bin/chmod
  • /usr/bin/file

will vanish instantly! But your SSH connection and bash terminal remain, showing that bash built-ins like echo are unaffected.

Become a Bash Expert

  1. root@rmrf:/# ls
  2. bash: /bin/ls: No such file or directory

After the above command, ls is gone, but echo and fileglobs remain. Using these“survivors”, what can we do?

  1. root@rmrf:/# echo *
  2. dev proc run sys
  3. # echo /dev/pts/*
  4. /dev/pts/0 /dev/pts/3 /dev/pts/ptmx

Note: /dev, /proc, /run, /sys are still there. We must preserve them. Having ls would make directory reading easier.

  1. root@rmrf:/# for file in /dev/pts/*; do echo $file; done
  2. /dev/pts/0
  3. /dev/pts/3
  4. /dev/pts/ptmx

Many Reddit users noted printf still works. CAMH- said printf formats arguments into output strings.

  1. root@rmrf:/# ls() { printf ‘%s/n’ ${1:+${1%/}/}*; }

We can define bash functions to build a simple ls tool.

  1. root@rmrf:/# ls() { printf ‘%s/n’ ${1:+${1%/}/}*; }
  2. bash: syntax error near unexpected token `(‘

This should be perfectly valid. Is ls aliased or mapped to another command?

  1. root@rmrf:/# type ls
  2. ls is aliased to `ls –color=auto’

I see, our command was expanded. We can use unalias to remove the ls alias:

  1. root@rmrf:/# unalias ls
  2. root@rmrf:/# ls() { printf ‘%s/n’ ${1:+${1%/}/}*; }
  3. root@rmrf:/# ls
  4. /dev
  5. /proc
  6. /run
  7. /sys
  8. root@rmrf:/# ls /dev
  9. /dev/pts

Save the functions to utils.sh:

  1. root@rmrf:/# echo ‘ls() { printf ‘%s/n’ ${1:+${1%/}/}*; }‘ >> utils.sh
  2. root@rmrf:/# source utils.sh

What about cat? Use read! read is a survivor. Combine it with pipes and redirection to make a basic cat:

  1. root@rmrf:/# (while read line; do echo “$line”; done) < utils.sh
  2. ls() { printf ‘%s/n’ ${1:+${1%/}/}*; }

Combining the above approach of restoring commands through survivors and echo’s ability to write arbitrary bytes, we can rebuild Linux tools and use curl or wget to get binaries. First, refer toechoed by others, getbusybox. Busybox is the Swiss Army knife of embedded Linux, including wget, dd, tar, and many tools.Eusebeîa detailed how to get an escaped version of busybox, I will not elaborate here.

But there is one more problem.

Even if we echo all bytes of the binary, they cannot execute. The early solution was to find executable programs and overwrite them with echo in /usr and /bin. This approach worked but was rather complex.

Use shell wildcards and bash to filter files with executable bits, excluding directories.

  1. executable () { if [[ ( ! d $1 ) && x $1 ]] ; then echo “$1”; fi }

Found executable files!

  1. root@rmrf:/# for file in /*; do executable $file; done
  2. root@rmrf:/# for file in /*/*; do executable $file; done
  3. root@rmrf:/# for file in /*/*/*; do executable $file; done
  4. /proc/1107/exe
  5. /proc/1136/exe
  6. /proc/1149/exe
  7. /proc/1179/exe
  8. /proc/1215/exe
  9. /proc/1217/exe
  10. /proc/1220/exe
  11. /proc/1221/exe
  12. /proc/1223/exe
  13. /proc/1248/exe
  14. /proc/1277/exe
  15. /proc/1468/exe
  16. /proc/1478/exe
  17. /proc/1625/exe
  18. /proc/1644/exe
  19. /proc/1/exe
  20. /proc/374/exe
  21. /proc/378/exe
  22. /proc/471/exe
  23. /proc/616/exe
  24. /proc/657/exe
  25. /proc/self/exe

Great! But these are symlinks whose originals no longer exist on disk. We need to rewrite executable() to exclude symlinks.

  1. root@rmrf:/# executable () { if [[ ( ! d $1 ) && ( ! h $1 ) && x $1 ]] ; then echo “$1”; fi }
  2. root@rmrf:/# for file in /*/*/*; do executable $file; done
  3. root@rmrf:/# for file in /*/*/*/*; do executable $file; done
  4. root@rmrf:/# for file in /*/*/*/*/*; do executable $file; done
  5. root@rmrf:/# for file in /*/*/*/*/*/*; do executable $file; done

Bad news, no output. Maybe use kernel-level tricks like Magic Sysrq to restart busybox.

  1. root@rmrf:/# echo 1 > /proc/sys/kernel/sysrq
  2. root@rmrf:/# echo “b” > /proc/sysrqtrigger

UPDATE: Reddit user throw_away5046 proposed a solution:a full solution to this

Get a trusted Busybox for the architecture

  1. $ mkdir $(xxd p l 16 /dev/urandom)
  2. $ cd $_
  3. $ aptget download busyboxstatic
  4. $ dpkg x *.deb .
  5. $ alias encode=‘{ tr -d //n | sed “s#//(..//)#////x//1#g”; echo; }’
  6. $ alias upload=‘{ xxd -p | encode | nc -q0 -lp 5050; }’
  7. $ upload < bin/busybox

The machine after running rm -rf

  1. # cd /
  2. # alias decode=’while read -ru9 line; do printf “$line”; done’
  3. # alias download='( exec 9<>/dev/tcp/{IP OF NON HOSED BOX}/5050; decode )’
  4. # download > busybox

Create an object to change busybox permissions

  1. $ cat > setx.c <<EOF
  2. extern int chmod(const char *pathname, unsigned int mode);
  3.  
  4. int entry(void) {
  5.  
  6. return !! chmod(“busybox”, 0700);
  7. }
  8. char *desc[] = {0};
  9.  
  10. struct quick_hack {
  11.  
  12. char *name; int (*fn)(void); int on;
  13. char **long_doc, *short_doc, *other;
  14.  
  15. } setx_struct = { “setx”, entry, 1, desc, “chmod 0700 busybox”, 0 };
  16. EOF
  17. $ gcc Wall Wextra pedantic nostdlib Os fpic shared setx.c o setx
  18. $ upload < setx

Enable setx as a built-in to make busybox executable

  1. # ( download > setx; enable -f ./setx setx; setx; )
  2. # /busybox mkdir .bin
  3. # /busybox –install -s .bin
  4. # PATH=/.bin

The steps are as follows:

final.gif

Original link: rm -rf remains   Translation: Jobbole – honpey
Translated article: http://blog.jobbole.com/70971/

Leave a Comment

Your email address will not be published.