All Versions Critical Vulnerability

Thanks for this info. TBH if there was a paid-for upgrade on major versions like software used to be, I’d be all for that. But purchasing software, regardless of the in-place model, should include support, useful documentation, and improvements to keep up with changing technology. If @clavaque has things going on, that’s fine but it’s so quiet without any updates after weeks that is concerning.

The website is in desperate need of an update. The help articles are very out of date and many have not been updated in over a decade. Getting modern forms to use on our sites seems like a pipe dream. When a CRITICAL security issue comes up, there should be some form of acknowledgment and fix that’s either in the works or ready to go.

The fact that we all have to rely on this forum for everything and HOPE it solves our problems is… asking a lot.

If the business model has to change to make it more sustainable, then he can do that. But that doesn’t mean abandoning everyone here who paid for what is still being sold and we are on our own.

So… Did you find a better product or are you willing to build one with everything you say for what you ask, from scratch, to compete with it? Let us know because we might become your customers. :grin:

People love to complain.

I have personal difficulties and deal with health issues, amongst other things.

But, otherwise, if I were going to offer what you want, it would surely not be cheap.

This is not supposed to be a good “professional grade” product. It’s just something good enough when you run a small community.

I don’t think it’s fair to treat a single developer that’s clearly struggling as if he were a massive evil corporation enshitifying things.

I am also a user. I also wish for something better. I chose this product because it’s an “indie” solution that might lack here and there but that won’t rip me off for money or data.

As I mentioned, let’s give him some time. If he actually abandons the project, then we can think about the best next step.

People complain about subscriptions but without some sort of revenue isn’t sustainable. You get thousands of websites using your product with zero income, then an odd new customer per week or not even that.

You use this plugin because you likely also collect recurring subscriptions as you are aware you’d not be able to even fund your hosting expenses, let alone producing new content.

I am not defending a hefty subscription but things like the unlimited websites for a single license have to go. The least we can do is to pay per additional domain.

I didn’t check the forum stats yet (not sure if they’re visible to me, I’ll take a look later) but if we have a thousand users here paying at least 2 euros per month, on average, he’d get at least 1500 after fees that could at least mean he’d be able to spend a few hours per week working on very basic security updates, nothing else.

If we expect more, whatever new he comes up with should require extra.

For comparison, OneDrive broke shared folders almost an entire year ago. There’s thousands of complaints on an single thread. They’re a MASSIVE corporation and they’re ignoring the problem because they intentionally want to push people to upgrade to pay 2 ~ 5 times to get more storage per account by using dark patterns.

Yes, I get you’re frustrated when you see what you don’t understand (the security issues, for example, I assume you don’t understand because you didn’t come with a piece of code ot fix it even though it’s not that complex, did you?) but considering that I am here for more than half of a decade I can tell you that @clavaque doesn’t just dismiss us out of choice. I have no idea what is happening but we need to be more patient and understand the context. I have autism but I am not this tone deaf.

If you can have patience, that’s nice. Otherwise, just find a better solution and, if you want, share it with us.

We have a few users here donating time and energy trying to be helpful with ZERO compensation.

I had to register an emergency domain because everything went poof overnight for a few days, then thankfully came back.

I will do what I can when able, to help. There’s pieces of code that might be useful for the vulnerability issue, I am in a conversation with a third party and also @Gerard, via email.

I care about it, but I also feel overwhelmed. I also want to do everything I can to respect @clavaque’s ownership of the plugin and I didn’t learn php. I just copy pieces of code here and there, make adjustments using logical deduction etc.

I really hope he comes around. One thing I believe is that we need to have some sort of communication channel. Even if he comes here only 3 times per week or something. I am helping as a moderator here and even I can’t reach him, otherwise I’d surely have done that.

I am open to ideas, suggestions, thoughts. Sorry for anything.

:tulip:

I share your concern that s2member development has stagnated, at least since 2017 when the two lead developers, Jason Caldwell and Raam Dev, look to have left the project.

Both Jason and Raam now work for Automattic, which I’m not surprised at, they’ve shown their programming chops with developing such an excellent product as S2member.

