Skip to main content

A1 Injection

xkcd - Exploits of a Mom Image from xkcd

Definition​

Injection flaws, such as SQL, NoSQL, OS, and LDAP injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attacker’s hostile data can trick the interpreter into executing unintended commands or accessing data without proper authorization.

Risk Factor Summary​

  • Score: 8.0
  • Attack Vectors Exploitability: 3
  • Security Weakness Prevalence: 2
  • Security Weakness Detectability: 3
  • Impact Technical: 3

Context​

Almost any source of data can be an injection vector, environment variables, parameters, external and internal web services, and all types of users. Injection flaws occur when an attacker can send hostile data to an interpreter.

Injection flaws are very prevalent, particularly in legacy code. Injection vulnerabilities are often found in SQL, LDAP, XPath, or NoSQL queries, OS commands, XML parsers, SMTP headers, expression languages, and ORM queries.

Injection flaws are easy to discover when examining code. Scanners and fuzzers can help attackers find injection flaws.

Injection can result in data loss, corruption, or disclosure to unauthorized parties, loss of accountability, or denial of access.

Injection can sometimes lead to complete host takeover.

The business impact depends on the needs of the application and data.

Is the Application Vulnerable?​

An application is vulnerable to attack when:

  • User-supplied data is not validated, filtered, or sanitized by the application.
  • Dynamic queries or non-parameterized calls without contextaware escaping are used directly in the interpreter.
  • Hostile data is used within object-relational mapping (ORM) search parameters to extract additional, sensitive records.
  • Hostile data is directly used or concatenated, such that the SQL or command contains both structure and hostile data in dynamic queries, commands, or stored procedures. Some of the more common injections are SQL, NoSQL, OS command, Object Relational Mapping (ORM), LDAP, and Expression Language (EL) or Object Graph Navigation Library (OGNL) injection. The concept is identical among all interpreters. Source code review is the best method of detecting if applications are vulnerable to injections, closely followed by thorough automated testing of all parameters, headers, URL, cookies, JSON, SOAP, and XML data inputs. Organizations can include static source (SAST) and dynamic application test (DAST) tools into the CI/CD pipeline to identify newly introduced injection flaws prior to production deployment.

Conceptual Attacks​

Scenario #1:​

An application uses untrusted data in the construction of the following vulnerable SQL call:

const query = `SELECT * FROM accounts WHERE
custID='${request.getParameter('id')}'`

Scenario #2:​

Similarly, an application’s blind trust in frameworks may result in queries that are still vulnerable, (e.g. Hibernate Query Language (HQL)):

const HQLQuery = session.createQuery(`FROM accounts
WHERE custID='${request.getParameter('id')}'`)

In both cases, the attacker modifies the β€˜id’ parameter value in their browser to send: ' or '1'='1. For example:

http://example.com/app/accountView?id=' or '1'='1

This changes the meaning of both queries to return all the records from the accounts table, since '1'='1 will always evaluate TRUE. More dangerous attacks could modify or delete data, or even invoke stored procedures.

Example Attack Scenarios​

SQL Injection:​

This example is based on Scott Smith example about Injections

Our server code:

