13 Jul

Planet Zuda and OptinMonster Handle Poor Security of OptinMonster API Plugin Extremely Badly

Through the various things we do as part our service we have a fair amount of interaction with people making claims about vulnerabilities in WordPress plugins and with the developers of WordPress plugins with vulnerabilities. From doing that we don’t currently have a very positive view of either side, which shouldn’t be all that surprising considering the poor state of security of WordPress plugins and security in general. As example of what that looks like let’s take a look at a recent claim by Planet Zuda of a vulnerability in the plugin OptinMonster API, which we found pointed to poor security in the plugin, but not the vulnerability claimed.

Before we get to the details of what is actually going on with the plugin, let’s take a look at how Planet Zuda responded to it:

This is a severe vulnerability leaving the admin side of your site vulnerable, so we have written a patch for this one vulnerability in the WordPress plugin Optinmonster, also known as Optinmonster API. You  can buy the more secure version of the WordPress optinmonster plugin [https://planetzuda.com/product/wordpress-plugin-optinmonster-aka-optinmonster-api-security-fix/] on our site. We may patch more vulnerabilities in this plugin, but we can say that our version of WordPress plugin optinmonster is currently more secure then what is available and we can continue to update it to be more secure.

Based on that it sounded like they hadn’t made any attempt to contact the developer, which is bad. Instead they are trying to sell people a version they claim is secured. Depending on what exactly they are selling they could be violating multiple laws, as the code in the plugin is GPL’d, but other parts could be copyrighted or trademarked. On Tuesday they were charging 9.99 for their version of the plugin and by Wednesday it was 19.99.

Now let’s get the security issue with the plugin. Here is how they started their explanation:

The WordPress plugin Optinmonster API has left over 500 thousand sites vulnerable to a security flaw. Thankfully we have a fix [https://planetzuda.com/product/wordpress-plugin-optinmonster-aka-optinmonster-api-security-fix/] you can buy for this one vulnerability.

During our code review we discovered that the admin area is protected by a WordPress function called is_admin, which is a misunderstood function, that allows admins and unauthenticated users alike to access the information in optinmonster API. What does this mean to you, the user? It means that you can access the admin features, but so can criminals.

The use of is_admin occurs in the function init() in the file /optin-monster-wp-api.php:

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
public function init() {
 
	// Define necessary plugin constants.
	define( 'OPTINMONSTER_API', '//a.optnmstr.com/app/js/api.min.js' );
 
	// Load our global option.
	$this->load_option();
 
	// Load global components.
	$this->load_global();
 
	// Load admin only components.
	if ( is_admin() ) {
		$this->load_admin();
	}
 
	// Run hook once OptinMonster has been fully loaded.
	do_action( 'optin_monster_api_loaded' );
 
}

Not surprisingly that runs during init, so whenever WordPress is loading.

In looking at the code we are not sure that use of is_admin() is due to not understanding what it does, but maybe instead the way the developer decided to handle things is not really appropriate.

What does optinmonster protect using is_admin? At first glance it is just one line of code, so how bad could that actually be? It turns out that one line of code as you can tell is calling almost all of the rest of the code in the application.

Yes, the one line of code load_admin, loads everything that should be administrator only, but instead is accessible for all.

Here is the function load_admin() that is run if is_admin() is true:

201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
public function load_admin() {
 
	// Register admin components.
	$this->actions  = new OMAPI_Actions();
	$this->menu     = new OMAPI_Menu();
	$this->content  = new OMAPI_Content();
	$this->save     = new OMAPI_Save();
	$this->refresh  = new OMAPI_Refresh();
	$this->validate = new OMAPI_Validate();
	$this->welcome  = new OMAPI_Welcome();
	$this->review   = new OMAPI_Review();
 
	// Fire a hook to say that the admin classes are loaded.
	do_action( 'optin_monster_api_admin_loaded' );
 
}

As you can tell above, the administrator menu, admin actions, reviews the admin has access to, the welcome page, the site content and the ability to save content among other things are  accessible to anyone or any bot who logs onto the site.

The code shown above (the same code is shown in Planet Zuda’s post) doesn’t actually show what they are saying. It only shows that new instances of a number of classes are created. To show that those things were accessible you would then go on to show an example what happens with those new classes, but Planet Zuda doesn’t. The next piece of text in their post is what we quoted before we started discussing the details of security issue.

In looking at this the first thing that caught our eye was the class OMAPI_Save. If anyone can change the plugin’s settings that has the possibility of allowing for a number of vulnerabilities beyond just being able to change the plugin’s settings. That class is defined in the file /OMAPI/Save.php.

The construct function for that class will the cause the function maybe_save() to run:

53
54
55
56
57
58
59
60
61
public function __construct() {
 
	// Set our object.
	$this->set();
 
	// Possibly save settings.
	$this->maybe_save();
 
}

That function checks for a valid nonce, which protects against cross-site request forgery (CSRF), before saving the settings:

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public function maybe_save() {
 
	// If we are missing our save action, return early.
	if ( empty( $_POST['omapi_save'] ) ) {
		return;
	}
 
	// If the subkey is empty, return early.
	if ( empty( $_POST['omapi'][ $this->view ] ) ) {
		return;
	}
 
	// Verify the nonce field.
	check_admin_referer( 'omapi_nonce_' . $this->view, 'omapi_nonce_' . $this->view );
 
	// Save the settings.
	$this->save();
 
	// Provide action to save settings.
	do_action( 'optin_monster_api_save_settings', $this->view );
 
}

That nonce is only accessible on an admin page that is limited to those with the manage_option capability, which is normally only Administrators. So while the code to save the plugin’s setting is available to anyone it doesn’t mean that anyone can do that. For things to be properly secured there really should be a check to make sure that the person making the request is permitted to do that using a capabilities check. While the nonce does double duty in this instance, it should not be relied on in that way.

In looking over the classes we didn’t see anything that could be considered a vulnerability, but there other parts that don’t contain any restriction on who can run their functionality.

After looking over this we sent the following message to the developer of the plugin, OptinMonster:

A report of a security vulnerability has been released for your WordPress plugin: https://planetzuda.com/wordpress-plugin-optinmonster-popups-500-thousand-sites-allows-unauthenticated-users-admin-backend/2017/07/11/

In looking at the report it seems that the report is misleading at best, but there does seem to be room for improvement at least. It isn’t clear if your use of is_admin() is what you intended and, for example, you don’t do a capabilities check when saving the plugin’s options, which you should do in addition to checking for protection against CSRF.

There is no mention in that post of the discloser having had made any attempt to contact you about the issue, which they should have. We would appreciate if you let us know if they did or did not contact you, because if they didn’t, we would like to let people know that they are not handling things properly.

If you have any questions or need help in dealing with this issue, please feel free to get back to us.

Less than half an hour later we got this response:

The report was false and misleading – there are no security vulnerabilities. It is libelous and we have sent a Cease and Desist letter because they are using our trademarks without our permission in a defamatory way.

They did not attempt to contact us but rather went straight to our customers. It is obviously done as a scare and smear tactic to try and persuade visitors to purchase their update.

We check for capabilities plus CSRF. If you look in the code, it is there.

The response to what Planet Zuda had done seemed appropriate, but the seeming lack of concern about the poor security of the plugin was troubling. The claim that they check capabilities isn’t true, we looked at the code before we contacted them. At best they don’t actually know what they are talking about. A worse explanation is they thought they could get away with lying about this.

At that point we doubled check the code and after not seeing a capabilities check we sent the follow response:

We did look at the code for saving the plugin’s options and we didn’t see any capabilities check when saving them, where in the code does that occur?

A day later we haven’t heard anything back, which if it was there really was a capabilities check, they shouldn’t have had a problem getting back to us with where it is.

The developers seeming lack of interest in an issue raised about the security of the plugin is unfortunately an all to common occurrence.

Leave a Reply

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