Back

Please Stop Giving Me Your Passwords

I found myself in the office on Saturday night, mainly because the frozen pizza selection is more expansive than mine at home, and I wanted to get a head start on my project for this week. It was a normal Static Application Security Test (SAST), which follows a mostly pre-defined process, with embellishments depending on the languages and frameworks at play.

The first step in that process is to search for hardcoded passwords. I dug out the simple and ugly password regex I’ve created for this and did a search for /pa?ssw?o?r?d?|pwd/gi across the entire codebase. This regex covers all of the standard ways I’ve seen “password” used as a variable name in code. Without fail I got back:

Passwordregex

After digging through all the results and parsing out the false positives I ended up with a total of 30 hardcoded passwords. All of them were database connection strings spread across 20 total users, including multiple users with admin access to the database. Our recommendation for this is simple:

Passwords should never be hardcoded in the source code.

Why?

The reasoning behind this is that there are multiple attack paths that result in source code/arbitrary file disclosure. Error messages, public Github repos, arbitrary file read, “oopsies” email attachments, and shoulder surfing being just a few.

A typical escalation path that exploits hardcoded passwords could start with an XML External Entity (XXE) Injection. An application that is vulnerable to XXE will allow us to read (almost) any file on the server. Through this an attacker will fingerprint the technology at play and target the important source code files.  For example, a web application using the Python Django framework will contain a settings.py file. This file will sometimes contain hardcoded passwords for the DB connection. With some luck/bruteforce an attacker can find the source code directory and read the settings.py file via XXE.

HTTP Request:

POST /xxe HTTP/1.1
Host: netspi.com
Connection: close

<?xml version="1.0"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY settings SYSTEM "file:///django-app/settings.py" >]>
<foo>&settings;</foo>

HTTP Response:

HTTP/1.1 200 OK
Content-Length: 178
Connection: close

DATABASES = {
 'default': {
 'ENGINE': 'django.db.backends.mysql',
 'NAME': 'admin',
 'USER': 'admin',
 'PASSWORD': 'password',
 'HOST': 'prod-db.netspi.com',
 'PORT': '3306',
 }
}

An attacker can now take this information and connect directly to the database, which is public-facing because someone assumed authentication was enough. Once an attacker has this amount of access, any number of paths can be followed to further infiltrate the network. This attack extends to anything that requires a password: admin pages, config pages, mail servers, etc…

Remediation

We need various forms of secrets (passwords, api keys, etc…) on the box somehow and the goal is to find the method that minimizes the organization’s risk to the greatest degree. Our suggested remediation regularly has the goal of assigning them through environmental variables on the server. Environmental variables are the recommended method due to the low likelihood of an attacker gaining access to them. I’ve only seen them exploited in two common scenarios, overly-verbose debug pages left running in prod, or using OS command execution to list all of the environmental variables*. Both of which are easily combatable in large code repositories. Environmental variables also allow a more scalable solution, as rotating secrets will only require one configuration change.

Resources

Like most things in security, except 0days, all the information is out there. A lot of people just aren’t aware of the vulnerability or what the proper way to fix it is. Because of that I won’t rehash how every platform implements environmental variables, but I will identify what I think are the best resources for doing so.

The handling of sensitive data for an app should always be done at the deployment/orchestration level. This ensures that secrets are stored and managed away from the web servers and databases. Here are some of the popular deployment and orchestration frameworks, with their related resources:

Kubernetes

Jenkins

TravisCI

Drone

TeamCity

CircleCI

GitlabCI

AWS

  • This uses IAM roles, which are not discussed in this blog, but are a stronger substitute for environmental variables in AWS applications.

Docker Swarm

  • This is an interesting method using Docker Secrets. Unfortunately they are stored in files, but are the recommended method inside of the Docker ecosystem.

In the end, secrets don’t belong in the code. Proper distribution will decrease the reach an attacker has through other methods of attack and protect an organization by allowing them to rotate secrets easily and often.

Coming Up

This blog is an attempt at describing a holistic solution to secret handling that will work in every environment. Using environmental variables is still not the most secure method, as plaintext sensitive information is available to every user on the box. To combat this a platform-specific method is usually required, which Windows and Linux both offer.

We’re curious to hear how other organizations handle secrets in their code and what improvements could be made to further advance the topic. Let us know @NetSPI or by leaving a comment below!

* Edit 10:13 AM: Unfortunately environmental variables on linux are still vulnerable to arbitrary file disclosure as seen here https://blog.netspi.com/directory-traversal-file-inclusion-proc-file-system/.

Discover how the NetSPI BAS solution helps organizations validate the efficacy of existing security controls and understand their Security Posture and Readiness.

X