But since 2017 there have only been two significant additions to the s2member codebase, which I think is very poor indeed.

The first is an Payments Log Add-on which is sold as an added extra, which I think should be included within s2member-pro
https://s2member.com/checkout/payments-log-addon/

and some additional functionality to coupons, who’s development was paid for/sponsored by one Carl Borsani, as explained on the s2member-pro changelog

= v250214 =

- (Pro) **Enhancement**: Improved coupon usage logging for better tracking.

- (Pro) **Enhancement**: Added a new single-use per user option for coupons. Thanks to Carl Borsani for sponsoring this.

- (Pro) **Enhancement**: Coupons can now be limited to specific pro-forms. Thanks to Carl Borsani for sponsoring this.

- (Framework) **Fix**: s2Get can now handle s2Member’s custom profile fields. Thanks to Gerard Earley for reporting this.

- (Framework) **Fix**: Updated the admin notice about the PayPal button encryption setting.

- (Pro) **Enhancement**: Improved data handling in the Remote Operations API. Props to Istvan.

- (Pro) **Enhancement**: Improved validation of the template attribute in pro-forms and s2Member-List shortcodes. Props to Istvan.

As to @clavaque’s situation, I’m sympathetic to anyone in difficult circumstances, but their circumstances are ultimately their concern not mine. My concern is making sure their product, s2member, which I’ve now invested a lot of time and effort into developing on isn’t going to slowly become a risk. I’ve invested a significant amount of time and effort in developing my site in s2member by the time I realised that s2member is pseudo-abandoned I’m not sure I’d have chosen it as the technology to base my website on, if I’d known.

I sincerely hope @clavaque pulls his finger out and makes clear the development status of s2member. The lack of timely responses, stagnant development and still broken PayPal encrypted buttons after two+ years is grave concern to me.

For instance almost a year ago I did a little work to implement UK Postcodes as a s2member user form type, implemented in exactly the same way as US Zip codes. It works very well and I’ve had it running on a website for many many months with no bugs, but I got ZERO response from @clavaque on this issue. I can implemented it on my own sites very easily but its the constant lack of response from @clavaque that concerns me.

1 Like

You should directly add patches to GitHub s2member repo…

It’s the wrong payment model. 15-50 USD a year for s2member (including pro) would be much better instead of one time payment. I don’t know why it’s not changed many years ago. Give those who paid within last two years free, and that’s it.

Or maybe program a new checkout form and make that payable on subscription. The whole checkout experience really needs to be done newly and that’s not too hard. Including the complete possibility which fields to show and which not and getting rid of the ridiculous reload. The backend of s2member is pretty good once the learning curve is over. Plus integrate Stripe sources.

The content protection system is one of the best. Only niggle that standard browsers cannot resume downloads… it’s really not much except the checkout which is really missing. For invoicing there are many good solutions out there and S2 shouldn’t tackle it as it’s a minefield and huge huge topic. Also those solutions now all integrate directly into PayPal and stripe so it’s just needed that the data on checkout is parsed correctly onwards…

5-6 years ago I tried to change away but there was no single other membership plugin which allows the same flexibility on payments meaning subscription and fixed term. Nearly all others have lifetime and subscription only but no XX days/years. That makes moving away so hard.

2 Likes

Hello again!

Sorry for my delay.

So, the issue is on file s2member-pro/src/includes/classes/sc-member-list-in.inc.php

You need to replace the block:

 $custom_template = $attr['template'] && is_file(TEMPLATEPATH.'/'.$attr['template']) ? TEMPLATEPATH.'/'.$attr['template'] : $custom_template;
$custom_template = $attr['template'] && is_file(get_stylesheet_directory().'/'.$attr['template']) ? get_stylesheet_directory().'/'.$attr['template'] : $custom_template;
$custom_template = $attr['template'] && is_file(WP_CONTENT_DIR.'/'.$attr['template']) ? WP_CONTENT_DIR.'/'.$attr['template'] : $custom_template;

if($attr['template'] && !$custom_template) // Unable to locate the template file?
trigger_error(sprintf('Invalid `template=""` attribute. Could not find: `%1$s`.', esc_html($attr['template'])), E_USER_ERROR);