const { mysqlConfig } = require('./config)
const express = require('express');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const connection = mysql.createConnection(mysqlConfig);
const app = express();

//Middleware
app.use(bodyParser.urlencoded());

// Routes
app.post('/login', (req, res) => {
const { user, pass } = req.body;
const sql = `SELECT * FROM users WHERE user = '${user}' AND pass = '${pass}'`;
console.log(`SQL to be executed: ${sql}`)
// connection.query(sql, (err, results) => {})
});
app.listen(8080);

our malicious request:

POST /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded

user=admin'--&pass=pass

The final query is:

SELECT * FROM users WHERE user = 'admin'--' AND pass = 'pass'

The problem here is that "--" renders everything that follows as a comment, thus returning the user info regardless of the password.

In this particular case the mitigation can be done by using escape function built-it in mysql library.

//...
const connection = mysql.createConnection(mysqlConfig)
//..

app.post('/login', (req, res) => {
const user = connection.escape(req.body.user)
const pass = connection.escape(req.body.pass)

const sql = `SELECT * FROM users WHERE user = '${user}' AND pass = '${pass}'`
console.log(`SQL to be executed: ${sql}`)
// connection.query(sql, (err, results) => {})
})
//..

Using the previous malicious payload you get a safe query:

SELECT * FROM users WHERE user = 'admin''--' AND pass = 'pass'

NON-SQL Injection:​

This example is based on Scott Smith example about Injections

Our server code:

const MongoClient = require('mongodb').MongoClient;
const { dbName, url } = require('./config).mongoConfig;
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
let db;

// DB Connection
MongoClient.connect(url, (err, client) => {
if(err) throw err;
console.log("Connected successfully to MongoDB");
db = client.db(dbName);
app.listen(8080);
});

//Middleware
app.use(bodyParser.urlencoded());

// Routes
app.post('/login', (req, res) => {
const { user, pass } = req.body;
db.users.find({user: user, pass: pass});
});

our malicious request:

POST /login HTTP/1.1
Host: example.com
Content-Type: application/json

{
"user": {"$gt": ""},
"pass": {"$gt": ""}
}

The final query will return all the users within the user collection

db.users.find({ user: { $gt: '' }, pass: { $gt: '' } })

In this particular case the mitigation can be done by using an explicit query selector.

app.post('/login', (req, res) => {
const { user, pass } = req.body
db.users.find({ user: { $in: [user] }, pass: { $in: [pass] } })
})

Using the previous malicious payload, you get a safe query now:

db.users.find({user: { $in: [{ '$gt': '' }] }, pass: { $in: [{ '$gt': '' }] }});

OS Command Injection:​

In this case we want to use youtube-dl to download a cool youtube video to our local machine.

As youtube-dl is a Python library we will use process child to run it in Nodejs.

Note: This example is non very common, but is much better than the typical eval() example.

Our server code:

const { exec } = require('child_process')
const express = require('express')
const app = express()

// Routes
app.get('/download_video/:id', (req, res) => {
const { id } = req.params
const cmd = `youtube-dl https://www.youtube.com/watch\?v\=${id}`
exec(cmd, (error, stdout, stderr) => {
if (error || stderr)
return res
.status(500)
.send(`😱 The video with id:${id} was not downloaded!`)
res.send(`πŸŽ‰ Video with id:${id} was downloaded!. ${stdout}`)
})
})

app.listen(8080)

Our malicious request:

GET /download_video/KEkrWRHCDQU; printenv

This request will be translated to youtube-dl https://www.youtube.com/watch?v=KEkrWRHCDQU; printenv. So will include all the enviromental variables in the response.

To prevent this specific attack you can use a regex to validate the Youtube video id with a regex: /^[a-zA-Z0-9_-]{11}$/ and avoid to include the stdout or raw errors in the response.

Our server code:

//..
const idValidation = id => {
const re = /^[a-zA-Z0-9_-]{11}$/
return re.test(String(id))
}

// Routes
app.get('/download_video/:id', (req, res) => {
const { id } = req.params
if (!idValidation(id)) return res.status(400).send('You sent an invalid id!')
const cmd = `youtube-dl https://www.youtube.com/watch\?v\=${id}`
exec(cmd, (error, stdout, stderr) => {
if (error || stderr)
return res
.status(500)
.send(`😱 The video with id:${id} was not downloaded!`)
res.send(`πŸŽ‰ Video with id:${id} was downloaded!.`)
})
})
//..

Note: These kind of bugs in the design can easily lead to Backdoors. Check out this post for more details.

External content Injection:​

It is very common to use Content Delivery Networks (CDNs) to speed up our static content (css, js, etc...). If we are using a third party CDN we must use Subresource Integrity validation in our HTML in order to prevent the execution on malicious content in case that the CDN is compromised and the files replaced by malicious ones.

<script
src="https://example.com/example-framework.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
></script>

As you can see the integrity property contains the hashed value of the file that we're expecting. This will allow the browser to check the integrity of the files downloaded before the execution.

Note: Most of the CDN services already had included this features, please use them

How to Prevent​

General advice​

Preventing injection requires keeping data separate from commands and queries

  • The preferred option is to use a safe API, which avoids the use of the interpreter entirely or provides a parameterized interface, or migrate to use Object Relational Mapping Tools (ORMs). Note: Even when parameterized, stored procedures can still introduce SQL injection if PL/SQL or T-SQL concatenates queries and data, or executes hostile data with EXECUTE IMMEDIATE or exec().
  • Use positive or "whitelist" server-side input validation. This is not a complete defense as many applications require special characters, such as text areas or APIs for mobile applications.
  • For any residual dynamic queries, escape special characters using the specific escape syntax for that interpreter. Note: SQL structure such as table names, column names, and so on cannot be escaped, and thus user-supplied structure names are dangerous. This is a common issue in report-writing software.
  • Use LIMIT and other SQL controls within queries to prevent mass disclosure of records in case of SQL injection.

Keys to mitigate​

  • Validate and sanitize any user input
  • Strongly type your parameters
  • Use parameterized queries for databases
  • Configure your database account with the minimum permissions possible

Extra mile: Unexpected injections​

Car with an SQL injection in as a plate Image from backtrackacademy

You always must sanitize all the Inputs. No matter where they came from. Any non-controller input can be a very harmful.

Hacking Playground​

References​

OWASP​

CWEs​

Other​