
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:
- # 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
- root@rmrf:/# ls
- –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?
- root@rmrf:/# echo *
- dev proc run sys
- # echo /dev/pts/*
- /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.
- root@rmrf:/# for file in /dev/pts/*; do echo $file; done
- /dev/pts/0
- /dev/pts/3
- /dev/pts/ptmx
Many Reddit users noted printf still works. CAMH- said printf formats arguments into output strings.
- root@rmrf:/# ls() { printf ‘%s/n’ ${1:+${1%/}/}*; }
We can define bash functions to build a simple ls tool.
- root@rmrf:/# ls() { printf ‘%s/n’ ${1:+${1%/}/}*; }
- –bash: syntax error near unexpected token `(‘
This should be perfectly valid. Is ls aliased or mapped to another command?
- root@rmrf:/# type ls
- ls is aliased to `ls –color=auto’
I see, our command was expanded. We can use unalias to remove the ls alias:
- root@rmrf:/# unalias ls
- root@rmrf:/# ls() { printf ‘%s/n’ ${1:+${1%/}/}*; }
- root@rmrf:/# ls
- /dev
- /proc
- /run
- /sys
- root@rmrf:/# ls /dev
- /dev/pts
Save the functions to utils.sh:
- root@rmrf:/# echo ‘ls() { printf ‘%s/n’ ${1:+${1%/}/}*; }‘ >> utils.sh
- 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:
- root@rmrf:/# (while read line; do echo “$line”; done) < utils.sh
- 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.
- executable () { if [[ ( ! –d $1 ) && –x $1 ]] ; then echo “$1”; fi }
Found executable files!
- root@rmrf:/# for file in /*; do executable $file; done
- root@rmrf:/# for file in /*/*; do executable $file; done
- root@rmrf:/# for file in /*/*/*; do executable $file; done
- /proc/1107/exe
- /proc/1136/exe
- /proc/1149/exe
- /proc/1179/exe
- /proc/1215/exe
- /proc/1217/exe
- /proc/1220/exe
- /proc/1221/exe
- /proc/1223/exe
- /proc/1248/exe
- /proc/1277/exe
- /proc/1468/exe
- /proc/1478/exe
- /proc/1625/exe
- /proc/1644/exe
- /proc/1/exe
- /proc/374/exe
- /proc/378/exe
- /proc/471/exe
- /proc/616/exe
- /proc/657/exe
- /proc/self/exe
Great! But these are symlinks whose originals no longer exist on disk. We need to rewrite executable() to exclude symlinks.
- root@rmrf:/# executable () { if [[ ( ! –d $1 ) && ( ! –h $1 ) && –x $1 ]] ; then echo “$1”; fi }
- root@rmrf:/# for file in /*/*/*; do executable $file; done
- root@rmrf:/# for file in /*/*/*/*; do executable $file; done
- root@rmrf:/# for file in /*/*/*/*/*; do executable $file; done
- root@rmrf:/# for file in /*/*/*/*/*/*; do executable $file; done
Bad news, no output. Maybe use kernel-level tricks like Magic Sysrq to restart busybox.
- root@rmrf:/# echo 1 > /proc/sys/kernel/sysrq
- root@rmrf:/# echo “b” > /proc/sysrq–trigger
UPDATE: Reddit user throw_away5046 proposed a solution:a full solution to this。
Get a trusted Busybox for the architecture
- $ mkdir $(xxd –p –l 16 /dev/urandom)
- $ cd $_
- $ apt–get download busybox–static
- $ dpkg –x *.deb .
- $ alias encode=‘{ tr -d //n | sed “s#//(..//)#////x//1#g”; echo; }’
- $ alias upload=‘{ xxd -p | encode | nc -q0 -lp 5050; }’
- $ upload < bin/busybox
The machine after running rm -rf
- # cd /
- # alias decode=’while read -ru9 line; do printf “$line”; done’
- # alias download='( exec 9<>/dev/tcp/{IP OF NON HOSED BOX}/5050; decode )’
- # download > busybox
Create an object to change busybox permissions
- $ cat > setx.c <<EOF
- extern int chmod(const char *pathname, unsigned int mode);
- int entry(void) {
- return !! chmod(“busybox”, 0700);
- }
- char *desc[] = {0};
- struct quick_hack {
- char *name; int (*fn)(void); int on;
- char **long_doc, *short_doc, *other;
- } setx_struct = { “setx”, entry, 1, desc, “chmod 0700 busybox”, 0 };
- EOF
- $ gcc –Wall –Wextra –pedantic –nostdlib –Os –fpic –shared setx.c –o setx
- $ upload < setx
Enable setx as a built-in to make busybox executable
- # ( download > setx; enable -f ./setx setx; setx; )
- # /busybox mkdir .bin
- # /busybox –install -s .bin
- # PATH=/.bin
The steps are as follows:

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