With…

		// Old version that needed to be sanitized, security vulnerability!!!
		//
		//			$custom_template = $attr['template'] && is_file(TEMPLATEPATH.'/'.$attr['template']) ? TEMPLATEPATH.'/'.$attr['template'] : $custom_template;
		//			$custom_template = $attr['template'] && is_file(get_stylesheet_directory().'/'.$attr['template']) ? get_stylesheet_directory().'/'.$attr['template'] : $custom_template;
		//			$custom_template = $attr['template'] && is_file(WP_CONTENT_DIR.'/'.$attr['template']) ? WP_CONTENT_DIR.'/'.$attr['template'] : $custom_template;
		//
		//			if($attr['template'] && !$custom_template) // Unable to locate the template file?
		//				trigger_error(sprintf('Invalid `template=""` attribute. Could not find: `%1$s`.', esc_html($attr['template'])), E_USER_ERROR);
		//

		//250331 Sanitize template attr.
		$upload_folder = basename(wp_upload_dir()['basedir']); // Get uploads folder name
		$attr['template'] = str_replace(['..', 'upload', $upload_folder], '', sanitize_text_field(esc_url_raw($attr['template'])));

		$custom_template = $attr['template'] && is_file(TEMPLATEPATH.'/'.$attr['template']) ? TEMPLATEPATH.'/'.$attr['template'] : $custom_template;
		$custom_template = $attr['template'] && is_file(get_stylesheet_directory().'/'.$attr['template']) ? get_stylesheet_directory().'/'.$attr['template'] : $custom_template;
		$custom_template = $attr['template'] && is_file(WP_CONTENT_DIR.'/'.$attr['template']) ? WP_CONTENT_DIR.'/'.$attr['template'] : $custom_template;

		if($attr['template'] && !$custom_template) // Unable to locate the template file?
		trigger_error(sprintf('Invalid `template=""` attribute. Could not find: `%1$s`.', esc_html($attr['template'])), E_USER_ERROR);
		//250331 End of Sanitization

