8 Jan 2024

WordPress Hasn’t Provided Fix for Severe Vulnerability Being Exploited in the Frontend Admin Plugin

According to WordPress’ security page, their security team can provide fixes for severe vulnerabilities in WordPress plugins. When they would do that is almost entirely opaque, as they say “if the vulnerability is severe, the plugin/theme is pulled from the public directory, and in some cases, fixed and updated directly by the Security Team.” We keep running into situations where that isn’t happening, when it should. The latest incident involves an arbitrary file upload vulnerability in the plugin Frontend Admin that was publicly, but vaguely, claimed to have existed on December 27. It took until January 4 for the plugin to be closed on the WordPress Plugin Directory. No update has been provided, despite the ease of providing a fix, as we will show. We have offered for years to provide fixes to WordPress in situations like this, without them taking up the offer.

Despite the already public claim it contained a serious vulnerability, WordPress isn’t warning that the plugin is vulnerable, instead only saying on the listing for the plugin that “This closure is temporary, pending a full review.”:

Websites already using it, are not receiving even a warning it has been closed.

The head of WordPress also heads a for-profit company, which owns a plugin vulnerability data provider, WPScan. Creating a rather obvious potential for a conflict of interest here. Made worse, by the problematic, to say the least, handling of disclosure of the connection between WordPress and the team running the Plugin Directory.

Hackers have clearly caught on to this vulnerability, as we had a hacker probing to see if the vulnerable code existed on our website yesterday, with a request to this address on our website:

/wp-admin/admin-ajax.php?action=acf/fields/upload_file/add_attachment

Vulnerable Code

That request is trying to call an AJAX accessible function through WordPress that is registered to be accessed with the action “acf/fields/upload_file/add_attachment”. Frontend Admin registers the function ajax_add_attachment() in the file /main/frontend/fields/general/class-upload-file.php to be accessed that way:

44
45
add_action( 'wp_ajax_acf/fields/upload_file/add_attachment', array( $this, 'ajax_add_attachment' ) );
add_action( 'wp_ajax_nopriv_acf/fields/upload_file/add_attachment', array( $this, 'ajax_add_attachment' ) );

That function allows uploading arbitrary files to the website. An attacker could, for example, upload a file with a .php extension and then run arbitrary code on the website.

There is one important limitation to note: the code does check for a valid nonce to prevent cross-site request forgery (CSRF):

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
function ajax_add_attachment() {
	$args = acf_parse_args(
		$_POST,
		array(
			'field_key' => '',
			'nonce'     => '',
		)
	);
 
	// validate nonce
	if ( ! acf_verify_ajax() ) {
		wp_send_json_error( __( 'Invalid Nonce', 'acf-frontend-form-element' ) );
	}
 
	// bail early if no attachments
	if ( empty( $_FILES['file']['name'] ) ) {
		wp_send_json_error( __( 'Missing file name', 'acf-frontend-form-element' ) );
	}
 
	// TO dos: validate file types, sizes, and dimensions
	// Add loading bar for each image
 
	if ( isset( $args['field_key'] ) ) {
		$field = get_field_object( $args['field_key'] );
	} else {
		wp_send_json_error( __( 'Invalid Key', 'acf-frontend-form-element' ) );
	}
 
	$file = $_FILES['file'];
 
	// get errors
	$errors = acf_validate_attachment( $file, $field, 'upload' );
 
	// append error
	if ( ! empty( $errors ) ) {
		$data = implode( "\n", $errors );
		wp_send_json_error( $data );
	}
 
	$wp_filetype = wp_check_filetype( basename( $file['name'] ), null );
 
	$wp_upload_dir = wp_upload_dir();
 
	$submissions_dir = $this->maybe_mkdir( $wp_upload_dir['basedir'] . '/fea-submissions', true );	
	$url = $this->upload_file($submissions_dir,$file['name'], true);			
	move_uploaded_file( $file['tmp_name'], $url );

Based on what we found, the needed nonce value may not always be easily accessible, but it would be a bad idea to assume that hackers could not easily find it.

The uploaded file is given a partially randomized name, and the name is not returned when the file is uploaded, so the hacker would need additional information to access the file. But the file name can normally be accessed by making an additional request to the website. Because of poor coding by the developer, there is also a full path disclosure vulnerability that occurs when combining the file upload vulnerability and the additional request.

Quick Fix

The vulnerable code already includes code to check if the file being uploaded is of a type normally allowed by WordPress. The problem is the result of that isn’t used to restrict what files can be uploaded. This is the relevant line of code:

336
$wp_filetype = wp_check_filetype( basename( $file['name'] ), null );

The function wp_check_filetype() used there returns the file extension and file type of the file, if the file is of a type allowed by WordPress. Otherwise, the values are empty. By exiting if the file extension is empty, the file upload won’t happen. That can be done like this (the WordPress function wp_upload_bits() has similar code to accomplish the same thing):

if ( ! $wp_filetype['ext'] )
	exit();

So adding two lines would be all WordPress would need to address this serious vulnerability. The same was true in other instance of an unfixed exploited in October (a situation that continues to be improperly properly resolved).

Leave a Reply

Your email address will not be published. Required fields are marked *