If you’re using the PREVIOUS version 241216 From December of last year, you can use the file I attach below, and you must replace that block TWICE in that file (first occurrence at line 185, second occurrence a few lines below, you’ll see it about 50 lines below the first block or so.

If you’re using the LATEST (as of now) version 250214 from February 2025 you only need to do it ONCE on the SECOND Block that starts around line 216, since the first block already had been sanitized by @Clavaque.

His original sanitization block looks a bit different than mine, you can choose either (I only made changes to the comments) or make your own.

//250211 Sanitize template attr.
$upload_folder = basename(wp_upload_dir()['basedir']); // Get uploads folder name
$attr['template'] = str_replace(['..', 'upload', $upload_folder], '', sanitize_text_field(esc_url_raw($attr['template'])));</b>

$custom_template = $attr['template'] && is_file(TEMPLATEPATH.'/'.$attr['template']) ? TEMPLATEPATH.'/'.$attr['template'] : $custom_template;
$custom_template = $attr['template'] && is_file(get_stylesheet_directory().'/'.$attr['template']) ? get_stylesheet_directory().'/'.$attr['template'] : $custom_template;
$custom_template = $attr['template'] && is_file(WP_CONTENT_DIR.'/'.$attr['template']) ? WP_CONTENT_DIR.'/'.$attr['template'] : $custom_template;

if($attr['template'] && !$custom_template) // Unable to locate the template file?
trigger_error(sprintf('Invalid `template=""` attribute. Could not find: `%1$s`.', esc_html($attr['template'])), E_USER_ERROR);

This was brought to us thanks to the help of Istán Márton (Lana Codes) and you can find the vulnerability report reference at https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/s2member-pro/s2member-pro-250214-authenticated-contributor-local-file-inclusion-to-remote-code-execution-via-shortcode

Below is “MY” patched file, as mentioned, I worked on the version I am currently using, which is 241216 (I believe some of you are doing the same). I will upload a patched version for 250211 later tonight, maybe tomorrow.

sc-member-list-in.inc.php.for version 241216.zip (3.9 KB)

I applied it to mine and things seem to be working normally. I did not have a security warning. Can anybody test the patch and let me know if wordfence agrees with it?

I think I need to find the file for the pro form that was fixed (for people using 2024 December’s Version). I will also look for it by comparing both versions of the plugin to find the modified files. I wonder if that sanitization on the pro form isn’t the cause for issues some of you are reporting, though. :thinking:

Please keep me posted. We’ll get this situation controlled somehow. :pray:t2:

4 Likes

2 posts were split to a new topic: Possible Alternatives to s2Member

https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/s2member/s2member-250214-authenticated-administrator-local-file-inclusion

https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/s2member-pro/s2member-pro-250214-authenticated-contributor-local-file-inclusion-to-remote-code-execution-via-shortcode

What is the status of the fixes for these?

I moved your message back here, so you could find the answer above.

I am unlocking the topic for the time being. Just in case anybody wants to talk about it. I thought I was helping by closing it, but maybe it was a bad idea, sorry.

The report says the user needs to have admin privileges to be able to use that new “vulnerability”. It’s my understanding that if you’re an admin you’re supposed to have full control no matter what. What am I missing here?

Also, this is a feature of the plugin. We can inject php code. Not only use shortcodes.

What changed now, from before?

:slightly_smiling_face:

I have to check this a bit longer - but I think since I applied this change - My stripe subscription cancellations didn’t go through anymore. I did it however on the up to date s2member pro with one version back s2member main version.
I will downgrade to one version back plus fix to check.

New user registrations with Stripe did go through. I had enough cancellations/new users to know this was not by chance but clearly related to changes I have done since mid March!

Can other people check if Stripe cancellations due to failed payment cause EOT? Failed Payment is the ultimate status in Stripe, the long term problem that accounts stay active while payments are tried of course still active. This is after Stripe gives up (I set it to 4 tries within 1 month)

1 Like

I have my own system modified, to be honest.

I neutered all cancellation routines from s2Member on my own install because Stripe would send two or three notifications for each subscription, s2Member thought there were duplicates and cancelled new subscriptions right away, for example.

I also made a few changes so users are demoted o s2member as soon as a payment fails (I could not hack the code to give them a grace period, so I just had to compromise and do that instead).

I also have no cancel buttons that automatically cancel, instead I have a link that sends me an email asking me to cancel, the user puts some data as desired, I quickly cancel manually and double check the user won’t be charged against their will.

I have my Stripe set up to try 8 times for 30 days, I used to have it trying 8 times for 60 days but I notice it’s not common for people who went more than 30 days overdue to fix their payment methods.

I also try all pending invoices manually, from time to time, to see if I can recover them. Even with a tiny user base (hence why I can do things manually), that method of recovery is very effective reducing involuntary churn.

The modifications I showed above I applied on my version. I had shared my modifications with @clavaque so he could look at them and maybe use some of those ideas, but since he seems to be unable to reach us out (I send him my best wishes, wherever he is), we might have to work those things out in another manner.

I am a bit overwhelmed right now with taxes and a couple of personal issues but I believe May will be a calmer month where I will be able to think of, at least, share some pieces of modified code here. That also gives @Clavaque a few more weeks to say something.

I don’t want to abandon the plugin and move on to another, so I will try to see what’s possible (at least minor tweaks).

I think that it’s against our interest to have s2Member cancelling subscriptions via API by mistake and that’s what I experienced before neutering cancellations from s2Member towards payment processors altogether. Plus, having no cancellation button that’s automated means users can’t say they used it and that it “failed” when they, instead, just forgot to cancel timely. I understand that larger sites with lots of users might be unable to keep up with demand.

:thinking:

Well, just to report. The older version from 24.12 with the above patched file works fine and Stripe cancellations work as expected.

So the reason for Stripe messing up just like paypal IPN messing up is in the botched version from February.

Now yesterday a new version came out - I don’t know about it working or not. I just made sure that both Paypal IPN messages with redirection as well as Stripe work well on the patched version from 24.12.

New version: https://s2member.com/changelog/

  • (Pro) Enhancement : Improved the new coupon code limit per user which prevents a user from applying a coupon code unlimited times, Instead of single use, it can now be limited to more uses, e.g. 3. It’s been renamed from “User Once” to “User Max”, max number of times a user can use that coupon. This is optional and leaving it blank will give the default “no limit”.
  • (Pro) Enhancement : Improved validation of the template attribute in the s2Member-List-Search-Box shortcode.
  • (Framework) UI : Temporary admin notice about Easter promo for Pro add-on at 20% off.

Note that it doesn’t say anything about the new vulnerability being patched or not. I haven’t check the source code yet.

1 Like

WordFence recognizes the latest update as being “safe” - both vulnerabilities don’t come up for it. Thanks for the update, but I hope next time we can get some immediate feedback about the status of fixes - I imagine this has caused a bunch of folks to start considering alternative membership plugins/systems - I know I have.

1 Like

There’s even a topic about it, already.

:grimacing:

We’re back in the saddle again as of this morning:

https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/s2member/s2member-250214-authenticated-administrator-local-file-inclusion

2 Likes

I guess the issue wasn’t fixed, after all.

We need to mod it ourselves.

I don’t think @clavaque is visiting the forum at all, otherwise he’d be well aware of it.

That only reinforces the idea it might be unwise to update the plugin to any 2025 version without comparing the code.

I am sorry for not looking into it yet, even though it’s not my responsibility, I want to help but I am still overwhelmed with personal stuff.

Of course, you can apply the information already in the topic to patch your own and you’re welcome to list the affected files and the segments of code to be replaced, if you’d like, numbering the lines, if you feel that can be helpful (I think it’s just the two files mentioned above and the segments are already mentioned too, you can use your text editor to change those files then upload the patched versions to your own server via FTP).

Alternatively, if you can’t or don’t want to use FTP you can use do it using WordPress’ Plugin File Editor (under Plugins Tab inside your admin, but you can only make changes if you first disable s2Member, edit, save, re-enable). I’d rather make changes locally, though, and reupload whatever needs to be updated. Easier to keep track of things this way too and you can use tools like FreeFileSync (Windows) to compare unchanged with changed versions, as it will show the files with different contents, which you can open with NotePad++, which also allows you to compare two files line by line, side by side, also useful to see what’s modified when a plugin is updated, for example.

I hope the information helps somehow.

At least until we figure out what to do next.

:thinking:

I would really like to know how to exploit the bug. Luckily the big 9.8 bug is fixed. Surely the text description of the current one is wrong - it cannot be administrator but must be subscriber or moderator/contributor level.

I guess it’s subscriber or above can execute any php file that is on the server and not prevented by nginx or similar rules. And so thereby with enough knowledge of other plugins or general wordpress php files escape the prison by uploading their own php files to folders from which nginx does not prevent running them.
So like upload a new plugin to plugin folder and then run it.

If it were only for contributor/author ranks or above I would not be worried. But if subscriber is enough (and s2member_level1 and so on in my opinion is just a subscriber with additional rights) it would really be worrysome. Especially if you have a bigger website where surely someone can find an account with leaked username/password data.

I.e. - I had an author level user taken over after his password leaked that posted a lot of spam in some articles - and that automatically even worse got sent out in the newsletter integration before I could fix it. Bad enough but that’s not a takeover of your website.
I don’t want to force everyone to use 2factor authentication - because it causes a lot of support problems. I did after that problem not only enable 2factor on my own account, but all with editor/autor rights.

From some other bugs with similar description and CVSS level - it seems like the attack would try to find out the database password - and then if that is somehow accessible (some plugins may store it so they can do operations on the wordpress database) use it to ultimately take over your site. It’s not very likely to succeed but possible with enough knowledge and effort.

1 Like

s2Member Pro v250424that just came out is not showing a vulnerability in WordFence (but the change notes don’t seem to address the previous issues) - so I guess we’re good right now?

Last time it took a couple of days for the vulnerabilities to show up again. Maybe there’s a delay?

If it’s not mentioned in the changelog there’s a high chance it wasn’t addressed. :grimacing: